commit 7d7b3a9869b59e06a7b4abf6f6dddf7211b7f0c7
parent 8d1cd0740bf5a020618e2e39e965d70258908e8b
Author: Ryan <69221034+ryfu-msft@users.noreply.github.com>
Date: Wed, 24 Jul 2024 16:25:35 -0400
Pull in latest SFSClient (#4670)
Diffstat:
9 files changed, 124 insertions(+), 46 deletions(-)
diff --git a/src/SfsClient/readme.md b/src/SfsClient/readme.md
@@ -1,9 +1,9 @@
## SfsClient
-Do not change code under the sfs-client directory; it contains sfs-client source code from commit [ff315ec](https://github.com/microsoft/sfs-client/commits/ff315ec).
+Do not change code under the sfs-client directory; it contains sfs-client source code from commit [6ab78af](https://github.com/microsoft/sfs-client/commits/6ab78af).
It is created using git subtree command:
```
- git subtree add --prefix=src/SfsClient/sfs-client https://github.com/microsoft/sfs-client.git cf18b357f43aa9bbaba7d8b3b3774b39140aa00f --squash
+ git subtree add --prefix=src/SfsClient/sfs-client https://github.com/microsoft/sfs-client.git 6ab78af61bc859461ea8298786d87f24b49e3ec2 --squash
```
### Update
diff --git a/src/SfsClient/sfs-client/.github/dependabot.yml b/src/SfsClient/sfs-client/.github/dependabot.yml
@@ -17,3 +17,7 @@ updates:
directory: "/"
schedule:
interval: "weekly"
+ ignore:
+ # Ignore patch updates for all pip dependencies
+ - dependency-name: "*"
+ update-types: ["version-update:semver-patch"]
diff --git a/src/SfsClient/sfs-client/cgmanifest.json b/src/SfsClient/sfs-client/cgmanifest.json
@@ -37,7 +37,7 @@
"type": "git",
"git": {
"repositoryUrl": "https://github.com/curl/curl",
- "commitHash": "d755a5f7c009dd63a61b2c745180d8ba937cbfeb"
+ "commitHash": "fd567d4f06857f4fc8e2f64ea727b1318f76ad33"
}
},
"developmentDependency": false
@@ -56,7 +56,7 @@
"type": "git",
"git": {
"repositoryUrl": "https://github.com/curl/curl",
- "commitHash": "d755a5f7c009dd63a61b2c745180d8ba937cbfeb"
+ "commitHash": "fd567d4f06857f4fc8e2f64ea727b1318f76ad33"
}
}
]
@@ -75,7 +75,7 @@
"type": "git",
"git": {
"repositoryUrl": "https://github.com/curl/curl",
- "commitHash": "d755a5f7c009dd63a61b2c745180d8ba937cbfeb"
+ "commitHash": "fd567d4f06857f4fc8e2f64ea727b1318f76ad33"
}
}
]
diff --git a/src/SfsClient/sfs-client/client/tests/functional/details/CurlConnectionTests.cpp b/src/SfsClient/sfs-client/client/tests/functional/details/CurlConnectionTests.cpp
@@ -45,15 +45,15 @@ class CurlConnectionTimeout : public CurlConnection
std::string Get(const std::string& url) override
{
- // Timeout within 100ms
- curl_easy_setopt(m_handle, CURLOPT_TIMEOUT_MS, 100L);
+ // Timeout within 1ms
+ curl_easy_setopt(m_handle, CURLOPT_TIMEOUT_MS, 1L);
return CurlConnection::Get(url);
}
std::string Post(const std::string& url, const std::string& data) override
{
- // Timeout within 100ms
- curl_easy_setopt(m_handle, CURLOPT_TIMEOUT_MS, 100L);
+ // Timeout within 1ms
+ curl_easy_setopt(m_handle, CURLOPT_TIMEOUT_MS, 1L);
return CurlConnection::Post(url, data);
}
};
@@ -316,11 +316,11 @@ TEST("Testing a response over the limit fails the operation")
json body = {{{"TargetingAttributes", {}}, {"Product", largeProductName}}};
REQUIRE_NOTHROW(connection->Post(url, body.dump()));
- // Over limit fails
+ // Going over the limit fails with a message like "client returned ERROR on write of 16384 bytes"
body[0]["Product"] = overLimitProductName;
- REQUIRE_THROWS_CODE_MSG(connection->Post(url, body.dump()),
- ConnectionUnexpectedError,
- "Failure writing output to destination");
+ REQUIRE_THROWS_CODE_MSG_MATCHES(connection->Post(url, body.dump()),
+ ConnectionUnexpectedError,
+ Catch::Matchers::ContainsSubstring("client returned ERROR on write of"));
}
TEST("Testing MS-CV is sent to server")
diff --git a/src/SfsClient/sfs-client/client/tests/util/TestHelper.cpp b/src/SfsClient/sfs-client/client/tests/util/TestHelper.cpp
@@ -43,7 +43,7 @@ std::string TimestampToString(std::chrono::time_point<std::chrono::system_clock>
void SFS::test::LogCallbackToTest(const SFS::LogData& logData)
{
std::lock_guard guard(s_logMutex);
- UNSCOPED_INFO("Log: " << TimestampToString(logData.time) << " [" << ToString(logData.severity) << "]" << " "
- << std::filesystem::path(logData.file).filename().string() << ":" << logData.line << " "
- << logData.message);
+ UNSCOPED_INFO("Log: " << TimestampToString(logData.time) << " [" << ToString(logData.severity) << "]"
+ << " " << std::filesystem::path(logData.file).filename().string() << ":" << logData.line
+ << " " << logData.message);
}
diff --git a/src/SfsClient/sfs-client/samples/integration-do-client/IntegrationDOClient.cpp b/src/SfsClient/sfs-client/samples/integration-do-client/IntegrationDOClient.cpp
@@ -171,7 +171,8 @@ std::string TimestampToString(std::chrono::time_point<std::chrono::system_clock>
void LoggingCallback(const SFS::LogData& logData)
{
std::cout << c_darkGreyStart << "Log: " << TimestampToString(logData.time) << " [" << ToString(logData.severity)
- << "]" << " " << std::filesystem::path(logData.file).filename().string() << ":" << logData.line << " "
+ << "]"
+ << " " << std::filesystem::path(logData.file).filename().string() << ":" << logData.line << " "
<< logData.message << c_colorEnd << std::endl;
}
diff --git a/src/SfsClient/sfs-client/samples/tool/SFSClientTool.cpp b/src/SfsClient/sfs-client/samples/tool/SFSClientTool.cpp
@@ -5,6 +5,7 @@
#include <exception>
#include <filesystem>
+#include <fstream>
#include <iomanip>
#include <iostream>
#include <sstream>
@@ -30,6 +31,7 @@ void DisplayUsage()
<< "Options:" << std::endl
<< " -h, --help\t\t\tDisplay this help message" << std::endl
<< " -v, --version\t\t\tDisplay the library version" << std::endl
+ << " -o, --outputFile <path>\t\tWhen specified, the JSON output is saved to this file" << std::endl
<< " --isApp\t\t\tIndicates the specific product is an App" << std::endl
<< " --instanceId <id>\t\tA custom SFS instance ID" << std::endl
<< " --namespace <ns>\t\tA custom SFS namespace" << std::endl
@@ -87,6 +89,12 @@ struct Settings
std::string instanceId;
std::string nameSpace;
std::string customUrl;
+ std::string outputFile;
+
+ bool ShouldOutputToFile() const
+ {
+ return !outputFile.empty();
+ }
};
void ParseArguments(const std::vector<std::string_view>& args, Settings& settings)
@@ -126,6 +134,11 @@ void ParseArguments(const std::vector<std::string_view>& args, Settings& setting
{
settings.displayVersion = true;
}
+ else if (matchArg(args[i], "-o", "--outputFile"))
+ {
+ validateArg(i, "outputFile", settings.outputFile);
+ settings.outputFile = args[++i];
+ }
else if (matchLongArg(args[i], "--isApp"))
{
settings.isApp = true;
@@ -197,16 +210,8 @@ constexpr std::string_view ToString(Architecture type)
return "";
}
-void DisplayResults(const std::vector<Content>& contents)
+json ContentsToJson(const std::vector<Content>& contents)
{
- if (contents.empty())
- {
- PrintError("No results found");
- return;
- }
-
- PrintLog("Content found:");
-
json out = json::array();
for (const auto& content : contents)
{
@@ -233,7 +238,7 @@ void DisplayResults(const std::vector<Content>& contents)
out.push_back(j);
}
- PrintLog(out.dump(2 /*indent*/));
+ return out;
}
json AppFileToJson(const AppFile& file)
@@ -265,16 +270,8 @@ json AppFileToJson(const AppFile& file)
return fileJson;
}
-void DisplayResults(const std::vector<AppContent>& contents)
+json AppContentsToJson(const std::vector<AppContent>& contents)
{
- if (contents.empty())
- {
- std::cout << "No results found." << std::endl;
- return;
- }
-
- PrintLog("Content found:");
-
json out = json::array();
for (const auto& content : contents)
{
@@ -309,7 +306,50 @@ void DisplayResults(const std::vector<AppContent>& contents)
out.push_back(j);
}
- PrintLog(out.dump(2 /*indent*/));
+ return out;
+}
+
+void DisplayResults(const json& results)
+{
+ if (results.empty())
+ {
+ return;
+ }
+
+ PrintLog("Content found:");
+ PrintLog(results.dump(2 /*indent*/));
+}
+
+Result JSONToFile(const json& results, const std::string& filename)
+{
+ if (results.empty())
+ {
+ return Result::Unexpected;
+ }
+
+ const std::filesystem::path filepath = std::filesystem::absolute(filename);
+ try
+ {
+ std::filesystem::create_directories(filepath.parent_path());
+ }
+ catch (const std::exception& e)
+ {
+ PrintError("Failed to create parent directories for filepath: " + filepath.string() + ". Error: " + e.what());
+ return Result::Unexpected;
+ }
+
+ std::ofstream file(filepath, std::ios::out | std::ios::trunc);
+ if (!file.is_open())
+ {
+ PrintError("Failed to open file for writing: " + filename);
+ return Result::Unexpected;
+ }
+
+ file << results.dump(2 /*indent*/);
+
+ PrintLog("Content found. Saved to file " + filepath.string());
+
+ return Result::Success;
}
void LogResult(const SFS::Result& result)
@@ -348,7 +388,8 @@ std::string TimestampToString(std::chrono::time_point<std::chrono::system_clock>
void LoggingCallback(const SFS::LogData& logData)
{
std::cout << c_darkGreyStart << "Log: " << TimestampToString(logData.time) << " [" << ToString(logData.severity)
- << "]" << " " << std::filesystem::path(logData.file).filename().string() << ":" << logData.line << " "
+ << "]"
+ << " " << std::filesystem::path(logData.file).filename().string() << ":" << logData.line << " "
<< logData.message << c_colorEnd << std::endl;
}
@@ -365,11 +406,12 @@ bool SetEnv(const std::string& varName, const std::string& value)
#endif
}
-Result GetLatestDownloadInfo(const SFSClient& sfsClient, const Settings& settings)
+Result GetLatestDownloadInfo(const SFSClient& sfsClient, const Settings& settings, json& out)
{
PrintLog("Getting latest download info for product: " + settings.product);
RequestParams params;
params.productRequests = {{settings.product, {}}};
+
if (settings.isApp)
{
std::vector<AppContent> appContents;
@@ -381,14 +423,12 @@ Result GetLatestDownloadInfo(const SFSClient& sfsClient, const Settings& setting
return result.GetCode();
}
- // Display results
- DisplayResults(appContents);
+ out = AppContentsToJson(appContents);
}
else
{
std::vector<Content> contents;
auto result = sfsClient.GetLatestDownloadInfo(params, contents);
-
if (!result)
{
PrintError("Failed to get latest download info.");
@@ -396,8 +436,33 @@ Result GetLatestDownloadInfo(const SFSClient& sfsClient, const Settings& setting
return result.GetCode();
}
- // Display results
+ out = ContentsToJson(contents);
+ }
+
+ if (out.empty())
+ {
+ PrintError("No results found.");
+ return Result::Unexpected;
+ }
+
+ return Result::Success;
+}
+
+Result HandleJSONContents(const Settings& settings, const json& contents)
+{
+ if (settings.ShouldOutputToFile())
+ {
+ auto result = JSONToFile(contents, settings.outputFile);
+ if (!result)
+ {
+ PrintError("Failed to save to file.");
+ return result.GetCode();
+ }
+ }
+ else
+ {
DisplayResults(contents);
+ return Result::Success;
}
return Result::Success;
@@ -464,7 +529,15 @@ int main(int argc, char* argv[])
}
// Perform operations using SFSClient
- result = GetLatestDownloadInfo(*sfsClient, settings);
+ json contents;
+ result = GetLatestDownloadInfo(*sfsClient, settings, contents);
+ if (!result)
+ {
+ LogResult(result);
+ return result.GetCode();
+ }
+
+ result = HandleJSONContents(settings, contents);
if (!result)
{
LogResult(result);
diff --git a/src/SfsClient/sfs-client/scripts/pip.requirements.txt b/src/SfsClient/sfs-client/scripts/pip.requirements.txt
@@ -1,2 +1,2 @@
-clang-format==18.1.4
+clang-format==18.1.5
cmake-format==0.6.13
\ No newline at end of file
diff --git a/src/SfsClient/sfs-client/vcpkg.json b/src/SfsClient/sfs-client/vcpkg.json
@@ -44,7 +44,7 @@
},
{
"name": "curl",
- "version": "8.4.0"
+ "version": "8.8.0"
},
{
"name": "nlohmann-json",