commit d82f5876a81abf979e9328f0a1823b7952d12466
parent 56e210b30e42ad26b3750a7ea8a1e1b6eacc5d8d
Author: Ashwini Patil <47225815+ashpatil-msft@users.noreply.github.com>
Date: Mon, 30 Aug 2021 22:33:10 -0700
Integrating custom header with V1.1 changes (#1422)
Integrating custom header with V1.1 changes
Diffstat:
10 files changed, 80 insertions(+), 19 deletions(-)
diff --git a/.github/actions/spelling/allow.txt b/.github/actions/spelling/allow.txt
@@ -78,6 +78,7 @@ cref
csproj
CStr
CURSORPOSITON
+CUSTOMHEADER
cwctype
datatelemetry
dbconn
@@ -249,6 +250,7 @@ MAKEINTRESOURCE
makemsix
MANIFESTSCHEMA
MANIFESTVERSION
+MAXLENGTH
MBs
mday
memset
diff --git a/src/AppInstallerCLICore/Workflows/SourceFlow.cpp b/src/AppInstallerCLICore/Workflows/SourceFlow.cpp
@@ -90,8 +90,6 @@ namespace AppInstaller::CLI::Workflow
sourceDetails.Type = context.Args.GetArg(Args::Type::SourceType);
}
- sourceDetails.CustomHeader = GetCustomHeaderFromArg(context, sourceDetails);
-
context.Reporter.Info() <<
Resource::String::SourceAddBegin << std::endl <<
" "_liv << sourceDetails.Name << " -> "_liv << sourceDetails.Arg << std::endl;
diff --git a/src/AppInstallerCLIE2ETests/Constants.cs b/src/AppInstallerCLIE2ETests/Constants.cs
@@ -137,6 +137,7 @@ namespace AppInstallerCLIE2ETests
public const int ERROR_RESTSOURCE_ENDPOINT_NOT_FOUND = unchecked((int)0x8a150044);
public const int ERROR_SOURCE_OPEN_FAILED = unchecked((int)0x8a150045);
public const int ERROR_SOURCE_AGREEMENTS_NOT_ACCEPTED = unchecked((int)0x8a150046);
+ public const int ERROR_CUSTOMHEADER_EXCEEDS_MAXLENGTH = unchecked((int)0x8a150047);
}
}
}
diff --git a/src/AppInstallerCLITests/CustomHeader.cpp b/src/AppInstallerCLITests/CustomHeader.cpp
@@ -6,7 +6,7 @@
#include "TestSettings.h"
#include "TestSource.h"
#include "TestRestRequestHandler.h"
-#include <Rest/Schema/1_0/Interface.h>
+#include <Rest/Schema/1_1/Interface.h>
#include <Rest/Schema/JsonHelper.h>
#include <Rest/RestClient.h>
#include <winget/Settings.h>
@@ -161,3 +161,52 @@ TEST_CASE("CreateSource_CustomHeaderNotApplicable", "[RestSource][CustomHeader]"
auto source = OpenSourceFromDetails(details, progress).Source;
REQUIRE(!source.get()->GetDetails().CustomHeader.has_value());
}
+
+TEST_CASE("RestSourceSearch_CustomHeader", "[RestSource][CustomHeader]")
+{
+ utility::string_t customHeader = L"Testing custom header";
+ auto header = std::make_pair<>(CustomHeaderName, customHeader);
+ HttpClientHelper helper{ GetCustomHeaderVerificationHandler(web::http::status_codes::OK, sampleSearchResponse, header) };
+ std::unordered_map<utility::string_t, utility::string_t> headers;
+ headers.emplace(CustomHeaderName, customHeader);
+
+ V1_1::Interface v1_1{ "https://restsource.com/api", {}, headers, std::move(helper) };
+ Schema::IRestClient::SearchResult searchResponse = v1_1.Search({});
+ REQUIRE(searchResponse.Matches.size() == 1);
+ Schema::IRestClient::Package package = searchResponse.Matches.at(0);
+}
+
+TEST_CASE("RestSourceSearch_WhitespaceCustomHeader", "[RestSource][CustomHeader]")
+{
+ utility::string_t customHeader = L" ";
+ auto header = std::make_pair<>(CustomHeaderName, customHeader);
+ HttpClientHelper helper{ GetCustomHeaderVerificationHandler(web::http::status_codes::OK, sampleSearchResponse, header) };
+ std::unordered_map<utility::string_t, utility::string_t> headers;
+ headers.emplace(CustomHeaderName, customHeader);
+
+ V1_1::Interface v1_1{ "https://restsource.com/api", {}, headers, std::move(helper) };
+ Schema::IRestClient::SearchResult searchResponse = v1_1.Search({});
+ REQUIRE(searchResponse.Matches.size() == 1);
+}
+
+TEST_CASE("RestSourceSearch_NoCustomHeader", "[RestSource][CustomHeader]")
+{
+ utility::string_t customHeader = L" ";
+ auto header = std::make_pair<>(CustomHeaderName, customHeader);
+ HttpClientHelper helper{ GetCustomHeaderVerificationHandler(web::http::status_codes::OK, sampleSearchResponse, header) };
+ std::unordered_map<utility::string_t, utility::string_t> headers;
+ headers.emplace(CustomHeaderName, customHeader);
+
+ V1_1::Interface v1_1{ "https://restsource.com/api", {}, {}, std::move(helper) };
+ REQUIRE_THROWS_HR(v1_1.Search({}), APPINSTALLER_CLI_ERROR_RESTSOURCE_INTERNAL_ERROR);
+}
+
+TEST_CASE("RestSourceSearch_CustomHeaderExceedingSize", "[RestSource][CustomHeader]")
+{
+ std::string customHeader = "This is a custom header that is longer than 1024 characters. This is a custom header that is longer than 1024 characters. This is a custom header that is longer than 1024 characters. This is a custom header that is longer than 1024 characters. This is a custom header that is longer than 1024 characters. This is a custom header that is longer than 1024 characters. This is a custom header that is longer than 1024 characters. This is a custom header that is longer than 1024 characters. This is a custom header that is longer than 1024 characters. This is a custom header that is longer than 1024 characters. This is a custom header that is longer than 1024 characters. This is a custom header that is longer than 1024 characters. This is a custom header that is longer than 1024 characters. This is a custom header that is longer than 1024 characters. This is a custom header that is longer than 1024 characters. This is a custom header that is longer than 1024 characters. This is a custom header that is longer than 1024 characters. This is a custom header that is longer than 1024 characters. This is a custom header that is longer than 1024 characters. This is a custom header that is longer than 1024 characters. This is a custom header that is longer than 1024 characters. This is a custom header that is longer than 1024 characters. ";
+ auto header = std::make_pair<>(CustomHeaderName, JsonHelper::GetUtilityString(customHeader));
+ HttpClientHelper helper{ GetCustomHeaderVerificationHandler(web::http::status_codes::OK, sampleSearchResponse, header) };
+
+ REQUIRE_THROWS_HR(RestClient::Create(utility::conversions::to_utf8string("https://restsource.com/api"), customHeader, std::move(helper)),
+ APPINSTALLER_CLI_ERROR_CUSTOMHEADER_EXCEEDS_MAXLENGTH);
+}
diff --git a/src/AppInstallerCLITests/RestInterface_1_1.cpp b/src/AppInstallerCLITests/RestInterface_1_1.cpp
@@ -42,7 +42,7 @@ TEST_CASE("Search_BadResponse_UnsupportedPackageMatchFields", "[RestSource][Inte
})delimiter");
HttpClientHelper helper{ GetTestRestRequestHandler(web::http::status_codes::OK, std::move(sample)) };
- Interface v1_1{ TestRestUriString, GetTestSourceInformation(), std::move(helper) };
+ Interface v1_1{ TestRestUriString, GetTestSourceInformation(), {}, std::move(helper) };
AppInstaller::Repository::SearchRequest request;
PackageMatchFilter filter{ PackageMatchField::Name, MatchType::Exact, "Foo" };
request.Filters.emplace_back(std::move(filter));
@@ -58,7 +58,7 @@ TEST_CASE("Search_BadResponse_RequiredPackageMatchFields", "[RestSource][Interfa
})delimiter");
HttpClientHelper helper{ GetTestRestRequestHandler(web::http::status_codes::OK, std::move(sample)) };
- Interface v1_1{ TestRestUriString, GetTestSourceInformation(), std::move(helper) };
+ Interface v1_1{ TestRestUriString, GetTestSourceInformation(), {}, std::move(helper) };
AppInstaller::Repository::SearchRequest request;
PackageMatchFilter filter{ PackageMatchField::Name, MatchType::Exact, "Foo" };
request.Filters.emplace_back(std::move(filter));
@@ -74,7 +74,7 @@ TEST_CASE("GetManifests_BadResponse_UnsupportedQueryParameters", "[RestSource][I
})delimiter");
HttpClientHelper helper{ GetTestRestRequestHandler(web::http::status_codes::OK, std::move(sample)) };
- Interface v1_1{ TestRestUriString, GetTestSourceInformation(), std::move(helper) };
+ Interface v1_1{ TestRestUriString, GetTestSourceInformation(), {}, std::move(helper) };
REQUIRE_THROWS_HR(v1_1.GetManifests("Foo"), APPINSTALLER_CLI_ERROR_UNSUPPORTED_SOURCE_REQUEST);
}
@@ -87,7 +87,7 @@ TEST_CASE("GetManifests_BadResponse_RequiredQueryParameters", "[RestSource][Inte
})delimiter");
HttpClientHelper helper{ GetTestRestRequestHandler(web::http::status_codes::OK, std::move(sample)) };
- Interface v1_1{ TestRestUriString, GetTestSourceInformation(), std::move(helper) };
+ Interface v1_1{ TestRestUriString, GetTestSourceInformation(), {}, std::move(helper) };
REQUIRE_THROWS_HR(v1_1.GetManifests("Foo"), APPINSTALLER_CLI_ERROR_UNSUPPORTED_SOURCE_REQUEST);
}
@@ -107,7 +107,7 @@ TEST_CASE("Search_BadRequest_UnsupportedPackageMatchFields", "[RestSource][Inter
})delimiter");
HttpClientHelper helper{ GetTestRestRequestHandler(web::http::status_codes::OK, std::move(sample)) };
- Interface v1_1{ TestRestUriString, GetTestSourceInformation(), std::move(helper) };
+ Interface v1_1{ TestRestUriString, GetTestSourceInformation(), {}, std::move(helper) };
AppInstaller::Repository::SearchRequest request;
PackageMatchFilter filter{ PackageMatchField::Moniker, MatchType::Exact, "Foo" };
request.Filters.emplace_back(std::move(filter));
@@ -130,7 +130,7 @@ TEST_CASE("Search_GoodRequest_OnlyMarketRequired", "[RestSource][Interface_1_1]"
})delimiter");
HttpClientHelper helper{ GetTestRestRequestHandler(web::http::status_codes::OK, std::move(sample)) };
- Interface v1_1{ TestRestUriString, GetTestSourceInformation(), std::move(helper) };
+ Interface v1_1{ TestRestUriString, GetTestSourceInformation(), {}, std::move(helper) };
AppInstaller::Repository::SearchRequest request;
PackageMatchFilter filter{ PackageMatchField::Name, MatchType::Exact, "Foo" };
request.Filters.emplace_back(std::move(filter));
@@ -175,7 +175,7 @@ TEST_CASE("GetManifests_BadRequest_UnsupportedQueryParameters", "[RestSource][In
})delimiter");
HttpClientHelper helper{ GetTestRestRequestHandler(web::http::status_codes::OK, std::move(sample)) };
- Interface v1_1{ TestRestUriString, GetTestSourceInformation(), std::move(helper) };
+ Interface v1_1{ TestRestUriString, GetTestSourceInformation(), {}, std::move(helper) };
REQUIRE_THROWS_HR(v1_1.GetManifestByVersion("Foo", "1.0", "beta"), APPINSTALLER_CLI_ERROR_UNSUPPORTED_SOURCE_REQUEST);
}
@@ -211,7 +211,7 @@ TEST_CASE("GetManifests_GoodRequest_OnlyMarketRequired", "[RestSource][Interface
IRestClient::Information info = GetTestSourceInformation();
info.UnsupportedQueryParameters.clear();
HttpClientHelper helper{ GetTestRestRequestHandler(web::http::status_codes::OK, std::move(sample)) };
- Interface v1_1{ TestRestUriString, info, std::move(helper) };
+ Interface v1_1{ TestRestUriString, info, {}, std::move(helper) };
auto manifestResult = v1_1.GetManifestByVersion("Foo", "5.0.0", "");
REQUIRE(manifestResult.has_value());
const Manifest& manifest = manifestResult.value();
@@ -258,7 +258,7 @@ TEST_CASE("GetManifests_GoodResponse_MSStoreType", "[RestSource][Interface_1_1]"
})delimiter");
HttpClientHelper helper{ GetTestRestRequestHandler(web::http::status_codes::OK, std::move(msstoreInstallerResponse)) };
- Interface v1_1{ TestRestUriString, GetTestSourceInformation(), std::move(helper) };
+ Interface v1_1{ TestRestUriString, GetTestSourceInformation(), {}, std::move(helper) };
std::vector<Manifest> manifests = v1_1.GetManifests("Foo.Bar");
REQUIRE(manifests.size() == 1);
diff --git a/src/AppInstallerCommonCore/Errors.cpp b/src/AppInstallerCommonCore/Errors.cpp
@@ -154,6 +154,8 @@ namespace AppInstaller
return "Failed to open the source.";
case APPINSTALLER_CLI_ERROR_SOURCE_AGREEMENTS_NOT_ACCEPTED:
return "Source agreements were not agreed to";
+ case APPINSTALLER_CLI_ERROR_CUSTOMHEADER_EXCEEDS_MAXLENGTH:
+ return "Header size exceeds the allowable limit of 1024 characters. Please reduce the size and try again.";
default:
return "Unknown Error Code";
}
diff --git a/src/AppInstallerCommonCore/Public/AppInstallerErrors.h b/src/AppInstallerCommonCore/Public/AppInstallerErrors.h
@@ -84,6 +84,7 @@
#define APPINSTALLER_CLI_ERROR_RESTSOURCE_ENDPOINT_NOT_FOUND ((HRESULT)0x8a150044)
#define APPINSTALLER_CLI_ERROR_SOURCE_OPEN_FAILED ((HRESULT)0x8a150045)
#define APPINSTALLER_CLI_ERROR_SOURCE_AGREEMENTS_NOT_ACCEPTED ((HRESULT)0x8a150046)
+#define APPINSTALLER_CLI_ERROR_CUSTOMHEADER_EXCEEDS_MAXLENGTH ((HRESULT)0x8a150047)
namespace AppInstaller
diff --git a/src/AppInstallerRepositoryCore/Rest/RestClient.cpp b/src/AppInstallerRepositoryCore/Rest/RestClient.cpp
@@ -20,6 +20,7 @@ namespace AppInstaller::Repository::Rest
std::set<Version> WingetSupportedContracts = { Version_1_0_0, Version_1_1_0 };
constexpr std::string_view WindowsPackageManagerHeader = "Windows-Package-Manager"sv;
+ constexpr size_t WindowsPackageManagerHeaderMaxLength = 1024;
namespace {
std::unordered_map<utility::string_t, utility::string_t> GetHeaders(std::optional<std::string> customHeader)
@@ -30,6 +31,8 @@ namespace AppInstaller::Repository::Rest
return {};
}
+ THROW_HR_IF(APPINSTALLER_CLI_ERROR_CUSTOMHEADER_EXCEEDS_MAXLENGTH, customHeader.value().size() > WindowsPackageManagerHeaderMaxLength);
+
std::unordered_map<utility::string_t, utility::string_t> headers;
headers.emplace(JsonHelper::GetUtilityString(WindowsPackageManagerHeader), JsonHelper::GetUtilityString(customHeader.value()));
return headers;
@@ -125,12 +128,9 @@ namespace AppInstaller::Repository::Rest
}
else if (version == Version_1_1_0)
{
- return std::make_unique<Schema::V1_1::Interface>(api, information);
+ return std::make_unique<Schema::V1_1::Interface>(api, information, additionalHeaders);
}
- // TODO: USE additionalHeaders with V1.1 changes.
- (void)additionalHeaders;
-
THROW_HR(APPINSTALLER_CLI_ERROR_RESTSOURCE_INVALID_VERSION);
}
diff --git a/src/AppInstallerRepositoryCore/Rest/Schema/1_1/Interface.h b/src/AppInstallerRepositoryCore/Rest/Schema/1_1/Interface.h
@@ -8,7 +8,7 @@ namespace AppInstaller::Repository::Rest::Schema::V1_1
// Interface to this schema version exposed through IRestClient.
struct Interface : public V1_0::Interface
{
- Interface(const std::string& restApi, IRestClient::Information information, const HttpClientHelper& httpClientHelper = {});
+ Interface(const std::string& restApi, IRestClient::Information information, const std::unordered_map<utility::string_t, utility::string_t>& additionalHeaders = {}, const HttpClientHelper& httpClientHelper = {});
Interface(const Interface&) = delete;
Interface& operator=(const Interface&) = delete;
diff --git a/src/AppInstallerRepositoryCore/Rest/Schema/1_1/RestInterface_1_1.cpp b/src/AppInstallerRepositoryCore/Rest/Schema/1_1/RestInterface_1_1.cpp
@@ -27,10 +27,18 @@ namespace AppInstaller::Repository::Rest::Schema::V1_1
constexpr std::string_view RequiredQueryParameters = "RequiredQueryParameters"sv;
}
- Interface::Interface(const std::string& restApi, IRestClient::Information information, const HttpClientHelper& httpClientHelper) :
- V1_0::Interface(restApi, httpClientHelper), m_information(std::move(information))
+ Interface::Interface(
+ const std::string& restApi,
+ IRestClient::Information information,
+ const std::unordered_map<utility::string_t, utility::string_t>& additionalHeaders,
+ const HttpClientHelper& httpClientHelper) : V1_0::Interface(restApi, httpClientHelper), m_information(std::move(information))
{
m_requiredRestApiHeaders[JsonHelper::GetUtilityString(ContractVersion)] = JsonHelper::GetUtilityString(Version_1_1_0.ToString());
+
+ if (!additionalHeaders.empty())
+ {
+ m_requiredRestApiHeaders.insert(additionalHeaders.begin(), additionalHeaders.end());
+ }
}
Utility::Version Interface::GetVersion() const