diff --git a/src/FuelClient.cc b/src/FuelClient.cc index e5e547a4..2fdf0a34 100644 --- a/src/FuelClient.cc +++ b/src/FuelClient.cc @@ -687,6 +687,55 @@ Result FuelClient::DownloadModel(const ModelIdentifier &_id, if (!this->dataPtr->cache->SaveModel(newId, resp.data, true)) return Result(ResultType::FETCH_ERROR); + // Locate any dependencies from the input model and download them. + std::string path; + ignition::msgs::FuelMetadata meta; + if (this->CachedModel(ignition::common::URI(newId.UniqueName()), path)) + { + std::string metadataPath = + ignition::common::joinPaths(path, "metadata.pbtxt"); + std::string modelConfigPath = + ignition::common::joinPaths(path, "model.config"); + + bool foundMetadataPath = ignition::common::exists(metadataPath); + bool foundModelConfigPath = ignition::common::exists(modelConfigPath); + + if (foundMetadataPath || foundModelConfigPath) + { + std::string modelPath = + (foundMetadataPath) ? metadataPath : modelConfigPath; + + // Read the pbtxt file. + std::ifstream inputFile(modelPath); + std::string inputStr((std::istreambuf_iterator(inputFile)), + std::istreambuf_iterator()); + + if (foundMetadataPath) + { + // Parse the file into the fuel metadata message + google::protobuf::TextFormat::ParseFromString(inputStr, &meta); + } + else + { + if (!ignition::msgs::ConvertFuelMetadata(inputStr, meta)) + { + return Result(ResultType::UPLOAD_ERROR); + } + } + + for (int i = 0; i < meta.dependencies_size(); ++i) + { + std::string dependencyPath; + ignition::common::URI dependencyURI(meta.dependencies(i).uri()); + + // If the model is not already cached, download it; this prevents + // any sort of cyclic dependencies from running infinitely + if (!this->CachedModel(dependencyURI, dependencyPath)) + this->DownloadModel(dependencyURI, dependencyPath); + } + } + } + return Result(ResultType::FETCH); } diff --git a/src/FuelClient_TEST.cc b/src/FuelClient_TEST.cc index 04c5cd5a..c31bbd1f 100644 --- a/src/FuelClient_TEST.cc +++ b/src/FuelClient_TEST.cc @@ -499,6 +499,76 @@ TEST_F(FuelClientTest, IGN_UTILS_TEST_DISABLED_ON_WIN32(DownloadModel)) EXPECT_NE(std::string::npos, modelSdf.find("https://")); } + // Download model with a dependency specified within its `metadata.pbtxt` + { + common::URI url{ + "https://fuel.ignitionrobotics.org/1.0/JShep1/models/hatchback_red_1"}; + common::URI depUrl{ + "https://fuel.ignitionrobotics.org/1.0/JShep1/models/hatchback_1"}; + + // Check it is not cached + std::string cachedPath; + Result res1 = client.CachedModel(url, cachedPath); + EXPECT_FALSE(res1); + EXPECT_EQ(Result(ResultType::FETCH_ERROR), res1); + + // Check the dependency is not cached + Result res2 = client.CachedModel(depUrl, cachedPath); + EXPECT_FALSE(res2); + EXPECT_EQ(Result(ResultType::FETCH_ERROR), res2); + + // Download + std::string path; + Result res3 = client.DownloadModel(url, path); + EXPECT_TRUE(res3); + EXPECT_EQ(Result(ResultType::FETCH_ALREADY_EXISTS), res3); + + // Check it is cached + Result res4 = client.CachedModel(url, cachedPath); + EXPECT_TRUE(res4); + EXPECT_EQ(Result(ResultType::FETCH_ALREADY_EXISTS), res4); + + // Check the dependency is cached + Result res5 = client.CachedModel(depUrl, cachedPath); + EXPECT_TRUE(res5); + EXPECT_EQ(Result(ResultType::FETCH_ALREADY_EXISTS), res5); + } + + // Download model with a dependency specified within its `model.config` + { + common::URI url{ + "https://fuel.ignitionrobotics.org/1.0/JShep1/models/hatchback_red_2"}; + common::URI depUrl{ + "https://fuel.ignitionrobotics.org/1.0/JShep1/models/hatchback_2"}; + + // Check it is not cached + std::string cachedPath; + Result res1 = client.CachedModel(url, cachedPath); + EXPECT_FALSE(res1); + EXPECT_EQ(Result(ResultType::FETCH_ERROR), res1); + + // Check the dependency is not cached + Result res2 = client.CachedModel(depUrl, cachedPath); + EXPECT_FALSE(res2); + EXPECT_EQ(Result(ResultType::FETCH_ERROR), res2); + + // Download + std::string path; + Result res3 = client.DownloadModel(url, path); + EXPECT_TRUE(res3); + EXPECT_EQ(Result(ResultType::FETCH_ALREADY_EXISTS), res3); + + // Check it is cached + Result res4 = client.CachedModel(url, cachedPath); + EXPECT_TRUE(res4); + EXPECT_EQ(Result(ResultType::FETCH_ALREADY_EXISTS), res4); + + // Check the dependency is cached + Result res5 = client.CachedModel(depUrl, cachedPath); + EXPECT_TRUE(res5); + EXPECT_EQ(Result(ResultType::FETCH_ALREADY_EXISTS), res5); + } + // Try using nonexistent URL { std::string url{