winget-cli

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

commit 63ea8b0cef902df41be4b5d49eca20be7ea36f88
parent 58402d4a0b4dd47fbbe61f8ffb6f29d799fd9f3e
Author: Ashwini Patil <47225815+ashpatil-msft@users.noreply.github.com>
Date:   Fri, 27 Aug 2021 16:22:48 -0700

Pass through header changes (#1364)

Arbitrary Header Changes
Diffstat:
Msrc/AppInstallerCLICore/Argument.cpp | 2++
Msrc/AppInstallerCLICore/Command.cpp | 6++++++
Msrc/AppInstallerCLICore/Commands/InstallCommand.cpp | 1+
Msrc/AppInstallerCLICore/Commands/ListCommand.cpp | 1+
Msrc/AppInstallerCLICore/Commands/SearchCommand.cpp | 1+
Msrc/AppInstallerCLICore/Commands/ShowCommand.cpp | 3+++
Msrc/AppInstallerCLICore/Commands/SourceCommand.cpp | 1+
Msrc/AppInstallerCLICore/Commands/UninstallCommand.cpp | 1+
Msrc/AppInstallerCLICore/Commands/UpgradeCommand.cpp | 1+
Msrc/AppInstallerCLICore/ExecutionArgs.h | 1+
Msrc/AppInstallerCLICore/Resources.h | 3+++
Msrc/AppInstallerCLICore/Workflows/SourceFlow.cpp | 15+++++++++------
Msrc/AppInstallerCLICore/Workflows/WorkflowBase.cpp | 38+++++++++++++++++++++++++++++++++++---
Msrc/AppInstallerCLICore/Workflows/WorkflowBase.h | 4++++
Msrc/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw | 9+++++++++
Msrc/AppInstallerCLITests/AppInstallerCLITests.vcxproj | 1+
Msrc/AppInstallerCLITests/AppInstallerCLITests.vcxproj.filters | 981++++++++++++++++++++++++++++++++++++++++---------------------------------------
Asrc/AppInstallerCLITests/CustomHeader.cpp | 163+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/AppInstallerCLITests/PreIndexedPackageSource.cpp | 42+++++++++++++++++++++++-------------------
Msrc/AppInstallerCLITests/RestClient.cpp | 10+++++-----
Msrc/AppInstallerCLITests/Sources.cpp | 177+++++++++++++++++++++++++++++++++++++++++++++----------------------------------
Msrc/AppInstallerCLITests/TestSource.h | 3+++
Msrc/AppInstallerCLITests/WorkFlow.cpp | 41+++++++++++++++++++++++++++++++++++++++--
Msrc/AppInstallerRepositoryCore/Public/AppInstallerRepositorySource.h | 8+++++++-
Msrc/AppInstallerRepositoryCore/RepositorySource.cpp | 32++++++++++++++++----------------
Msrc/AppInstallerRepositoryCore/Rest/RestClient.cpp | 36++++++++++++++++++++++++++++++------
Msrc/AppInstallerRepositoryCore/Rest/RestClient.h | 8++++----
Msrc/AppInstallerRepositoryCore/Rest/RestSourceFactory.cpp | 2+-
28 files changed, 964 insertions(+), 627 deletions(-)

diff --git a/src/AppInstallerCLICore/Argument.cpp b/src/AppInstallerCLICore/Argument.cpp @@ -80,6 +80,8 @@ namespace AppInstaller::CLI return Argument{ "verbose-logs", NoAlias, Args::Type::VerboseLogs, Resource::String::VerboseLogsArgumentDescription, ArgumentType::Flag }; case Args::Type::ExperimentalArg: return Argument{ "arg", NoAlias, Args::Type::ExperimentalArg, Resource::String::ExperimentalArgumentDescription, ArgumentType::Flag, ExperimentalFeature::Feature::ExperimentalArg }; + case Args::Type::CustomHeader: + return Argument{ "header", NoAlias, Args::Type::CustomHeader, Resource::String::HeaderArgumentDescription, ArgumentType::Standard, Argument::Visibility::Help }; default: THROW_HR(E_UNEXPECTED); } diff --git a/src/AppInstallerCLICore/Command.cpp b/src/AppInstallerCLICore/Command.cpp @@ -676,6 +676,12 @@ namespace AppInstaller::CLI throw CommandException(Resource::String::TooManyBehaviorsError, s_Command_ArgName_SilentAndInteractive); } + if (execArgs.Contains(Execution::Args::Type::CustomHeader) && !execArgs.Contains(Execution::Args::Type::Source) && + !execArgs.Contains(Execution::Args::Type::SourceName)) + { + throw CommandException(Resource::String::HeaderArgumentNotApplicableWithoutSource, Argument::ForType(Execution::Args::Type::CustomHeader).Name(), {}); + } + ValidateArgumentsInternal(execArgs); } diff --git a/src/AppInstallerCLICore/Commands/InstallCommand.cpp b/src/AppInstallerCLICore/Commands/InstallCommand.cpp @@ -40,6 +40,7 @@ namespace AppInstaller::CLI Argument::ForType(Args::Type::InstallLocation), Argument::ForType(Args::Type::HashOverride), Argument::ForType(Args::Type::AcceptPackageAgreements), + Argument::ForType(Args::Type::CustomHeader), }; } diff --git a/src/AppInstallerCLICore/Commands/ListCommand.cpp b/src/AppInstallerCLICore/Commands/ListCommand.cpp @@ -22,6 +22,7 @@ namespace AppInstaller::CLI Argument::ForType(Execution::Args::Type::Command), Argument::ForType(Execution::Args::Type::Count), Argument::ForType(Execution::Args::Type::Exact), + Argument::ForType(Execution::Args::Type::CustomHeader), }; } diff --git a/src/AppInstallerCLICore/Commands/SearchCommand.cpp b/src/AppInstallerCLICore/Commands/SearchCommand.cpp @@ -23,6 +23,7 @@ namespace AppInstaller::CLI Argument::ForType(Execution::Args::Type::Source), Argument::ForType(Execution::Args::Type::Count), Argument::ForType(Execution::Args::Type::Exact), + Argument::ForType(Execution::Args::Type::CustomHeader), }; } diff --git a/src/AppInstallerCLICore/Commands/ShowCommand.cpp b/src/AppInstallerCLICore/Commands/ShowCommand.cpp @@ -7,6 +7,8 @@ #include "Workflows/WorkflowBase.h" #include "Resources.h" +using namespace AppInstaller::CLI::Execution; + namespace AppInstaller::CLI { std::vector<Argument> ShowCommand::GetArguments() const @@ -23,6 +25,7 @@ namespace AppInstaller::CLI Argument::ForType(Execution::Args::Type::Source), Argument::ForType(Execution::Args::Type::Exact), Argument::ForType(Execution::Args::Type::ListVersions), + Argument::ForType(Execution::Args::Type::CustomHeader), }; } diff --git a/src/AppInstallerCLICore/Commands/SourceCommand.cpp b/src/AppInstallerCLICore/Commands/SourceCommand.cpp @@ -52,6 +52,7 @@ namespace AppInstaller::CLI Argument::ForType(Args::Type::SourceName).SetRequired(true), Argument::ForType(Args::Type::SourceArg), Argument::ForType(Args::Type::SourceType), + Argument::ForType(Args::Type::CustomHeader), }; } diff --git a/src/AppInstallerCLICore/Commands/UninstallCommand.cpp b/src/AppInstallerCLICore/Commands/UninstallCommand.cpp @@ -30,6 +30,7 @@ namespace AppInstaller::CLI Argument::ForType(Args::Type::Interactive), Argument::ForType(Args::Type::Silent), Argument::ForType(Args::Type::Log), + Argument::ForType(Args::Type::CustomHeader), }; } diff --git a/src/AppInstallerCLICore/Commands/UpgradeCommand.cpp b/src/AppInstallerCLICore/Commands/UpgradeCommand.cpp @@ -44,6 +44,7 @@ namespace AppInstaller::CLI Argument::ForType(Args::Type::HashOverride), Argument::ForType(Args::Type::AcceptPackageAgreements), Argument{ "all", Argument::NoAlias, Args::Type::All, Resource::String::UpdateAllArgumentDescription, ArgumentType::Flag }, + Argument::ForType(Execution::Args::Type::CustomHeader), }; } diff --git a/src/AppInstallerCLICore/ExecutionArgs.h b/src/AppInstallerCLICore/ExecutionArgs.h @@ -77,6 +77,7 @@ namespace AppInstaller::CLI::Execution Help, // Show command usage Info, // Show general info about WinGet VerboseLogs, // Increases winget logging level to verbose + CustomHeader, // Optional Rest source header // Used for demonstration purposes ExperimentalArg, diff --git a/src/AppInstallerCLICore/Resources.h b/src/AppInstallerCLICore/Resources.h @@ -67,6 +67,9 @@ namespace AppInstaller::CLI::Resource WINGET_DEFINE_RESOURCE_STRINGID(GetManifestResultVersionNotFound); WINGET_DEFINE_RESOURCE_STRINGID(HashCommandLongDescription); WINGET_DEFINE_RESOURCE_STRINGID(HashCommandShortDescription); + WINGET_DEFINE_RESOURCE_STRINGID(HeaderArgumentDescription); + WINGET_DEFINE_RESOURCE_STRINGID(HeaderArgumentNotApplicableForNonRestSourceWarning); + WINGET_DEFINE_RESOURCE_STRINGID(HeaderArgumentNotApplicableWithoutSource); WINGET_DEFINE_RESOURCE_STRINGID(HelpArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(HelpForDetails); WINGET_DEFINE_RESOURCE_STRINGID(HelpLinkPreamble); diff --git a/src/AppInstallerCLICore/Workflows/SourceFlow.cpp b/src/AppInstallerCLICore/Workflows/SourceFlow.cpp @@ -81,19 +81,22 @@ namespace AppInstaller::CLI::Workflow void AddSource(Execution::Context& context) { - std::string name(context.Args.GetArg(Args::Type::SourceName)); - std::string arg(context.Args.GetArg(Args::Type::SourceArg)); - std::string type; + Repository::SourceDetails sourceDetails; + sourceDetails.Name = context.Args.GetArg(Args::Type::SourceName); + sourceDetails.Arg = context.Args.GetArg(Args::Type::SourceArg); + if (context.Args.Contains(Args::Type::SourceType)) { - type = context.Args.GetArg(Args::Type::SourceType); + sourceDetails.Type = context.Args.GetArg(Args::Type::SourceType); } + sourceDetails.CustomHeader = GetCustomHeaderFromArg(context, sourceDetails); + context.Reporter.Info() << Resource::String::SourceAddBegin << std::endl << - " "_liv << name << " -> "_liv << arg << std::endl; + " "_liv << sourceDetails.Name << " -> "_liv << sourceDetails.Arg << std::endl; - if (context.Reporter.ExecuteWithProgress(std::bind(Repository::AddSource, std::move(name), std::move(type), std::move(arg), std::placeholders::_1))) + if (context.Reporter.ExecuteWithProgress(std::bind(Repository::AddSource, sourceDetails, std::placeholders::_1))) { context.Reporter.Info() << Resource::String::Done; } diff --git a/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp b/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp @@ -46,9 +46,23 @@ namespace AppInstaller::CLI::Workflow std::shared_ptr<Repository::ISource> source; try { - auto result = context.Reporter.ExecuteWithProgress(std::bind(Repository::OpenSource, sourceName, std::placeholders::_1), true); - source = result.Source; + OpenSourceResult result; + if (!sourceName.empty()) + { + auto sourceDetails = Repository::GetSource(sourceName); + if (sourceDetails) + { + sourceDetails.value().CustomHeader = GetCustomHeaderFromArg(context, sourceDetails.value()); + + result = context.Reporter.ExecuteWithProgress(std::bind(Repository::OpenSourceFromDetails, sourceDetails.value(), std::placeholders::_1), true); + } + } + else + { + result = context.Reporter.ExecuteWithProgress(std::bind(Repository::OpenSource, sourceName, std::placeholders::_1), true); + } + source = result.Source; // We'll only report the source update failure as warning and continue for (const auto& s : result.SourcesWithUpdateFailure) { @@ -353,7 +367,7 @@ namespace AppInstaller::CLI::Workflow latestVersion->GetProperty(PackageVersionProperty::Version), GetMatchCriteriaDescriptor(searchResult.Matches[i]), sourceIsComposite ? static_cast<std::string>(latestVersion->GetProperty(PackageVersionProperty::SourceName)) : ""s - }); + }); } table.Complete(); @@ -698,6 +712,24 @@ namespace AppInstaller::CLI::Workflow } } + std::optional<std::string> GetCustomHeaderFromArg(Execution::Context& context, const SourceDetails& sourceDetails) + { + std::optional<std::string> customHeader; + if (context.Args.Contains(Execution::Args::Type::CustomHeader)) + { + if (!SupportsCustomHeader(sourceDetails)) + { + context.Reporter.Warn() << Resource::String::HeaderArgumentNotApplicableForNonRestSourceWarning << std::endl; + } + else + { + customHeader = context.Args.GetArg(Execution::Args::Type::CustomHeader); + } + } + + return customHeader; + } + void EnsureFeatureEnabled::operator()(Execution::Context& context) const { if (!Settings::ExperimentalFeature::IsEnabled(m_feature)) diff --git a/src/AppInstallerCLICore/Workflows/WorkflowBase.h b/src/AppInstallerCLICore/Workflows/WorkflowBase.h @@ -295,6 +295,10 @@ namespace AppInstaller::CLI::Workflow // Outputs: None void EnsureRunningAsAdmin(Execution::Context& context); + // Gets the custom header from Arguments. + // Returns: Custom header if provided and applicable. + std::optional<std::string> GetCustomHeaderFromArg(Execution::Context& context, const AppInstaller::Repository::SourceDetails& sourceDetails); + // Ensures that the feature is enabled. // Required Args: the desired feature // Inputs: None diff --git a/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw b/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw @@ -1022,4 +1022,13 @@ Do you agree to the terms?</value> <data name="InstallFlowRegistrationDeferred" xml:space="preserve"> <value>Successfully installed. Restart the application to complete the upgrade.</value> </data> + <data name="HeaderArgumentDescription" xml:space="preserve"> + <value>Optional Windows-Package-Manager REST source HTTP header</value> + </data> + <data name="HeaderArgumentNotApplicableForNonRestSourceWarning" xml:space="preserve"> + <value>Ignoring the optional header as it is not applicable for this source.</value> + </data> + <data name="HeaderArgumentNotApplicableWithoutSource" xml:space="preserve"> + <value>The optional header is not applicable without specifying a Rest source</value> + </data> </root> \ No newline at end of file diff --git a/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj b/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj @@ -186,6 +186,7 @@ <ClCompile Include="Command.cpp" /> <ClCompile Include="Completion.cpp" /> <ClCompile Include="CompositeSource.cpp" /> + <ClCompile Include="CustomHeader.cpp" /> <ClCompile Include="Downloader.cpp" /> <ClCompile Include="ExperimentalFeature.cpp" /> <ClCompile Include="GroupPolicy.cpp" /> diff --git a/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj.filters b/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj.filters @@ -1,490 +1,493 @@ -<?xml version="1.0" encoding="utf-8"?> -<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> - <ItemGroup> - <Filter Include="Source Files"> - <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier> - <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions> - </Filter> - <Filter Include="Header Files"> - <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier> - <Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions> - </Filter> - <Filter Include="Resource Files"> - <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier> - <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions> - </Filter> - <Filter Include="TestData"> - <UniqueIdentifier>{d5cac203-3846-4b39-a1cd-8de9303757b4}</UniqueIdentifier> - </Filter> - <Filter Include="TestData\MultiFileManifestV1"> - <UniqueIdentifier>{69fcd25c-e737-4d28-a6d1-39ce491bf293}</UniqueIdentifier> - </Filter> - </ItemGroup> - <ItemGroup> - <ClInclude Include="pch.h"> - <Filter>Header Files</Filter> - </ClInclude> - <ClInclude Include="TestCommon.h"> - <Filter>Header Files</Filter> - </ClInclude> - <ClInclude Include="TestHooks.h"> - <Filter>Header Files</Filter> - </ClInclude> - <ClInclude Include="TestSource.h"> - <Filter>Header Files</Filter> - </ClInclude> - <ClInclude Include="TestSettings.h"> - <Filter>Header Files</Filter> - </ClInclude> - <ClInclude Include="TestRestRequestHandler.h"> - <Filter>Header Files</Filter> - </ClInclude> - </ItemGroup> - <ItemGroup> - <ClCompile Include="pch.cpp"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="main.cpp"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="SQLiteWrapper.cpp"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="SQLiteIndex.cpp"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="TestCommon.cpp"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="YamlManifest.cpp"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="Downloader.cpp"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="LanguageUtilities.cpp"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="Settings.cpp"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="Sources.cpp"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="WorkFlow.cpp"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="Synchronization.cpp"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="MsixInfo.cpp"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="PreIndexedPackageSource.cpp"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="SQLiteIndexSource.cpp"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="HashCommand.cpp"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="Versions.cpp"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="Strings.cpp"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="Command.cpp"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="UserSettings.cpp"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="ExperimentalFeature.cpp"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="Completion.cpp"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="PredefinedInstalledSource.cpp"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="CompositeSource.cpp"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="TestSource.cpp"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="Registry.cpp"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="NameNormalization.cpp"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="PackageCollection.cpp"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="Regex.cpp"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="ARPChanges.cpp"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="GroupPolicy.cpp"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="WorkflowGroupPolicy.cpp"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="TestSettings.cpp"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="ManifestComparator.cpp"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="RestHelper.cpp"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="JsonHelper.cpp"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="RestClient.cpp"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="RestInterface_1_0.cpp"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="TestRestRequestHandler.cpp"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="HttpClientHelper.cpp"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="SearchRequestSerializer.cpp"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="MsiExecArguments.cpp"> - <Filter>Source Files</Filter> - </ClCompile> - </ItemGroup> - <ItemGroup> - <None Include="PropertySheet.props" /> - <None Include="packages.config" /> - <None Include="Run-TestsInPackage.ps1" /> - </ItemGroup> - <ItemGroup> - <CopyFileToFolders Include="TestData\Manifest-Bad-ArchInvalid.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Bad-ArchMissing.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Bad-DifferentCase-camelCase.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Bad-DifferentCase-lower.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Bad-DifferentCase-UPPER.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Bad-DuplicateKey.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Bad-DuplicateKey-DifferentCase.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Bad-DuplicateKey-DifferentCase-lower.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Bad-IdInvalid.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Bad-IdMissing.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Bad-InstallersMissing.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Bad-InstallerTypeExe-NoSilent.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Bad-InstallerTypeExe-NoSilentRoot.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Bad-InstallerTypeExeRoot-NoSilent.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Bad-InstallerTypeExeRoot-NoSilentRoot.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Bad-InstallerTypeInvalid.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Bad-InstallerTypeMissing.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Bad-InstallerUniqueness.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Bad-InstallerUniqueness-DefaultScope.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Bad-InstallerUniqueness-DefaultValues.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Bad-InstallerUniqueness-SameLang.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Bad-InvalidLocale.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Bad-InvalidManifestVersionValue.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Bad-LicenseMissing.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Bad-NameMissing.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Bad-PublisherMissing.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Bad-Sha256Invalid.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Bad-Sha256Missing.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Bad-SwitchInvalid.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Bad-UnknownProperty.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Bad-UnsupportedVersion.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Bad-UrlInvalid.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Bad-UrlMissing.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Bad-VersionInvalid.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Bad-VersionMissing.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Good-InstallerTypeExeRoot-Silent.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Good-InstallerTypeExeRoot-SilentRoot.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Good-InstallerTypeExe-Silent.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Good-InstallerTypeExe-SilentRoot.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Good-InstallerUniqueness-DefaultLang.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Good-InstallerUniqueness-DiffLangs.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Good-InstallerUniqueness-DiffScope.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Good-Minimum.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Good-Minimum-InstallerType.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Good-MultiLocale.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Good-Switches.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\index.1.0.0.0.msix"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\index.2.0.0.0.msix"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\TestSignedApp.msix"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Good.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\InstallFlowTest_NoApplicableArchitecture.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\InstallFlowTest_NonZeroExitCode.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\InstallFlowTest_Msix_StreamingFlow.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\InstallFlowTest_Msix_DownloadFlow.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\InstallFlowTest_Exe.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\InstallFlowTest_LicenseAgreement.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\InstallFlowTest_MSStore.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\InstallerArgTest_Msi_WithSwitches.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\InstallerArgTest_Msi_NoSwitches.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\InstallerArgTest_Inno_WithSwitches.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\InstallerArgTest_Inno_NoSwitches.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Good-Spaces.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Encoding-ANSI.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Encoding-UTF8.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Encoding-UTF16LE-BOM.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Encoding-UTF8-BOM.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Encoding-UTF16BE.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Encoding-UTF16BE-BOM.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Encoding-UTF16LE.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Good-SystemReferenceComplex.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Bad-PackageFamilyNameOnMSI.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Bad-ProductCodeOnMSIX.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Bad-InvalidUpdateBehavior.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\UpdateFlowTest_Exe.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\UpdateFlowTest_Exe.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\UpdateFlowTest_Exe_2_LicenseAgreement.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\UpdateFlowTest_Msix.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\UpdateFlowTest_Msix_LicenseAgreement.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\ImportFile-Good.json"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\ImportFile-Good-AlreadyInstalled.json"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\ImportFile-Good-WithLicenseAgreement.json"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\ImportFile-Bad-Malformed.json"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\ImportFile-Bad-UnknownPackage.json"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\ImportFile-Bad-UnknownPackageVersion.json"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\ImportFile-Bad-UnknownSource.json"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\ImportFile-Bad-Invalid.json"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\InputNames.txt"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\InputPublishers.txt"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\NormalizationInitialIds.txt"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\ManifestV1-Singleton.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\MultiFileManifestV1\ManifestV1-MultiFile-DefaultLocale.yaml"> - <Filter>TestData\MultiFileManifestV1</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\MultiFileManifestV1\ManifestV1-MultiFile-Installer.yaml"> - <Filter>TestData\MultiFileManifestV1</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\MultiFileManifestV1\ManifestV1-MultiFile-Locale.yaml"> - <Filter>TestData\MultiFileManifestV1</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\MultiFileManifestV1\ManifestV1-MultiFile-Version.yaml"> - <Filter>TestData\MultiFileManifestV1</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Bad-Channel-NotSupported.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\UpdateFlowTest_Exe_2.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\ImportFile-Good-MachineScope.json"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Good-AllDependencyTypes.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\ImportFile-Good-Dependencies.json"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Installer_Exe_Dependencies.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\UpdateFlowTest_ExeDependencies.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Installer_Msix_WFDependency.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Installer_Exe_DependenciesOnRoot.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Installer_Exe_DependenciesMultideclaration.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - </ItemGroup> +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Source Files"> + <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier> + <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions> + </Filter> + <Filter Include="Header Files"> + <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier> + <Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions> + </Filter> + <Filter Include="Resource Files"> + <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier> + <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions> + </Filter> + <Filter Include="TestData"> + <UniqueIdentifier>{d5cac203-3846-4b39-a1cd-8de9303757b4}</UniqueIdentifier> + </Filter> + <Filter Include="TestData\MultiFileManifestV1"> + <UniqueIdentifier>{69fcd25c-e737-4d28-a6d1-39ce491bf293}</UniqueIdentifier> + </Filter> + </ItemGroup> + <ItemGroup> + <ClInclude Include="pch.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="TestCommon.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="TestHooks.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="TestSource.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="TestSettings.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="TestRestRequestHandler.h"> + <Filter>Header Files</Filter> + </ClInclude> + </ItemGroup> + <ItemGroup> + <ClCompile Include="pch.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="main.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="SQLiteWrapper.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="SQLiteIndex.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="TestCommon.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="YamlManifest.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="Downloader.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="LanguageUtilities.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="Settings.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="Sources.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="WorkFlow.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="Synchronization.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="MsixInfo.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="PreIndexedPackageSource.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="SQLiteIndexSource.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="HashCommand.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="Versions.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="Strings.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="Command.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="UserSettings.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="ExperimentalFeature.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="Completion.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="PredefinedInstalledSource.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="CompositeSource.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="TestSource.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="Registry.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="NameNormalization.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="PackageCollection.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="Regex.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="ARPChanges.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="GroupPolicy.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="WorkflowGroupPolicy.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="TestSettings.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="ManifestComparator.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="RestHelper.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="JsonHelper.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="RestClient.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="RestInterface_1_0.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="TestRestRequestHandler.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="HttpClientHelper.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="SearchRequestSerializer.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="CustomHeader.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="MsiExecArguments.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + </ItemGroup> + <ItemGroup> + <None Include="PropertySheet.props" /> + <None Include="packages.config" /> + <None Include="Run-TestsInPackage.ps1" /> + </ItemGroup> + <ItemGroup> + <CopyFileToFolders Include="TestData\Manifest-Bad-ArchInvalid.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-ArchMissing.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-DifferentCase-camelCase.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-DifferentCase-lower.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-DifferentCase-UPPER.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-DuplicateKey.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-DuplicateKey-DifferentCase.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-DuplicateKey-DifferentCase-lower.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-IdInvalid.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-IdMissing.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-InstallersMissing.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-InstallerTypeExe-NoSilent.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-InstallerTypeExe-NoSilentRoot.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-InstallerTypeExeRoot-NoSilent.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-InstallerTypeExeRoot-NoSilentRoot.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-InstallerTypeInvalid.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-InstallerTypeMissing.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-InstallerUniqueness.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-InstallerUniqueness-DefaultScope.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-InstallerUniqueness-DefaultValues.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-InstallerUniqueness-SameLang.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-InvalidLocale.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-InvalidManifestVersionValue.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-LicenseMissing.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-NameMissing.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-PublisherMissing.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-Sha256Invalid.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-Sha256Missing.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-SwitchInvalid.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-UnknownProperty.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-UnsupportedVersion.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-UrlInvalid.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-UrlMissing.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-VersionInvalid.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-VersionMissing.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Good-InstallerTypeExeRoot-Silent.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Good-InstallerTypeExeRoot-SilentRoot.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Good-InstallerTypeExe-Silent.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Good-InstallerTypeExe-SilentRoot.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Good-InstallerUniqueness-DefaultLang.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Good-InstallerUniqueness-DiffLangs.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Good-InstallerUniqueness-DiffScope.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Good-Minimum.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Good-Minimum-InstallerType.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Good-MultiLocale.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Good-Switches.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\index.1.0.0.0.msix"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\index.2.0.0.0.msix"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\TestSignedApp.msix"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Good.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\InstallFlowTest_NoApplicableArchitecture.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\InstallFlowTest_NonZeroExitCode.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\InstallFlowTest_Msix_StreamingFlow.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\InstallFlowTest_Msix_DownloadFlow.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\InstallFlowTest_Exe.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\InstallFlowTest_LicenseAgreement.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\InstallFlowTest_MSStore.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\InstallerArgTest_Msi_WithSwitches.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\InstallerArgTest_Msi_NoSwitches.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\InstallerArgTest_Inno_WithSwitches.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\InstallerArgTest_Inno_NoSwitches.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Good-Spaces.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Encoding-ANSI.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Encoding-UTF8.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Encoding-UTF16LE-BOM.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Encoding-UTF8-BOM.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Encoding-UTF16BE.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Encoding-UTF16BE-BOM.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Encoding-UTF16LE.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Good-SystemReferenceComplex.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-PackageFamilyNameOnMSI.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-ProductCodeOnMSIX.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-InvalidUpdateBehavior.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\UpdateFlowTest_Exe.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\UpdateFlowTest_Exe.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\UpdateFlowTest_Exe_2_LicenseAgreement.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\UpdateFlowTest_Msix.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\UpdateFlowTest_Msix_LicenseAgreement.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\ImportFile-Good.json"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\ImportFile-Good-AlreadyInstalled.json"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\ImportFile-Good-WithLicenseAgreement.json"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\ImportFile-Bad-Malformed.json"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\ImportFile-Bad-UnknownPackage.json"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\ImportFile-Bad-UnknownPackageVersion.json"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\ImportFile-Bad-UnknownSource.json"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\ImportFile-Bad-Invalid.json"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\InputNames.txt"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\InputPublishers.txt"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\NormalizationInitialIds.txt"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\ManifestV1-Singleton.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\MultiFileManifestV1\ManifestV1-MultiFile-DefaultLocale.yaml"> + <Filter>TestData\MultiFileManifestV1</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\MultiFileManifestV1\ManifestV1-MultiFile-Installer.yaml"> + <Filter>TestData\MultiFileManifestV1</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\MultiFileManifestV1\ManifestV1-MultiFile-Locale.yaml"> + <Filter>TestData\MultiFileManifestV1</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\MultiFileManifestV1\ManifestV1-MultiFile-Version.yaml"> + <Filter>TestData\MultiFileManifestV1</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-Channel-NotSupported.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\UpdateFlowTest_Exe_2.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\ImportFile-Good-MachineScope.json"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Good-AllDependencyTypes.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\ImportFile-Good-Dependencies.json"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Installer_Exe_Dependencies.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\UpdateFlowTest_ExeDependencies.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Installer_Msix_WFDependency.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Installer_Exe_DependenciesOnRoot.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Installer_Exe_DependenciesMultideclaration.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + </ItemGroup> </Project> \ No newline at end of file diff --git a/src/AppInstallerCLITests/CustomHeader.cpp b/src/AppInstallerCLITests/CustomHeader.cpp @@ -0,0 +1,163 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#include "pch.h" +#include "TestCommon.h" +#include "TestHooks.h" +#include "TestSettings.h" +#include "TestSource.h" +#include "TestRestRequestHandler.h" +#include <Rest/Schema/1_0/Interface.h> +#include <Rest/Schema/JsonHelper.h> +#include <Rest/RestClient.h> +#include <winget/Settings.h> + +using namespace TestCommon; +using namespace AppInstaller; +using namespace AppInstaller::Settings; +using namespace AppInstaller::Repository; +using namespace AppInstaller::Repository::Rest; +using namespace AppInstaller::Repository::Rest::Schema; +using namespace AppInstaller::Repository::Rest::Schema::V1_0; + +namespace +{ + utility::string_t CustomHeaderName = L"Windows-Package-Manager"; + + constexpr std::string_view s_EmptySources = R"( + Sources: + )"sv; + + utility::string_t sampleSearchResponse = _XPLATSTR( + R"delimiter({ + "Data" : [ + { + "PackageIdentifier": "git.package", + "PackageName": "package", + "Publisher": "git", + "Versions": [ + { "PackageVersion": "1.0.0" }] + }] + })delimiter"); + + std::shared_ptr<TestRestRequestHandler> GetCustomHeaderVerificationHandler( + const web::http::status_code statusCode, const utility::string_t& sampleResponseString, const std::pair<utility::string_t, utility::string_t>& customHeader) + { + return std::make_shared<TestRestRequestHandler>([statusCode, sampleResponseString, customHeader](web::http::http_request request) -> + pplx::task<web::http::http_response> + { + web::http::http_response response; + auto& headers = request.headers(); + if (!headers.has(customHeader.first) || + (utility::conversions::to_utf8string(customHeader.second).compare(utility::conversions::to_utf8string(headers[customHeader.first]))) != 0) + { + response.set_status_code(web::http::status_codes::BadRequest); + return pplx::task_from_result(response); + } + + if (!sampleResponseString.empty()) + { + response.set_body(web::json::value::parse(sampleResponseString)); + } + + response.headers().set_content_type(web::http::details::mime_types::application_json); + response.set_status_code(statusCode); + return pplx::task_from_result(response); + }); + } +} + +TEST_CASE("RestClient_CustomHeader", "[RestSource][CustomHeader]") +{ + utility::string_t sample = _XPLATSTR( + R"delimiter({ + "Data" : { + "SourceIdentifier": "Source123", + "ServerSupportedVersions": [ + "1.0.0", + "2.0.0"] + }})delimiter"); + + std::optional<std::string> customHeader = "Testing custom header"; + auto header = std::make_pair<>(CustomHeaderName, JsonHelper::GetUtilityString(customHeader.value())); + HttpClientHelper helper{ GetCustomHeaderVerificationHandler(web::http::status_codes::OK, sample, header) }; + RestClient client = RestClient::Create(utility::conversions::to_utf8string("https://restsource.com/api"), customHeader, std::move(helper)); + REQUIRE(client.GetSourceIdentifier() == "Source123"); +} + +TEST_CASE("AddSource_CustomHeader", "[RestSource][CustomHeader]") +{ + SetSetting(Streams::UserSources, s_EmptySources); + TestHook_ClearSourceFactoryOverrides(); + + std::string customHeader = "Testing custom header with open source"; + + SourceDetails details; + details.Name = "restsource"; + details.Type = "thisIsTheType"; + details.Arg = "thisIsTheArg"; + details.Data = "thisIsTheData"; + details.CustomHeader = customHeader; + + bool receivedCustomHeader = false; + TestSourceFactory factory{ [&](const SourceDetails& sd) { return std::shared_ptr<ISource>(new TestSource(sd)); } }; + factory.OnAdd = [&](SourceDetails& sd) { receivedCustomHeader = customHeader.compare(sd.CustomHeader.value()) == 0; }; + TestHook_SetSourceFactoryOverride(details.Type, factory); + + TestProgress progress; + AddSource(details, progress); + REQUIRE(receivedCustomHeader); +} + +TEST_CASE("CreateSource_CustomHeader", "[RestSource][CustomHeader]") +{ + SetSetting(Streams::UserSources, s_EmptySources); + TestHook_ClearSourceFactoryOverrides(); + + std::string customHeader = "Testing custom header with open source"; + + SourceDetails details; + details.Name = "restsource"; + details.Type = "Microsoft.Rest"; + details.Arg = "thisIsTheArg"; + details.Data = "thisIsTheData"; + details.CustomHeader = customHeader; + + bool receivedCustomHeader = false; + TestSourceFactory factory{ [&](const SourceDetails& sd) { return std::shared_ptr<ISource>(new TestSource(sd)); } }; + factory.OnAdd = [&](SourceDetails& sd) { receivedCustomHeader = customHeader.compare(sd.CustomHeader.value()) == 0; }; + TestHook_SetSourceFactoryOverride(details.Type, factory); + + TestProgress progress; + AddSource(details, progress); + + details.CustomHeader = "Custom Header"; + auto source = OpenSourceFromDetails(details, progress).Source; + REQUIRE(details.CustomHeader.value().compare(source.get()->GetDetails().CustomHeader.value_or("")) == 0); +} + +TEST_CASE("CreateSource_CustomHeaderNotApplicable", "[RestSource][CustomHeader]") +{ + SetSetting(Streams::UserSources, s_EmptySources); + TestHook_ClearSourceFactoryOverrides(); + + std::string customHeader = "Testing custom header with open source"; + + SourceDetails details; + details.Name = "restsource"; + details.Type = "thisIsTheType"; + details.Arg = "thisIsTheArg"; + details.Data = "thisIsTheData"; + details.CustomHeader = customHeader; + + bool receivedCustomHeader = false; + TestSourceFactory factory{ [&](const SourceDetails& sd) { return std::shared_ptr<ISource>(new TestSource(sd)); } }; + factory.OnAdd = [&](SourceDetails& sd) { receivedCustomHeader = customHeader.compare(sd.CustomHeader.value()) == 0; }; + TestHook_SetSourceFactoryOverride(details.Type, factory); + + TestProgress progress; + AddSource(details, progress); + + details.CustomHeader = {}; + auto source = OpenSourceFromDetails(details, progress).Source; + REQUIRE(!source.get()->GetDetails().CustomHeader.has_value()); +} diff --git a/src/AppInstallerCLITests/PreIndexedPackageSource.cpp b/src/AppInstallerCLITests/PreIndexedPackageSource.cpp @@ -69,12 +69,13 @@ TEST_CASE("PIPS_Add", "[pips]") TestDataFile index(s_MsixFile_1); CopyIndexFileToDirectory(index, dir); - std::string name = "TestName"; - std::string type(AppInstaller::Repository::Microsoft::PreIndexedPackageSourceFactory::Type()); - std::string arg = dir; + SourceDetails details; + details.Name = "TestName"; + details.Type = AppInstaller::Repository::Microsoft::PreIndexedPackageSourceFactory::Type(); + details.Arg = dir; ProgressCallback callback; - AddSource(name, type, arg, callback); + AddSource(details, callback); fs::path state = GetPathToFileDir(); REQUIRE(fs::exists(state)); @@ -98,12 +99,13 @@ TEST_CASE("PIPS_UpdateSameVersion", "[pips]") TestDataFile index(s_MsixFile_1); CopyIndexFileToDirectory(index, dir); - std::string name = "TestName"; - std::string type(AppInstaller::Repository::Microsoft::PreIndexedPackageSourceFactory::Type()); - std::string arg = dir; + SourceDetails details; + details.Name = "TestName"; + details.Type = AppInstaller::Repository::Microsoft::PreIndexedPackageSourceFactory::Type(); + details.Arg = dir; TestProgress callback; - AddSource(name, type, arg, callback); + AddSource(details, callback); fs::path state = GetPathToFileDir(); REQUIRE(fs::exists(state)); @@ -111,7 +113,7 @@ TEST_CASE("PIPS_UpdateSameVersion", "[pips]") bool progressCalled = false; callback.m_OnProgress = [&](uint64_t, uint64_t, ProgressType) { progressCalled = true; }; - UpdateSource(name, callback); + UpdateSource(details.Name, callback); REQUIRE(!progressCalled); } @@ -123,12 +125,13 @@ TEST_CASE("PIPS_UpdateNewVersion", "[pips]") TestDataFile indexMsix1(s_MsixFile_1); CopyIndexFileToDirectory(indexMsix1, dir); - std::string name = "TestName"; - std::string type(AppInstaller::Repository::Microsoft::PreIndexedPackageSourceFactory::Type()); - std::string arg = dir; + SourceDetails details; + details.Name = "TestName"; + details.Type = AppInstaller::Repository::Microsoft::PreIndexedPackageSourceFactory::Type(); + details.Arg = dir; TestProgress callback; - AddSource(name, type, arg, callback); + AddSource(details, callback); fs::path state = GetPathToFileDir(); REQUIRE(fs::exists(state)); @@ -147,7 +150,7 @@ TEST_CASE("PIPS_UpdateNewVersion", "[pips]") bool progressCalled = false; callback.m_OnProgress = [&](uint64_t, uint64_t, ProgressType) { progressCalled = true; }; - UpdateSource(name, callback); + UpdateSource(details.Name, callback); REQUIRE(progressCalled); std::string manifestContents2 = GetContents(manifestPath); @@ -165,12 +168,13 @@ TEST_CASE("PIPS_Remove", "[pips]") TestDataFile index(s_MsixFile_1); CopyIndexFileToDirectory(index, dir); - std::string name = "TestName"; - std::string type(AppInstaller::Repository::Microsoft::PreIndexedPackageSourceFactory::Type()); - std::string arg = dir; + SourceDetails details; + details.Name = "TestName"; + details.Type = AppInstaller::Repository::Microsoft::PreIndexedPackageSourceFactory::Type(); + details.Arg = dir; ProgressCallback callback; - AddSource(name, type, arg, callback); + AddSource(details, callback); fs::path state = GetPathToFileDir(); REQUIRE(fs::exists(state)); @@ -183,6 +187,6 @@ TEST_CASE("PIPS_Remove", "[pips]") indexFile /= s_IndexFileName; REQUIRE(fs::exists(indexFile)); - RemoveSource(name, callback); + RemoveSource(details.Name, callback); REQUIRE(!fs::exists(state)); } diff --git a/src/AppInstallerCLITests/RestClient.cpp b/src/AppInstallerCLITests/RestClient.cpp @@ -38,10 +38,10 @@ TEST_CASE("GetLatestCommonVersion_UnsupportedVersion", "[RestSource]") TEST_CASE("GetSupportedInterface", "[RestSource]") { Version version{ "1.0.0" }; - REQUIRE(RestClient::GetSupportedInterface(utility::conversions::to_utf8string(TestRestUri), version)->GetVersion() == version); + REQUIRE(RestClient::GetSupportedInterface(utility::conversions::to_utf8string(TestRestUri), {}, version)->GetVersion() == version); Version invalid{ "1.2.0" }; - REQUIRE_THROWS(RestClient::GetSupportedInterface(utility::conversions::to_utf8string(TestRestUri), invalid)); + REQUIRE_THROWS(RestClient::GetSupportedInterface(utility::conversions::to_utf8string(TestRestUri), {}, invalid)); } TEST_CASE("GetInformation_Success", "[RestSource]") @@ -56,7 +56,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, {}, std::move(helper)); REQUIRE(information.SourceIdentifier == "Source123"); REQUIRE(information.ServerSupportedVersions.size() == 2); REQUIRE(information.ServerSupportedVersions.at(0) == "0.2.0"); @@ -75,7 +75,7 @@ TEST_CASE("RestClientCreate_UnexpectedVersion", "[RestSource]") }})delimiter"); HttpClientHelper helper{ GetTestRestRequestHandler(web::http::status_codes::OK, sample) }; - REQUIRE_THROWS_HR(RestClient::Create("https://restsource.com/api", std::move(helper)), + REQUIRE_THROWS_HR(RestClient::Create("https://restsource.com/api", {}, std::move(helper)), APPINSTALLER_CLI_ERROR_UNSUPPORTED_RESTSOURCE); } @@ -91,6 +91,6 @@ TEST_CASE("RestClientCreate_Success", "[RestSource]") }})delimiter"); HttpClientHelper helper{ GetTestRestRequestHandler(web::http::status_codes::OK, sample) }; - RestClient client = RestClient::Create(utility::conversions::to_utf8string(TestRestUri), std::move(helper)); + RestClient client = RestClient::Create(utility::conversions::to_utf8string(TestRestUri), {}, std::move(helper)); REQUIRE(client.GetSourceIdentifier() == "Source123"); } diff --git a/src/AppInstallerCLITests/Sources.cpp b/src/AppInstallerCLITests/Sources.cpp @@ -243,28 +243,29 @@ TEST_CASE("RepoSources_AddSource", "[sources]") SetSetting(Streams::UserSources, s_EmptySources); TestHook_ClearSourceFactoryOverrides(); - std::string name = "thisIsTheName"; - std::string type = "thisIsTheType"; - std::string arg = "thisIsTheArg"; - std::string data = "thisIsTheData"; + SourceDetails details; + details.Name = "thisIsTheName"; + details.Type = "thisIsTheType"; + details.Arg = "thisIsTheArg"; + details.Data = "thisIsTheData"; bool addCalledOnFactory = false; TestSourceFactory factory{ SourcesTestSource::Create }; - factory.OnAdd = [&](SourceDetails& sd) { addCalledOnFactory = true; sd.Data = data; }; - TestHook_SetSourceFactoryOverride(type, factory); + factory.OnAdd = [&](SourceDetails& sd) { addCalledOnFactory = true; sd.Data = details.Data; }; + TestHook_SetSourceFactoryOverride(details.Type, factory); ProgressCallback progress; - AddSource(name, type, arg, progress); + AddSource(details, progress); REQUIRE(addCalledOnFactory); std::vector<SourceDetails> sources = GetSources(); REQUIRE(sources.size() == 2); - REQUIRE(sources[0].Name == name); - REQUIRE(sources[0].Type == type); - REQUIRE(sources[0].Arg == arg); - REQUIRE(sources[0].Data == data); + REQUIRE(sources[0].Name == details.Name); + REQUIRE(sources[0].Type == details.Type); + REQUIRE(sources[0].Arg == details.Arg); + REQUIRE(sources[0].Data == details.Data); REQUIRE(sources[0].LastUpdateTime != ConvertUnixEpochToSystemClock(0)); REQUIRE(sources[0].Origin == SourceOrigin::User); @@ -275,37 +276,43 @@ TEST_CASE("RepoSources_AddMultipleSources", "[sources]") { SetSetting(Streams::UserSources, s_EmptySources); - std::string name = "thisIsTheName"; - std::string type = "thisIsTheType"; - std::string arg = "thisIsTheArg"; - std::string data = "thisIsTheData"; + SourceDetails details; + details.Name = "thisIsTheName"; + details.Type = "thisIsTheType"; + details.Arg = "thisIsTheArg"; + details.Data = "thisIsTheData"; const char* suffix[2] = { "", "2" }; TestSourceFactory factory1{ SourcesTestSource::Create }; - factory1.OnAdd = [&](SourceDetails& sd) { sd.Data = data; }; - TestHook_SetSourceFactoryOverride(type, factory1); + factory1.OnAdd = [&](SourceDetails& sd) { sd.Data = details.Data; }; + TestHook_SetSourceFactoryOverride(details.Type, factory1); ProgressCallback progress; - AddSource(name, type, arg, progress); + AddSource(details, progress); std::vector<SourceDetails> sources = GetSources(); REQUIRE(sources.size() == 2); - REQUIRE(sources[0].Name == name); - REQUIRE(sources[0].Type == type); - REQUIRE(sources[0].Arg == arg); - REQUIRE(sources[0].Data == data); + REQUIRE(sources[0].Name == details.Name); + REQUIRE(sources[0].Type == details.Type); + REQUIRE(sources[0].Arg == details.Arg); + REQUIRE(sources[0].Data == details.Data); REQUIRE(sources[0].LastUpdateTime != ConvertUnixEpochToSystemClock(0)); REQUIRE(sources[0].Origin == SourceOrigin::User); REQUIRE(sources[1].Origin == SourceOrigin::Default); + SourceDetails details2; + details2.Name = details.Name + suffix[1]; + details2.Type = details.Type + suffix[1]; + details2.Arg = details.Arg + suffix[1]; + details2.Data = details.Data + suffix[1]; TestSourceFactory factory2{ SourcesTestSource::Create }; - factory2.OnAdd = [&](SourceDetails& sd) { sd.Data = data + suffix[1]; }; - TestHook_SetSourceFactoryOverride(type + suffix[1], factory2); + factory2.OnAdd = [&](SourceDetails& sd) { sd.Data = details2.Data; }; + TestHook_SetSourceFactoryOverride(details2.Type, factory2); - AddSource(name + suffix[1], type + suffix[1], arg + suffix[1], progress); + AddSource(details2, progress); sources = GetSources(); REQUIRE(sources.size() == 3); @@ -313,10 +320,10 @@ TEST_CASE("RepoSources_AddMultipleSources", "[sources]") for (size_t i = 0; i < 2; ++i) { INFO("Source #" << i); - REQUIRE(sources[i].Name == name + suffix[i]); - REQUIRE(sources[i].Type == type + suffix[i]); - REQUIRE(sources[i].Arg == arg + suffix[i]); - REQUIRE(sources[i].Data == data + suffix[i]); + REQUIRE(sources[i].Name == details.Name + suffix[i]); + REQUIRE(sources[i].Type == details.Type + suffix[i]); + REQUIRE(sources[i].Arg == details.Arg + suffix[i]); + REQUIRE(sources[i].Data == details.Data + suffix[i]); REQUIRE(sources[i].LastUpdateTime != ConvertUnixEpochToSystemClock(0)); REQUIRE(sources[i].Origin == SourceOrigin::User); } @@ -331,28 +338,29 @@ TEST_CASE("RepoSources_UpdateSource", "[sources]") SetSetting(Streams::UserSources, s_EmptySources); TestHook_ClearSourceFactoryOverrides(); - std::string name = "thisIsTheName"; - std::string type = "thisIsTheType"; - std::string arg = "thisIsTheArg"; - std::string data = "thisIsTheData"; + SourceDetails details; + details.Name = "thisIsTheName"; + details.Type = "thisIsTheType"; + details.Arg = "thisIsTheArg"; + details.Data = "thisIsTheData"; bool addCalledOnFactory = false; TestSourceFactory factory{ SourcesTestSource::Create }; - factory.OnAdd = [&](SourceDetails& sd) { addCalledOnFactory = true; sd.Data = data; }; - TestHook_SetSourceFactoryOverride(type, factory); + factory.OnAdd = [&](SourceDetails& sd) { addCalledOnFactory = true; sd.Data = details.Data; }; + TestHook_SetSourceFactoryOverride(details.Type, factory); ProgressCallback progress; - AddSource(name, type, arg, progress); + AddSource(details, progress); REQUIRE(addCalledOnFactory); std::vector<SourceDetails> sources = GetSources(); REQUIRE(sources.size() == 2); - REQUIRE(sources[0].Name == name); - REQUIRE(sources[0].Type == type); - REQUIRE(sources[0].Arg == arg); - REQUIRE(sources[0].Data == data); + REQUIRE(sources[0].Name == details.Name); + REQUIRE(sources[0].Type == details.Type); + REQUIRE(sources[0].Arg == details.Arg); + REQUIRE(sources[0].Data == details.Data); REQUIRE(sources[0].LastUpdateTime != ConvertUnixEpochToSystemClock(0)); REQUIRE(sources[0].Origin == SourceOrigin::User); @@ -363,17 +371,17 @@ TEST_CASE("RepoSources_UpdateSource", "[sources]") auto now = std::chrono::system_clock::now(); factory.OnUpdate = [&](const SourceDetails&) { updateCalledOnFactory = true; }; - UpdateSource(name, progress); + UpdateSource(details.Name, progress); REQUIRE(updateCalledOnFactory); sources = GetSources(); REQUIRE(sources.size() == 2); - REQUIRE(sources[0].Name == name); - REQUIRE(sources[0].Type == type); - REQUIRE(sources[0].Arg == arg); - REQUIRE(sources[0].Data == data); + REQUIRE(sources[0].Name == details.Name); + REQUIRE(sources[0].Type == details.Type); + REQUIRE(sources[0].Arg == details.Arg); + REQUIRE(sources[0].Data == details.Data); REQUIRE((now - sources[0].LastUpdateTime) < 1s); } @@ -384,17 +392,18 @@ TEST_CASE("RepoSources_UpdateSourceRetries", "[sources]") SetSetting(Streams::UserSources, s_EmptySources); TestHook_ClearSourceFactoryOverrides(); - std::string name = "thisIsTheName"; - std::string type = "thisIsTheType"; - std::string arg = "thisIsTheArg"; - std::string data = "thisIsTheData"; + SourceDetails details; + details.Name = "thisIsTheName"; + details.Type = "thisIsTheType"; + details.Arg = "thisIsTheArg"; + details.Data = "thisIsTheData"; TestSourceFactory factory{ SourcesTestSource::Create }; - factory.OnAdd = [&](SourceDetails& sd) { sd.Data = data; }; - TestHook_SetSourceFactoryOverride(type, factory); + factory.OnAdd = [&](SourceDetails& sd) { sd.Data = details.Data; }; + TestHook_SetSourceFactoryOverride(details.Type, factory); ProgressCallback progress; - AddSource(name, type, arg, progress); + AddSource(details, progress); // Reset for a call to update bool updateShouldThrow = false; @@ -409,7 +418,7 @@ TEST_CASE("RepoSources_UpdateSourceRetries", "[sources]") updateCalledOnFactoryAgain = true; }; - UpdateSource(name, progress); + UpdateSource(details.Name, progress); REQUIRE(updateCalledOnFactoryAgain); } @@ -419,23 +428,24 @@ TEST_CASE("RepoSources_RemoveSource", "[sources]") SetSetting(Streams::UserSources, s_EmptySources); TestHook_ClearSourceFactoryOverrides(); - std::string name = "thisIsTheName"; - std::string type = "thisIsTheType"; - std::string arg = "thisIsTheArg"; - std::string data = "thisIsTheData"; + SourceDetails details; + details.Name = "thisIsTheName"; + details.Type = "thisIsTheType"; + details.Arg = "thisIsTheArg"; + details.Data = "thisIsTheData"; bool removeCalledOnFactory = false; TestSourceFactory factory{ SourcesTestSource::Create }; factory.OnRemove = [&](const SourceDetails&) { removeCalledOnFactory = true; }; - TestHook_SetSourceFactoryOverride(type, factory); + TestHook_SetSourceFactoryOverride(details.Type, factory); ProgressCallback progress; - AddSource(name, type, arg, progress); + AddSource(details, progress); std::vector<SourceDetails> sources = GetSources(); REQUIRE(sources.size() == 2); - RemoveSource(name, progress); + RemoveSource(details.Name, progress); REQUIRE(removeCalledOnFactory); @@ -596,8 +606,12 @@ TEST_CASE("RepoSources_GroupPolicy_DefaultSource", "[sources][groupPolicy]") SetSetting(Streams::UserSources, s_EmptySources); ProgressCallback progress; + SourceDetails details; + details.Name = "winget"; + details.Type = "Microsoft.PreIndexed.Package"; + details.Arg = "https://winget.azureedge.net/cache"; REQUIRE_POLICY_EXCEPTION( - AddSource("winget", "Microsoft.PreIndexed.Package", "https://winget.azureedge.net/cache", progress), + AddSource(details, progress), TogglePolicy::Policy::DefaultSource); } SECTION("Ignore default source from user") @@ -615,28 +629,29 @@ TEST_CASE("RepoSources_GroupPolicy_DefaultSource", "[sources][groupPolicy]") SetSetting(Streams::UserSources, s_EmptySources); TestHook_ClearSourceFactoryOverrides(); - std::string name = "winget"; - std::string type = "someType"; - std::string arg = "notWingetRealArg"; - std::string data = "someData"; + SourceDetails details; + details.Name = "winget"; + details.Type = "someType"; + details.Arg = "notWingetRealArg"; + details.Data = "someData"; bool addCalledOnFactory = false; TestSourceFactory factory{ SourcesTestSource::Create }; - factory.OnAdd = [&](SourceDetails& sd) { addCalledOnFactory = true; sd.Data = data; }; - TestHook_SetSourceFactoryOverride(type, factory); + factory.OnAdd = [&](SourceDetails& sd) { addCalledOnFactory = true; sd.Data = details.Data; }; + TestHook_SetSourceFactoryOverride(details.Type, factory); ProgressCallback progress; - AddSource(name, type, arg, progress); + AddSource(details, progress); REQUIRE(addCalledOnFactory); auto sources = GetSources(); REQUIRE(sources.size() == 1); - REQUIRE(sources[0].Name == name); - REQUIRE(sources[0].Type == type); - REQUIRE(sources[0].Arg == arg); - REQUIRE(sources[0].Data == data); + REQUIRE(sources[0].Name == details.Name); + REQUIRE(sources[0].Type == details.Type); + REQUIRE(sources[0].Arg == details.Arg); + REQUIRE(sources[0].Data == details.Data); REQUIRE(sources[0].Origin == SourceOrigin::User); } SECTION("Allow same name source from user") @@ -841,7 +856,11 @@ TEST_CASE("RepoSources_GroupPolicy_AllowedSources", "[sources][groupPolicy]") TestHook_SetSourceFactoryOverride(policySource.Type, factory); ProgressCallback progress; - AddSource(policySource.Name, policySource.Type, policySource.Arg, progress); + SourceDetails details; + details.Name = policySource.Name; + details.Type = policySource.Type; + details.Arg = policySource.Arg; + AddSource(details, progress); REQUIRE(addCalledOnFactory); @@ -875,8 +894,12 @@ TEST_CASE("RepoSources_GroupPolicy_AllowedSources", "[sources][groupPolicy]") factory.OnAdd = [&](SourceDetails&) { addCalledOnFactory = true; }; ProgressCallback progress; + SourceDetails details; + details.Name = "notAllowed"; + details.Type = "type"; + details.Arg = "arg"; REQUIRE_POLICY_EXCEPTION( - AddSource("notAllowed", "type", "arg", progress), + AddSource(details, progress), TogglePolicy::Policy::AllowedSources); REQUIRE_FALSE(addCalledOnFactory); } @@ -896,8 +919,12 @@ TEST_CASE("RepoSources_GroupPolicy_AllowedSources", "[sources][groupPolicy]") factory.OnAdd = [&](SourceDetails&) { addCalledOnFactory = true; }; ProgressCallback progress; + SourceDetails details; + details.Name = "name"; + details.Type = "type"; + details.Arg = "arg"; REQUIRE_POLICY_EXCEPTION( - AddSource("name", "type", "arg", progress), + AddSource(details, progress), TogglePolicy::Policy::AllowedSources); REQUIRE_FALSE(addCalledOnFactory); diff --git a/src/AppInstallerCLITests/TestSource.h b/src/AppInstallerCLITests/TestSource.h @@ -84,6 +84,9 @@ namespace TestCommon AppInstaller::Repository::SourceDetails Details = { "TestSource", "Microsoft.TestSource", "//arg", "", "*TestSource" }; std::function<AppInstaller::Repository::SearchResult(const AppInstaller::Repository::SearchRequest& request)> SearchFunction; bool Composite = false; + + TestSource() = default; + TestSource(const AppInstaller::Repository::SourceDetails& details) : Details(details) {} }; // An ISourceFactory implementation for use across the test code. diff --git a/src/AppInstallerCLITests/WorkFlow.cpp b/src/AppInstallerCLITests/WorkFlow.cpp @@ -3,6 +3,8 @@ #include "pch.h" #include "TestCommon.h" #include "TestSource.h" +#include "TestHooks.h" +#include "TestSettings.h" #include <AppInstallerErrors.h> #include <AppInstallerLogging.h> #include <AppInstallerDownloader.h> @@ -22,6 +24,7 @@ #include <Commands/ImportCommand.h> #include <Commands/InstallCommand.h> #include <Commands/ShowCommand.h> +#include <Commands/SearchCommand.h> #include <Commands/UninstallCommand.h> #include <Commands/UpgradeCommand.h> #include <winget/LocIndependent.h> @@ -29,6 +32,7 @@ #include <Resources.h> #include <AppInstallerFileLogger.h> #include <Commands/ValidateCommand.h> +#include <winget/Settings.h> using namespace winrt::Windows::Foundation; using namespace winrt::Windows::Management::Deployment; @@ -41,6 +45,7 @@ using namespace AppInstaller::Manifest; using namespace AppInstaller::Repository; using namespace AppInstaller::Settings; using namespace AppInstaller::Utility; +using namespace AppInstaller::Settings; #define REQUIRE_TERMINATED_WITH(_context_,_hr_) \ @@ -2030,4 +2035,37 @@ TEST_CASE("InstallerWithoutDependencies_RootDependenciesAreUsed", "[dependencies // Verify root dependencies are shown REQUIRE(installOutput.str().find(Resource::LocString(Resource::String::InstallAndUpgradeCommandsReportDependencies).get()) != std::string::npos); REQUIRE(installOutput.str().find("PreviewIISOnRoot") != std::string::npos); -}- \ No newline at end of file +} + +TEST_CASE("OpenSource_WithCustomHeader", "[OpenSource][CustomHeader]") +{ + SetSetting(Streams::UserSources, R"(Sources:)"sv); + TestHook_ClearSourceFactoryOverrides(); + + SourceDetails details; + details.Name = "restsource"; + details.Type = "Microsoft.Rest"; + details.Arg = "thisIsTheArg"; + details.Data = "thisIsTheData"; + details.CustomHeader = "CustomHeader"; + + bool receivedCustomHeader = false; + TestSourceFactory factory { [&](const SourceDetails& sd) { return std::shared_ptr<ISource>(new TestSource(sd)); } }; + factory.OnAdd = [&](SourceDetails& sd) { receivedCustomHeader = details.CustomHeader.value().compare(sd.CustomHeader.value()) == 0; }; + TestHook_SetSourceFactoryOverride(details.Type, factory); + + TestProgress progress; + AddSource(details, progress); + + std::ostringstream output; + TestContext context{ output, std::cin }; + context.Args.AddArg(Execution::Args::Type::Query, "TestQuery"sv); + + std::string customHeader2 = "Test custom header in Open source Flow"; + context.Args.AddArg(Execution::Args::Type::CustomHeader, customHeader2); + context.Args.AddArg(Execution::Args::Type::Source, details.Name); + + OpenSource(context); + auto source = context.Get<Execution::Data::Source>(); + REQUIRE(source.get()->GetDetails().CustomHeader.value_or("").compare(customHeader2) == 0); +} diff --git a/src/AppInstallerRepositoryCore/Public/AppInstallerRepositorySource.h b/src/AppInstallerRepositoryCore/Public/AppInstallerRepositorySource.h @@ -65,6 +65,9 @@ namespace AppInstaller::Repository // Whether the source behavior has restrictions bool Restricted = false; + + // Custom header for Rest sources + std::optional<std::string> CustomHeader; }; // Interface for interacting with a source from outside of the repository lib. @@ -108,7 +111,7 @@ namespace AppInstaller::Repository std::optional<SourceDetails> GetSource(std::string_view name); // Adds a new source for the user. - bool AddSource(std::string_view name, std::string_view type, std::string_view arg, IProgressCallback& progress); + bool AddSource(SourceDetails& sourceDetails, IProgressCallback& progress); struct OpenSourceResult { @@ -191,4 +194,7 @@ namespace AppInstaller::Repository // Return value indicates whether the named source was found. // Passing an empty string drops all sources. bool DropSource(std::string_view name); + + // Checks if a source supports passing custom header. + bool SupportsCustomHeader(const SourceDetails& sourceDetails); } diff --git a/src/AppInstallerRepositoryCore/RepositorySource.cpp b/src/AppInstallerRepositoryCore/RepositorySource.cpp @@ -955,16 +955,16 @@ namespace AppInstaller::Repository } } - bool AddSource(std::string_view name, std::string_view type, std::string_view arg, IProgressCallback& progress) + bool AddSource(SourceDetails& sourceDetails, IProgressCallback& progress) { - THROW_HR_IF(E_INVALIDARG, name.empty()); + THROW_HR_IF(E_INVALIDARG, sourceDetails.Name.empty()); - AICLI_LOG(Repo, Info, << "Adding source: Name[" << name << "], Type[" << type << "], Arg[" << arg << "]"); + AICLI_LOG(Repo, Info, << "Adding source: Name[" << sourceDetails.Name << "], Type[" << sourceDetails.Type << "], Arg[" << sourceDetails.Arg << "]"); // Check all sources for the given name. SourceListInternal sourceList; - auto source = sourceList.GetCurrentSource(name); + auto source = sourceList.GetCurrentSource(sourceDetails.Name); THROW_HR_IF(APPINSTALLER_CLI_ERROR_SOURCE_NAME_ALREADY_EXISTS, source != nullptr); // Check for a non-user tombstone; hidden source data that we don't want to collide. @@ -973,26 +973,22 @@ namespace AppInstaller::Repository THROW_HR_IF(APPINSTALLER_CLI_ERROR_SOURCE_NAME_ALREADY_EXISTS, tombstoneSource && tombstoneSource->Origin != SourceOrigin::User); // Check sources allowed by group policy - auto blockingPolicy = GetPolicyBlockingUserSource(name, type, arg, false); + auto blockingPolicy = GetPolicyBlockingUserSource(sourceDetails.Name, sourceDetails.Type, sourceDetails.Arg, false); if (blockingPolicy != TogglePolicy::Policy::None) { throw GroupPolicyException(blockingPolicy); } - SourceDetailsInternal details; - details.Name = name; - details.Type = type; - details.Arg = arg; - details.LastUpdateTime = Utility::ConvertUnixEpochToSystemClock(0); - details.Origin = SourceOrigin::User; + sourceDetails.LastUpdateTime = Utility::ConvertUnixEpochToSystemClock(0); + sourceDetails.Origin = SourceOrigin::User; - bool result = AddSourceFromDetails(details, progress); + bool result = AddSourceFromDetails(sourceDetails, progress); if (result) { - AICLI_LOG(Repo, Info, << "Source created with extra data: " << details.Data); - AICLI_LOG(Repo, Info, << "Source created with identifier: " << details.Identifier); + AICLI_LOG(Repo, Info, << "Source created with extra data: " << sourceDetails.Data); + AICLI_LOG(Repo, Info, << "Source created with identifier: " << sourceDetails.Identifier); - sourceList.AddSource(details); + sourceList.AddSource(sourceDetails); } return result; @@ -1080,7 +1076,6 @@ namespace AppInstaller::Repository else { AICLI_LOG(Repo, Info, << "Named source requested, found: " << source->Name); - OpenSourceResult result; if (ShouldUpdateBeforeOpen(*source)) @@ -1305,6 +1300,11 @@ namespace AppInstaller::Repository } } + bool SupportsCustomHeader(const SourceDetails& sourceDetails) + { + return Utility::CaseInsensitiveEquals(Rest::RestSourceFactory::Type(), sourceDetails.Type); + } + bool SearchRequest::IsForEverything() const { return (!Query.has_value() && Inclusions.empty() && Filters.empty()); diff --git a/src/AppInstallerRepositoryCore/Rest/RestClient.cpp b/src/AppInstallerRepositoryCore/Rest/RestClient.cpp @@ -20,6 +20,23 @@ namespace AppInstaller::Repository::Rest // Supported versions std::set<Version> WingetSupportedContracts = { Version_1_0_0 }; + constexpr std::string_view WindowsPackageManagerHeader = "Windows-Package-Manager"sv; + + namespace { + std::unordered_map<utility::string_t, utility::string_t> GetHeaders(std::optional<std::string> customHeader) + { + if (!customHeader) + { + AICLI_LOG(Repo, Verbose, << "Custom header not found."); + return {}; + } + + std::unordered_map<utility::string_t, utility::string_t> headers; + headers.emplace(JsonHelper::GetUtilityString(WindowsPackageManagerHeader), JsonHelper::GetUtilityString(customHeader.value())); + return headers; + } + } + RestClient::RestClient(std::unique_ptr<Schema::IRestClient> supportedInterface, std::string sourceIdentifier) : m_interface(std::move(supportedInterface)), m_sourceIdentifier(std::move(sourceIdentifier)) { @@ -46,10 +63,11 @@ namespace AppInstaller::Repository::Rest return endpoint; } - IRestClient::Information RestClient::GetInformation(const utility::string_t& restApi, const HttpClientHelper& clientHelper) + IRestClient::Information RestClient::GetInformation( + const utility::string_t& restApi, const std::unordered_map<utility::string_t, utility::string_t>& additionalHeaders, const HttpClientHelper& clientHelper) { // Call information endpoint - std::optional<web::json::value> response = clientHelper.HandleGet(GetInformationEndpoint(restApi)); + std::optional<web::json::value> response = clientHelper.HandleGet(GetInformationEndpoint(restApi), additionalHeaders); THROW_HR_IF(APPINSTALLER_CLI_ERROR_UNSUPPORTED_RESTSOURCE, !response); @@ -80,26 +98,32 @@ namespace AppInstaller::Repository::Rest return *commonVersions.rbegin(); } - std::unique_ptr<Schema::IRestClient> RestClient::GetSupportedInterface(const std::string& api, const Version& version) + std::unique_ptr<Schema::IRestClient> RestClient::GetSupportedInterface( + const std::string& api, const std::unordered_map<utility::string_t, utility::string_t>& additionalHeaders, const Version& version) { if (version == Version_1_0_0) { return std::make_unique<Schema::V1_0::Interface>(api); } + // TODO: USE additionalHeaders with V1.1 changes. + (void)additionalHeaders; + THROW_HR(APPINSTALLER_CLI_ERROR_RESTSOURCE_INVALID_VERSION); } - RestClient RestClient::Create(const std::string& restApi, const HttpClientHelper& helper) + RestClient RestClient::Create(const std::string& restApi, std::optional<std::string> customHeader, const HttpClientHelper& helper) { utility::string_t restEndpoint = RestHelper::GetRestAPIBaseUri(restApi); THROW_HR_IF(APPINSTALLER_CLI_ERROR_RESTSOURCE_INVALID_URL, !RestHelper::IsValidUri(restEndpoint)); - IRestClient::Information information = GetInformation(restEndpoint, helper); + auto headers = GetHeaders(customHeader); + + IRestClient::Information information = GetInformation(restEndpoint, headers, helper); std::optional<Version> latestCommonVersion = GetLatestCommonVersion(information, WingetSupportedContracts); THROW_HR_IF(APPINSTALLER_CLI_ERROR_UNSUPPORTED_RESTSOURCE, !latestCommonVersion); - std::unique_ptr<Schema::IRestClient> supportedInterface = GetSupportedInterface(utility::conversions::to_utf8string(restEndpoint), latestCommonVersion.value()); + std::unique_ptr<Schema::IRestClient> supportedInterface = GetSupportedInterface(utility::conversions::to_utf8string(restEndpoint), headers, latestCommonVersion.value()); return RestClient{ std::move(supportedInterface), information.SourceIdentifier }; } } diff --git a/src/AppInstallerRepositoryCore/Rest/RestClient.h b/src/AppInstallerRepositoryCore/Rest/RestClient.h @@ -6,6 +6,7 @@ #include "Rest/Schema/IRestClient.h" #include "Rest/HttpClientHelper.h" #include "cpprest/json.h" +#include "AppInstallerRepositorySource.h" namespace AppInstaller::Repository::Rest { @@ -33,12 +34,11 @@ namespace AppInstaller::Repository::Rest static utility::string_t GetInformationEndpoint(const utility::string_t& restApiUri); - static Schema::IRestClient::Information GetInformation(const utility::string_t& restApi, const HttpClientHelper& httpClientHelper); + static Schema::IRestClient::Information GetInformation(const utility::string_t& restApi, const std::unordered_map<utility::string_t, utility::string_t>& additionalHeaders, const HttpClientHelper& httpClientHelper); - static std::unique_ptr<Schema::IRestClient> GetSupportedInterface(const std::string& restApi, const AppInstaller::Utility::Version& version); - - static RestClient Create(const std::string& restApi, const HttpClientHelper& helper = {}); + static std::unique_ptr<Schema::IRestClient> GetSupportedInterface(const std::string& restApi, const std::unordered_map<utility::string_t, utility::string_t>& additionalHeaders, const AppInstaller::Utility::Version& version); + static RestClient Create(const std::string& restApi, std::optional<std::string> customHeader, const HttpClientHelper & helper = {}); private: std::unique_ptr<Schema::IRestClient> m_interface; std::string m_sourceIdentifier; diff --git a/src/AppInstallerRepositoryCore/Rest/RestSourceFactory.cpp b/src/AppInstallerRepositoryCore/Rest/RestSourceFactory.cpp @@ -19,7 +19,7 @@ namespace AppInstaller::Repository::Rest { THROW_HR_IF(E_INVALIDARG, !Utility::CaseInsensitiveEquals(details.Type, RestSourceFactory::Type())); - RestClient restClient = RestClient::Create(details.Arg); + RestClient restClient = RestClient::Create(details.Arg, details.CustomHeader); return std::make_shared<RestSource>(details, restClient.GetSourceIdentifier(), std::move(restClient)); }