commit d8819d08d77b4e8490ad6e441191038d5e14619c
parent c925c39194f081b406b98223ef95d457f7e2fec0
Author: JohnMcPMS <johnmcp@microsoft.com>
Date: Tue, 7 Jan 2025 16:18:57 -0800
Store information result in RestSourceReference for reuse (#5112)
## Change
When the `RestSourceReference` retrieves the information, store it and
pass it along to `RestClient::Create` so that it can reuse the value
rather than calling again.
Diffstat:
6 files changed, 64 insertions(+), 35 deletions(-)
diff --git a/src/AppInstallerCLITests/CustomHeader.cpp b/src/AppInstallerCLITests/CustomHeader.cpp
@@ -41,6 +41,14 @@ namespace
})delimiter");
}
+// In RestClient.cpp tests
+extern RestClient CreateRestClient(
+ const std::string& restApi,
+ const std::optional<std::string>& customHeader,
+ std::string_view caller,
+ const Http::HttpClientHelper& helper,
+ const Authentication::AuthenticationArguments& authArgs = {});
+
TEST_CASE("RestClient_CustomHeader", "[RestSource][CustomHeader]")
{
utility::string_t sample = _XPLATSTR(
@@ -55,7 +63,7 @@ TEST_CASE("RestClient_CustomHeader", "[RestSource][CustomHeader]")
std::optional<std::string> customHeader = "Testing custom header";
auto header = std::make_pair<>(CustomHeaderName, JSON::GetUtilityString(customHeader.value()));
HttpClientHelper helper{ GetHeaderVerificationHandler(web::http::status_codes::OK, sample, header) };
- RestClient client = RestClient::Create(utility::conversions::to_utf8string("https://restsource.com/api"), customHeader, {}, std::move(helper), {});
+ RestClient client = CreateRestClient(utility::conversions::to_utf8string("https://restsource.com/api"), customHeader, {}, helper);
REQUIRE(client.GetSourceIdentifier() == "Source123");
}
@@ -104,7 +112,7 @@ TEST_CASE("RestSourceSearch_CustomHeaderExceedingSize", "[RestSource][CustomHead
auto header = std::make_pair<>(CustomHeaderName, JSON::GetUtilityString(customHeader));
HttpClientHelper helper{ GetHeaderVerificationHandler(web::http::status_codes::OK, sampleSearchResponse, header) };
- REQUIRE_THROWS_HR(RestClient::Create(utility::conversions::to_utf8string("https://restsource.com/api"), customHeader, {}, std::move(helper), {}),
+ REQUIRE_THROWS_HR(CreateRestClient(utility::conversions::to_utf8string("https://restsource.com/api"), customHeader, {}, helper),
APPINSTALLER_CLI_ERROR_CUSTOMHEADER_EXCEEDS_MAXLENGTH);
}
@@ -122,7 +130,7 @@ TEST_CASE("RestClient_CustomUserAgentHeader", "[RestSource][CustomHeader]")
std::string testCaller = "TestCaller";
auto header = std::make_pair<>(web::http::header_names::user_agent, JSON::GetUtilityString(Runtime::GetUserAgent(testCaller)));
HttpClientHelper helper{ GetHeaderVerificationHandler(web::http::status_codes::OK, sample, header) };
- RestClient client = RestClient::Create(utility::conversions::to_utf8string("https://restsource.com/api"), {}, testCaller, std::move(helper), {});
+ RestClient client = CreateRestClient(utility::conversions::to_utf8string("https://restsource.com/api"), {}, testCaller, helper);
REQUIRE(client.GetSourceIdentifier() == "Source123");
}
@@ -139,6 +147,6 @@ TEST_CASE("RestClient_DefaultUserAgentHeader", "[RestSource][CustomHeader]")
auto header = std::make_pair<>(web::http::header_names::user_agent, JSON::GetUtilityString(Runtime::GetDefaultUserAgent()));
HttpClientHelper helper{ GetHeaderVerificationHandler(web::http::status_codes::OK, sample, header) };
- RestClient client = RestClient::Create(utility::conversions::to_utf8string("https://restsource.com/api"), {}, {}, std::move(helper), {});
+ RestClient client = CreateRestClient(utility::conversions::to_utf8string("https://restsource.com/api"), {}, {}, helper);
REQUIRE(client.GetSourceIdentifier() == "Source123");
}
diff --git a/src/AppInstallerCLITests/RestClient.cpp b/src/AppInstallerCLITests/RestClient.cpp
@@ -17,6 +17,16 @@ using namespace AppInstaller::Repository::Rest::Schema;
const std::string TestRestUri = "http://restsource.net";
+RestClient CreateRestClient(
+ const std::string& restApi,
+ const std::optional<std::string>& customHeader,
+ std::string_view caller,
+ const Http::HttpClientHelper& helper,
+ const Authentication::AuthenticationArguments& authArgs = {})
+{
+ return RestClient::Create(restApi, customHeader, caller, helper, RestClient::GetInformation(restApi, customHeader, caller, helper), authArgs);
+}
+
TEST_CASE("GetLatestCommonVersion", "[RestSource]")
{
std::set<AppInstaller::Utility::Version> wingetSupportedContracts = { Version {"1.0.0"}, Version {"1.2.0"} };
@@ -103,7 +113,7 @@ TEST_CASE("GetInformation_Success", "[RestSource]")
}})delimiter");
HttpClientHelper helper{ GetTestRestRequestHandler(web::http::status_codes::OK, sample) };
- IRestClient::Information information = RestClient::GetInformation(TestRestUri, {}, {}, std::move(helper));
+ IRestClient::Information information = RestClient::GetInformation(TestRestUri, {}, {}, helper);
REQUIRE(information.SourceIdentifier == "Source123");
REQUIRE(information.ServerSupportedVersions.size() == 2);
REQUIRE(information.ServerSupportedVersions.at(0) == "1.0.0");
@@ -165,7 +175,7 @@ TEST_CASE("GetInformation_WithAuthenticationInfo_Success", "[RestSource]")
}})delimiter");
HttpClientHelper helper{ GetTestRestRequestHandler(web::http::status_codes::OK, sample) };
- IRestClient::Information information = RestClient::GetInformation(TestRestUri, {}, {}, std::move(helper));
+ IRestClient::Information information = RestClient::GetInformation(TestRestUri, {}, {}, helper);
REQUIRE(information.SourceIdentifier == "Source123");
REQUIRE(information.ServerSupportedVersions.size() == 1);
REQUIRE(information.ServerSupportedVersions.at(0) == "1.7.0");
@@ -208,7 +218,7 @@ TEST_CASE("GetInformation_Fail_AgreementsWithoutIdentifier", "[RestSource]")
}})delimiter");
HttpClientHelper helper{ GetTestRestRequestHandler(web::http::status_codes::OK, sample) };
- REQUIRE_THROWS_HR(RestClient::GetInformation(TestRestUri, {}, {}, std::move(helper)), APPINSTALLER_CLI_ERROR_UNSUPPORTED_RESTSOURCE);
+ REQUIRE_THROWS_HR(RestClient::GetInformation(TestRestUri, {}, {}, helper), APPINSTALLER_CLI_ERROR_UNSUPPORTED_RESTSOURCE);
}
TEST_CASE("GetInformation_Fail_InvalidMicrosoftEntraIdInfo", "[RestSource]")
@@ -226,7 +236,7 @@ TEST_CASE("GetInformation_Fail_InvalidMicrosoftEntraIdInfo", "[RestSource]")
}})delimiter");
HttpClientHelper helper1{ GetTestRestRequestHandler(web::http::status_codes::OK, sample1) };
- REQUIRE_THROWS_HR(RestClient::GetInformation(TestRestUri, {}, {}, std::move(helper1)), APPINSTALLER_CLI_ERROR_UNSUPPORTED_RESTSOURCE);
+ REQUIRE_THROWS_HR(RestClient::GetInformation(TestRestUri, {}, {}, helper1), APPINSTALLER_CLI_ERROR_UNSUPPORTED_RESTSOURCE);
utility::string_t sample2 = _XPLATSTR(
R"delimiter({
@@ -245,7 +255,7 @@ TEST_CASE("GetInformation_Fail_InvalidMicrosoftEntraIdInfo", "[RestSource]")
}})delimiter");
HttpClientHelper helper2{ GetTestRestRequestHandler(web::http::status_codes::OK, sample2) };
- REQUIRE_THROWS_HR(RestClient::GetInformation(TestRestUri, {}, {}, std::move(helper2)), APPINSTALLER_CLI_ERROR_UNSUPPORTED_RESTSOURCE);
+ REQUIRE_THROWS_HR(RestClient::GetInformation(TestRestUri, {}, {}, helper2), APPINSTALLER_CLI_ERROR_UNSUPPORTED_RESTSOURCE);
utility::string_t sample3 = _XPLATSTR(
R"delimiter({
@@ -263,7 +273,7 @@ TEST_CASE("GetInformation_Fail_InvalidMicrosoftEntraIdInfo", "[RestSource]")
}})delimiter");
HttpClientHelper helper3{ GetTestRestRequestHandler(web::http::status_codes::OK, sample3) };
- REQUIRE_THROWS_HR(RestClient::GetInformation(TestRestUri, {}, {}, std::move(helper3)), APPINSTALLER_CLI_ERROR_UNSUPPORTED_RESTSOURCE);
+ REQUIRE_THROWS_HR(RestClient::GetInformation(TestRestUri, {}, {}, helper3), APPINSTALLER_CLI_ERROR_UNSUPPORTED_RESTSOURCE);
utility::string_t sample4 = _XPLATSTR(
R"delimiter({
@@ -281,7 +291,7 @@ TEST_CASE("GetInformation_Fail_InvalidMicrosoftEntraIdInfo", "[RestSource]")
Authentication::AuthenticationArguments authArgs;
authArgs.Mode = Authentication::AuthenticationMode::Silent;
Version version_1_7{ "1.7.0" };
- REQUIRE_THROWS_HR(RestClient::GetSupportedInterface(TestRestUri, {}, RestClient::GetInformation(TestRestUri, {}, {}, std::move(helper4)), authArgs, version_1_7, {}), APPINSTALLER_CLI_ERROR_AUTHENTICATION_TYPE_NOT_SUPPORTED);
+ REQUIRE_THROWS_HR(RestClient::GetSupportedInterface(TestRestUri, {}, RestClient::GetInformation(TestRestUri, {}, {}, helper4), authArgs, version_1_7, {}), APPINSTALLER_CLI_ERROR_AUTHENTICATION_TYPE_NOT_SUPPORTED);
}
TEST_CASE("RestClientCreate_UnsupportedVersion", "[RestSource]")
@@ -296,7 +306,7 @@ TEST_CASE("RestClientCreate_UnsupportedVersion", "[RestSource]")
}})delimiter");
HttpClientHelper helper{ GetTestRestRequestHandler(web::http::status_codes::OK, sample) };
- REQUIRE_THROWS_HR(RestClient::Create("https://restsource.com/api", {}, {}, std::move(helper)), APPINSTALLER_CLI_ERROR_UNSUPPORTED_RESTSOURCE);
+ REQUIRE_THROWS_HR(CreateRestClient("https://restsource.com/api", {}, {}, helper), APPINSTALLER_CLI_ERROR_UNSUPPORTED_RESTSOURCE);
}
TEST_CASE("RestClientCreate_UnsupportedAuthenticationMethod", "[RestSource]")
@@ -316,7 +326,7 @@ TEST_CASE("RestClientCreate_UnsupportedAuthenticationMethod", "[RestSource]")
HttpClientHelper helper{ GetTestRestRequestHandler(web::http::status_codes::OK, sample) };
Authentication::AuthenticationArguments authArgs;
authArgs.Mode = Authentication::AuthenticationMode::Silent;
- REQUIRE_THROWS_HR(RestClient::Create("https://restsource.com/api", {}, {}, std::move(helper), std::move(authArgs)), APPINSTALLER_CLI_ERROR_AUTHENTICATION_TYPE_NOT_SUPPORTED);
+ REQUIRE_THROWS_HR(CreateRestClient("https://restsource.com/api", {}, {}, helper, authArgs), APPINSTALLER_CLI_ERROR_AUTHENTICATION_TYPE_NOT_SUPPORTED);
}
TEST_CASE("RestClientCreate_InvalidAuthenticationArguments", "[RestSource]")
@@ -339,7 +349,7 @@ TEST_CASE("RestClientCreate_InvalidAuthenticationArguments", "[RestSource]")
HttpClientHelper helper{ GetTestRestRequestHandler(web::http::status_codes::OK, sample) };
Authentication::AuthenticationArguments authArgs;
authArgs.Mode = Authentication::AuthenticationMode::Unknown;
- REQUIRE_THROWS_HR(RestClient::Create("https://restsource.com/api", {}, {}, std::move(helper), std::move(authArgs)), E_UNEXPECTED);
+ REQUIRE_THROWS_HR(CreateRestClient("https://restsource.com/api", {}, {}, helper, authArgs), E_UNEXPECTED);
}
TEST_CASE("RestClientCreate_1.0_Success", "[RestSource]")
@@ -354,7 +364,7 @@ TEST_CASE("RestClientCreate_1.0_Success", "[RestSource]")
}})delimiter");
HttpClientHelper helper{ GetTestRestRequestHandler(web::http::status_codes::OK, sample) };
- RestClient client = RestClient::Create(TestRestUri, {}, {}, std::move(helper), {});
+ RestClient client = CreateRestClient(TestRestUri, {}, {}, helper);
REQUIRE(client.GetSourceIdentifier() == "Source123");
}
@@ -391,7 +401,7 @@ TEST_CASE("RestClientCreate_1.1_Success", "[RestSource]")
}})delimiter");
HttpClientHelper helper{ GetTestRestRequestHandler(web::http::status_codes::OK, sample) };
- RestClient client = RestClient::Create(TestRestUri, {}, {}, std::move(helper));
+ RestClient client = CreateRestClient(TestRestUri, {}, {}, helper);
REQUIRE(client.GetSourceIdentifier() == "Source123");
auto information = client.GetSourceInformation();
REQUIRE(information.SourceAgreementsIdentifier == "agreementV1");
@@ -457,7 +467,7 @@ TEST_CASE("RestClientCreate_1.7_Success", "[RestSource]")
Authentication::AuthenticationArguments authArgs;
authArgs.Mode = Authentication::AuthenticationMode::Silent;
HttpClientHelper helper{ GetTestRestRequestHandler(web::http::status_codes::OK, sample) };
- RestClient client = RestClient::Create(TestRestUri, {}, {}, std::move(helper), std::move(authArgs));
+ RestClient client = CreateRestClient(TestRestUri, {}, {}, helper, authArgs);
REQUIRE(client.GetSourceIdentifier() == "Source123");
auto information = client.GetSourceInformation();
REQUIRE(information.SourceAgreementsIdentifier == "agreementV1");
diff --git a/src/AppInstallerCommonCore/Rest.cpp b/src/AppInstallerCommonCore/Rest.cpp
@@ -7,10 +7,9 @@
namespace AppInstaller::Rest
{
- utility::string_t GetRestAPIBaseUri(std::string restApiUri)
+ utility::string_t GetRestAPIBaseUri(std::string uri)
{
// Trim
- std::string uri = restApiUri;
if (!uri.empty())
{
uri = AppInstaller::Utility::Trim(uri);
diff --git a/src/AppInstallerRepositoryCore/Rest/RestClient.cpp b/src/AppInstallerRepositoryCore/Rest/RestClient.cpp
@@ -30,7 +30,7 @@ namespace AppInstaller::Repository::Rest
namespace
{
- HttpClientHelper::HttpRequestHeaders GetHeaders(std::optional<std::string> customHeader, std::string_view caller)
+ HttpClientHelper::HttpRequestHeaders GetHeaders(const std::optional<std::string>& customHeader, std::string_view caller)
{
HttpClientHelper::HttpRequestHeaders headers;
@@ -135,7 +135,7 @@ namespace AppInstaller::Repository::Rest
return *commonVersions.rbegin();
}
- Schema::IRestClient::Information RestClient::GetInformation(const std::string& restApi, std::optional<std::string> customHeader, std::string_view caller, const HttpClientHelper& helper)
+ Schema::IRestClient::Information RestClient::GetInformation(const std::string& restApi, const std::optional<std::string>& customHeader, std::string_view caller, const HttpClientHelper& helper)
{
utility::string_t restEndpoint = AppInstaller::Rest::GetRestAPIBaseUri(restApi);
THROW_HR_IF(APPINSTALLER_CLI_ERROR_RESTSOURCE_INVALID_URL, !AppInstaller::Rest::IsValidUri(restEndpoint));
@@ -185,14 +185,19 @@ namespace AppInstaller::Repository::Rest
THROW_HR(APPINSTALLER_CLI_ERROR_RESTSOURCE_INVALID_VERSION);
}
- RestClient RestClient::Create(const std::string& restApi, std::optional<std::string> customHeader, std::string_view caller, const HttpClientHelper& helper, const Authentication::AuthenticationArguments& authArgs)
+ RestClient RestClient::Create(
+ const std::string& restApi,
+ const std::optional<std::string>& customHeader,
+ std::string_view caller,
+ const HttpClientHelper& helper,
+ const Schema::IRestClient::Information& information,
+ const Authentication::AuthenticationArguments& authArgs)
{
utility::string_t restEndpoint = AppInstaller::Rest::GetRestAPIBaseUri(restApi);
THROW_HR_IF(APPINSTALLER_CLI_ERROR_RESTSOURCE_INVALID_URL, !AppInstaller::Rest::IsValidUri(restEndpoint));
auto headers = GetHeaders(customHeader, caller);
- IRestClient::Information information = GetInformationInternal(restEndpoint, headers, helper);
std::optional<Version> latestCommonVersion = GetLatestCommonVersion(information.ServerSupportedVersions, WingetSupportedContracts);
THROW_HR_IF(APPINSTALLER_CLI_ERROR_UNSUPPORTED_RESTSOURCE, !latestCommonVersion);
diff --git a/src/AppInstallerRepositoryCore/Rest/RestClient.h b/src/AppInstallerRepositoryCore/Rest/RestClient.h
@@ -28,7 +28,7 @@ namespace AppInstaller::Repository::Rest
static std::optional<AppInstaller::Utility::Version> GetLatestCommonVersion(const std::vector<std::string>& serverSupportedVersions, const std::set<AppInstaller::Utility::Version>& wingetSupportedVersions);
// Responsible for getting the source information contracts with minimal validation. Does not try to create a rest interface out of it.
- static Schema::IRestClient::Information GetInformation(const std::string& restApi, std::optional<std::string> customHeader, std::string_view caller, const Http::HttpClientHelper& helper);
+ static Schema::IRestClient::Information GetInformation(const std::string& restApi, const std::optional<std::string>& customHeader, std::string_view caller, const Http::HttpClientHelper& helper);
static std::unique_ptr<Schema::IRestClient> GetSupportedInterface(
const std::string& restApi,
@@ -39,7 +39,13 @@ namespace AppInstaller::Repository::Rest
const Http::HttpClientHelper& helper);
// Creates the rest client. Full validation performed (just as opening the source)
- static RestClient Create(const std::string& restApi, std::optional<std::string> customHeader, std::string_view caller, const Http::HttpClientHelper& helper, const Authentication::AuthenticationArguments& authArgs = {});
+ static RestClient Create(
+ const std::string& restApi,
+ const std::optional<std::string>& customHeader,
+ std::string_view caller,
+ const Http::HttpClientHelper& helper,
+ const Schema::IRestClient::Information& information,
+ const Authentication::AuthenticationArguments& authArgs = {});
private:
RestClient(std::unique_ptr<Schema::IRestClient> supportedInterface, std::string sourceIdentifier);
diff --git a/src/AppInstallerRepositoryCore/Rest/RestSourceFactory.cpp b/src/AppInstallerRepositoryCore/Rest/RestSourceFactory.cpp
@@ -50,7 +50,7 @@ namespace AppInstaller::Repository::Rest
std::shared_ptr<ISource> Open(IProgressCallback&) override
{
Initialize();
- RestClient restClient = RestClient::Create(m_details.Arg, m_customHeader, m_caller, m_httpClientHelper, m_authArgs);
+ RestClient restClient = RestClient::Create(m_details.Arg, m_customHeader, m_caller, m_httpClientHelper, m_restClientInformation, m_authArgs);
return std::make_shared<RestSource>(m_details, m_information, std::move(restClient));
}
@@ -61,28 +61,29 @@ namespace AppInstaller::Repository::Rest
[&]()
{
m_httpClientHelper.SetPinningConfiguration(m_details.CertificatePinningConfiguration);
- auto sourceInformation = RestClient::GetInformation(m_details.Arg, m_customHeader, m_caller, m_httpClientHelper);
+ m_restClientInformation = RestClient::GetInformation(m_details.Arg, m_customHeader, m_caller, m_httpClientHelper);
- m_details.Identifier = sourceInformation.SourceIdentifier;
+ m_details.Identifier = m_restClientInformation.SourceIdentifier;
- m_information.UnsupportedPackageMatchFields = sourceInformation.UnsupportedPackageMatchFields;
- m_information.RequiredPackageMatchFields = sourceInformation.RequiredPackageMatchFields;
- m_information.UnsupportedQueryParameters = sourceInformation.UnsupportedQueryParameters;
- m_information.RequiredQueryParameters = sourceInformation.RequiredQueryParameters;
+ m_information.UnsupportedPackageMatchFields = m_restClientInformation.UnsupportedPackageMatchFields;
+ m_information.RequiredPackageMatchFields = m_restClientInformation.RequiredPackageMatchFields;
+ m_information.UnsupportedQueryParameters = m_restClientInformation.UnsupportedQueryParameters;
+ m_information.RequiredQueryParameters = m_restClientInformation.RequiredQueryParameters;
- m_information.SourceAgreementsIdentifier = sourceInformation.SourceAgreementsIdentifier;
- for (auto const& agreement : sourceInformation.SourceAgreements)
+ m_information.SourceAgreementsIdentifier = m_restClientInformation.SourceAgreementsIdentifier;
+ for (auto const& agreement : m_restClientInformation.SourceAgreements)
{
m_information.SourceAgreements.emplace_back(agreement.Label, agreement.Text, agreement.Url);
}
- m_information.Authentication = sourceInformation.Authentication;
+ m_information.Authentication = m_restClientInformation.Authentication;
});
}
SourceDetails m_details;
Http::HttpClientHelper m_httpClientHelper;
SourceInformation m_information;
+ Schema::IRestClient::Information m_restClientInformation;
std::optional<std::string> m_customHeader;
std::string m_caller;
Authentication::AuthenticationArguments m_authArgs;