diff --git a/.github/ci/packages.apt b/.github/ci/packages.apt index 0accfa2f..c8ab7a2a 100644 --- a/.github/ci/packages.apt +++ b/.github/ci/packages.apt @@ -2,7 +2,7 @@ curl libcurl4-openssl-dev libgflags-dev libignition-cmake2-dev -libignition-common3-dev +libignition-common4-dev libignition-msgs7-dev libignition-tools-dev libjsoncpp-dev diff --git a/CMakeLists.txt b/CMakeLists.txt index c8561d11..5d61cf13 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -47,8 +47,8 @@ ign_find_package(ZIP REQUIRED PRIVATE) #-------------------------------------- # Find ignition-common -ign_find_package(ignition-common3 REQUIRED PRIVATE) -set(IGN_COMMON_MAJOR_VER ${ignition-common3_VERSION_MAJOR}) +ign_find_package(ignition-common4 REQUIRED PRIVATE) +set(IGN_COMMON_MAJOR_VER ${ignition-common4_VERSION_MAJOR}) #-------------------------------------- # Find ignition-msgs diff --git a/bitbucket-pipelines.yml b/bitbucket-pipelines.yml index d46b6519..880e0226 100644 --- a/bitbucket-pipelines.yml +++ b/bitbucket-pipelines.yml @@ -16,7 +16,7 @@ pipelines: git libcurl4-openssl-dev libjsoncpp-dev libzip-dev libgflags-dev libtinyxml2-dev lcov curl libyaml-dev libignition-cmake2-dev - libignition-common3-dev + libignition-common4-dev libignition-math6-dev # libignition-msgs5-dev # Ignition tools diff --git a/configure.bat b/configure.bat index 1deeb9de..dda28942 100644 --- a/configure.bat +++ b/configure.bat @@ -10,7 +10,7 @@ call %win_lib% :download_unzip_install libyaml-0.1.7-vc15-x64-md.zip call %win_lib% :download_unzip_install libzip-1.4.0_zlip-1.2.11_vc15-x64-dll-MD.zip call %win_lib% :install_ign_project ign-cmake ign-cmake2 call %win_lib% :install_ign_project ign-tools ign-tools1 -call %win_lib% :install_ign_project ign-common ign-common3 +call %win_lib% :install_ign_project ign-common main call %win_lib% :install_ign_project ign-msgs main :: Set configuration variables diff --git a/include/ignition/fuel_tools/FuelClient.hh b/include/ignition/fuel_tools/FuelClient.hh index baff1bd8..b8207792 100644 --- a/include/ignition/fuel_tools/FuelClient.hh +++ b/include/ignition/fuel_tools/FuelClient.hh @@ -23,7 +23,6 @@ #include #include -#include "ignition/fuel_tools/Helpers.hh" #include "ignition/fuel_tools/ModelIter.hh" #include "ignition/fuel_tools/RestClient.hh" #include "ignition/fuel_tools/Result.hh" diff --git a/include/ignition/fuel_tools/Helpers.hh b/include/ignition/fuel_tools/Helpers.hh index a8b5373c..748cfb40 100644 --- a/include/ignition/fuel_tools/Helpers.hh +++ b/include/ignition/fuel_tools/Helpers.hh @@ -19,6 +19,7 @@ #define IGNITION_FUEL_TOOLS_HELPERS_HH_ #include +#include #include // Use safer functions on Windows @@ -34,5 +35,19 @@ #define ign_strdup strdup #endif +namespace ignition +{ +namespace fuel_tools +{ +/// \brief Convert a URI to a string suitable to use as a path on disk. +/// It strips the scheme and authority's `//` prefix. +/// \param[in] _uri URI to convert. +/// \return String suitable to use in file paths +IGNITION_FUEL_TOOLS_VISIBLE +std::string uriToPath(const ignition::common::URI &_uri); +} +} + // IGNITION_FUEL_TOOLS_HELPERS_HH_ #endif + diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f42e86c9..e975156a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -2,6 +2,7 @@ set (sources ClientConfig.cc CollectionIdentifier.cc FuelClient.cc + Helpers.cc ign.cc Interface.cc JSONParser.cc @@ -22,6 +23,7 @@ set (gtest_sources FuelClient_TEST.cc ign_src_TEST.cc Interface_TEST.cc + Helpers_TEST.cc JSONParser_TEST.cc LocalCache_TEST.cc ModelIdentifier_TEST.cc diff --git a/src/ClientConfig_TEST.cc b/src/ClientConfig_TEST.cc index 74c5d246..53af2a4a 100644 --- a/src/ClientConfig_TEST.cc +++ b/src/ClientConfig_TEST.cc @@ -388,6 +388,7 @@ TEST(ServerConfig, Url) EXPECT_EQ("http://banana:8080", srv.Url().Str()); EXPECT_EQ("http", srv.Url().Scheme()); EXPECT_EQ("banana:8080", srv.Url().Path().Str()); + EXPECT_FALSE(srv.Url().Authority()); } // Trailing / @@ -395,6 +396,9 @@ TEST(ServerConfig, Url) ServerConfig srv; srv.SetUrl(common::URI("http://banana:8080/")); EXPECT_EQ("http://banana:8080", srv.Url().Str()); + EXPECT_EQ("http", srv.Url().Scheme()); + EXPECT_EQ("banana:8080", srv.Url().Path().Str()); + EXPECT_FALSE(srv.Url().Authority()); } // Set from URI @@ -408,6 +412,7 @@ TEST(ServerConfig, Url) EXPECT_EQ("http://banana:8080", srv.Url().Str()); EXPECT_EQ("http", srv.Url().Scheme()); EXPECT_EQ("banana:8080", srv.Url().Path().Str()); + EXPECT_FALSE(srv.Url().Authority()); } } diff --git a/src/FuelClient.cc b/src/FuelClient.cc index 6e2f0c36..51b3511d 100644 --- a/src/FuelClient.cc +++ b/src/FuelClient.cc @@ -40,6 +40,7 @@ #include "ignition/fuel_tools/ClientConfig.hh" #include "ignition/fuel_tools/CollectionIdentifier.hh" #include "ignition/fuel_tools/FuelClient.hh" +#include "ignition/fuel_tools/Helpers.hh" #include "ignition/fuel_tools/JSONParser.hh" #include "ignition/fuel_tools/LocalCache.hh" #include "ignition/fuel_tools/ModelIdentifier.hh" @@ -71,7 +72,9 @@ class ignition::fuel_tools::FuelClientPrivate // Name "([^\\/]+)\\/*" // Version - "([0-9]*|tip)"}; + "([0-9]*|tip)" + // Trailing / + "/?"}; /// \brief A world URL, /// E.g.: https://fuel.ignitionrobotics.org/1.0/OpenRobotics/worlds/Empty/1 @@ -90,7 +93,9 @@ class ignition::fuel_tools::FuelClientPrivate // Name "([^\\/]+)\\/*" // Version - "([0-9]*|tip)"}; + "([0-9]*|tip)" + // Trailing / + "/?"}; /// \brief A model file URL, /// E.g.: https://server.org/1.0/owner/models/modelname/files/meshes/mesh.dae @@ -113,7 +118,9 @@ class ignition::fuel_tools::FuelClientPrivate // "files" "files\\/+" // File path - "(.*)"}; + "(.*)" + // Trailing / + "/?"}; /// \brief A world file URL, /// E.g.: https://server.org/1.0/owner/worlds/worldname/files/meshes/mesh.dae @@ -136,7 +143,9 @@ class ignition::fuel_tools::FuelClientPrivate // "files" "files\\/+" // File path - "(.*)"}; + "(.*)" + // Trailing / + "/?"}; /// \brief A collection URL, /// E.g.: @@ -750,8 +759,6 @@ bool FuelClient::ParseModelUrl(const common::URI &_modelUrl, std::string modelName; std::string modelVersion; - std::regex_match(urlStr, match, *this->dataPtr->urlModelRegex); - if (std::regex_match(urlStr, match, *this->dataPtr->urlModelRegex) && match.size() >= 5u) { @@ -818,8 +825,6 @@ bool FuelClient::ParseWorldUrl(const common::URI &_worldUrl, std::string worldName; std::string worldVersion; - std::regex_match(urlStr, match, *this->dataPtr->urlWorldRegex); - if (std::regex_match(urlStr, match, *this->dataPtr->urlWorldRegex) && match.size() >= 5u) { diff --git a/src/FuelClient_TEST.cc b/src/FuelClient_TEST.cc index 22bda451..f2b5603e 100644 --- a/src/FuelClient_TEST.cc +++ b/src/FuelClient_TEST.cc @@ -173,13 +173,14 @@ TEST_F(FuelClientTest, ParseModelURL) // * with client config // * with server API version different from config // * with model version + // * with trailing / { ClientConfig config; FuelClient client(config); ModelIdentifier id; const std::string url{ - "https://fuel.ignitionrobotics.org/5.0/german/models/Cardboard Box/6"}; + "https://fuel.ignitionrobotics.org/5.0/german/models/Cardboard Box/6/"}; EXPECT_TRUE(client.ParseModelUrl(ignition::common::URI(url), id)); EXPECT_EQ(id.Server().Url().Str(), "https://fuel.ignitionrobotics.org"); @@ -801,13 +802,14 @@ TEST_F(FuelClientTest, ParseWorldUrl) // * with client config // * without server API version // * with world version `tip` + // * with trailing / { ClientConfig config; FuelClient client(config); WorldIdentifier id; const common::URI url{ - "https://fuel.ignitionrobotics.org/german/worlds/Cardboard Box/tip"}; + "https://fuel.ignitionrobotics.org/german/worlds/Cardboard Box/tip/"}; EXPECT_TRUE(client.ParseWorldUrl(url, id)); EXPECT_EQ(id.Server().Url().Str(), "https://fuel.ignitionrobotics.org"); diff --git a/src/Helpers.cc b/src/Helpers.cc new file mode 100644 index 00000000..dface578 --- /dev/null +++ b/src/Helpers.cc @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2021 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * +*/ +#include +#include + +#include "ignition/fuel_tools/Helpers.hh" + +using namespace ignition; +using namespace fuel_tools; + +////////////////////////////////////////////////// +std::string ignition::fuel_tools::uriToPath(const common::URI &_uri) +{ + auto path = _uri.Path().Str(); + if (_uri.Path().IsAbsolute()) + { + path = path.substr(1); + } + common::changeFromUnixPath(path); + + if (!_uri.Authority()) + { + return path; + } + + auto authority = _uri.Authority()->Str(); + if (authority.find("//") == 0) + { + authority = authority.substr(2); + } + + if (authority.empty()) + { + return path; + } + + if (path.empty()) + { + return authority; + } + + return common::joinPaths(authority, path); +} diff --git a/src/Helpers_TEST.cc b/src/Helpers_TEST.cc new file mode 100644 index 00000000..7cbd73c2 --- /dev/null +++ b/src/Helpers_TEST.cc @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2021 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * +*/ + +#include +#include +#include "ignition/fuel_tools/Helpers.hh" + +using namespace ignition; +using namespace fuel_tools; + +///////////////////////////////////////////////// +TEST(HelpersTEST, UriToPathNoAuthority) +{ + { + common::URI uri{"http://localhost:8000"}; + EXPECT_EQ("localhost:8000", uriToPath(uri)); + } + + { + common::URI uri{"http://localhost:8000/some/path"}; + EXPECT_EQ(common::joinPaths("localhost:8000", "some", "path"), + uriToPath(uri)); + } + + { + common::URI uri{"http://localhost:8000/some/path/"}; + EXPECT_EQ(common::separator(common::joinPaths("localhost:8000", "some", + "path")), uriToPath(uri)); + } +} + +///////////////////////////////////////////////// +TEST(HelpersTEST, UriToPathHasAuthority) +{ + { + common::URI uri{"http://localhost:8000", true}; + EXPECT_EQ("localhost:8000", uriToPath(uri)); + } + + { + common::URI uri{"http://localhost:8000/some/path", true}; + EXPECT_EQ(common::joinPaths("localhost:8000", "some", "path"), + uriToPath(uri)); + } + + { + common::URI uri{"http://localhost:8000/some/path/", true}; + EXPECT_EQ(common::separator(common::joinPaths("localhost:8000", "some", + "path")), uriToPath(uri)); + } +} + +////////////////////////////////////////////////// +int main(int argc, char **argv) +{ + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/src/LocalCache.cc b/src/LocalCache.cc index a89c7d1b..5b3c85b2 100644 --- a/src/LocalCache.cc +++ b/src/LocalCache.cc @@ -34,6 +34,7 @@ #include #include "ignition/fuel_tools/ClientConfig.hh" +#include "ignition/fuel_tools/Helpers.hh" #include "ignition/fuel_tools/LocalCache.hh" #include "ignition/fuel_tools/Zip.hh" @@ -231,7 +232,8 @@ ModelIter LocalCache::AllModels() for (auto &server : this->dataPtr->config->Servers()) { std::string path = common::joinPaths( - this->dataPtr->config->CacheLocation(), server.Url().Path().Str()); + this->dataPtr->config->CacheLocation(), uriToPath(server.Url())); + auto srvModels = this->dataPtr->ModelsInServer(path); for (auto &mod : srvModels) { @@ -254,7 +256,7 @@ WorldIter LocalCache::AllWorlds() const for (auto &server : this->dataPtr->config->Servers()) { std::string path = common::joinPaths( - this->dataPtr->config->CacheLocation(), server.Url().Path().Str()); + this->dataPtr->config->CacheLocation(), uriToPath(server.Url())); // Make sure the server info is correct auto srvWorlds = this->dataPtr->WorldsInServer(path); @@ -386,7 +388,7 @@ bool LocalCache::SaveModel( std::string cacheLocation = this->dataPtr->config->CacheLocation(); std::string modelRootDir = common::joinPaths(cacheLocation, - _id.Server().Url().Path().Str(), _id.Owner(), "models", _id.Name()); + uriToPath(_id.Server().Url()), _id.Owner(), "models", _id.Name()); std::string modelVersionedDir = common::joinPaths(modelRootDir, _id.VersionStr()); diff --git a/src/WorldIdentifier.cc b/src/WorldIdentifier.cc index 755d1ee5..cdbe6758 100644 --- a/src/WorldIdentifier.cc +++ b/src/WorldIdentifier.cc @@ -23,6 +23,7 @@ #include #include "ignition/fuel_tools/ClientConfig.hh" +#include "ignition/fuel_tools/Helpers.hh" #include "ignition/fuel_tools/WorldIdentifier.hh" using namespace ignition; @@ -79,7 +80,7 @@ WorldIdentifier::~WorldIdentifier() ////////////////////////////////////////////////// std::string WorldIdentifier::UniqueName() const { - return common::joinPaths(this->dataPtr->server.Url().Path().Str(), + return common::joinPaths(uriToPath(this->dataPtr->server.Url()), this->dataPtr->owner, "worlds", this->dataPtr->name); diff --git a/tutorials/01_installation.md b/tutorials/01_installation.md index cf84e496..2da8fe2f 100644 --- a/tutorials/01_installation.md +++ b/tutorials/01_installation.md @@ -73,7 +73,7 @@ sudo apt-get remove libignition-fuel-tools6-dev Install prerequisites. A clean Ubuntu system will need: ``` -sudo apt-get install git cmake pkg-config python ruby-ronn libignition-cmake2-dev libignition-common3-dev libignition-msgs5-dev libignition-tools-dev libzip-dev libjsoncpp-dev libcurl4-openssl-dev libyaml-dev +sudo apt-get install git cmake pkg-config python ruby-ronn libignition-cmake2-dev libignition-common4-dev libignition-msgs5-dev libignition-tools-dev libzip-dev libjsoncpp-dev libcurl4-openssl-dev libyaml-dev ``` Clone the repository into a directory and go into it: