diff --git a/doc/ReleaseNotes.md b/doc/ReleaseNotes.md index 57c970aeb6..2d1b8900a1 100644 --- a/doc/ReleaseNotes.md +++ b/doc/ReleaseNotes.md @@ -4,4 +4,4 @@ Nothing yet. ## Bug Fixes -* None yet +* Refactored source type handling to use a `SourceType` enum with centralized string conversion, and added stronger `source add` type validation (#4463). diff --git a/src/AppInstallerCLICore/Commands/DscSourceResource.cpp b/src/AppInstallerCLICore/Commands/DscSourceResource.cpp index 733a254879..4323cc67a0 100644 --- a/src/AppInstallerCLICore/Commands/DscSourceResource.cpp +++ b/src/AppInstallerCLICore/Commands/DscSourceResource.cpp @@ -107,7 +107,7 @@ namespace AppInstaller::CLI { Output.Exist(true); Output.Argument(source.Arg); - Output.Type(source.Type); + Output.Type(std::string{ SourceTypeEnumToString(source.Type) }); Output.TrustLevel(TrustLevelStringFromFlags(source.TrustLevel)); Output.Explicit(source.Explicit); @@ -495,7 +495,7 @@ namespace AppInstaller::CLI SourceResourceObject output; output.SourceName(source.Name); output.Argument(source.Arg); - output.Type(source.Type); + output.Type(std::string{ SourceTypeEnumToString(source.Type) }); output.TrustLevel(TrustLevelStringFromFlags(source.TrustLevel)); output.Explicit(source.Explicit); diff --git a/src/AppInstallerCLICore/Commands/SourceCommand.cpp b/src/AppInstallerCLICore/Commands/SourceCommand.cpp index aa820c9277..8b4ee09410 100644 --- a/src/AppInstallerCLICore/Commands/SourceCommand.cpp +++ b/src/AppInstallerCLICore/Commands/SourceCommand.cpp @@ -94,6 +94,22 @@ namespace AppInstaller::CLI void SourceAddCommand::ValidateArgumentsInternal(Args& execArgs) const { + if (execArgs.Contains(Execution::Args::Type::SourceType)) + { + std::string_view sourceTypeArg = execArgs.GetArg(Execution::Args::Type::SourceType); + auto validOptions = std::vector{ + Utility::LocIndString{ Repository::SourceTypeEnumToString(Repository::SourceType::PreIndexedPackage) }, + Utility::LocIndString{ Repository::SourceTypeEnumToString(Repository::SourceType::Rest) } }; + + auto sourceType = Repository::TryConvertToSourceTypeEnum(sourceTypeArg); + bool isUserAddType = sourceType.has_value() && + (sourceType.value() == Repository::SourceType::PreIndexedPackage || sourceType.value() == Repository::SourceType::Rest); + if (!isUserAddType) + { + throw CommandException(Resource::String::InvalidArgumentValueError(ArgumentCommon::ForType(Execution::Args::Type::SourceType).Name, Utility::Join(","_liv, validOptions))); + } + } + if (execArgs.Contains(Execution::Args::Type::SourceTrustLevel)) { try diff --git a/src/AppInstallerCLICore/ConfigurationWingetDscModuleUnitValidation.cpp b/src/AppInstallerCLICore/ConfigurationWingetDscModuleUnitValidation.cpp index 9455573983..8330143aeb 100644 --- a/src/AppInstallerCLICore/ConfigurationWingetDscModuleUnitValidation.cpp +++ b/src/AppInstallerCLICore/ConfigurationWingetDscModuleUnitValidation.cpp @@ -119,7 +119,7 @@ namespace AppInstaller::CLI::Configuration { if (Utility::CaseInsensitiveEquals(wellKnownSource.Name, source.Name) && Utility::CaseInsensitiveEquals(wellKnownSource.Arg, source.Arg) && - Utility::CaseInsensitiveEquals(wellKnownSource.Type, source.Type)) + Utility::CaseInsensitiveEquals(Repository::SourceTypeEnumToString(wellKnownSource.Type), source.Type)) { return true; } diff --git a/src/AppInstallerCLICore/PackageCollection.cpp b/src/AppInstallerCLICore/PackageCollection.cpp index 5d7b3eafa8..d696799f18 100644 --- a/src/AppInstallerCLICore/PackageCollection.cpp +++ b/src/AppInstallerCLICore/PackageCollection.cpp @@ -127,7 +127,9 @@ namespace AppInstaller::CLI sourceDetails.Identifier = Utility::LocIndString{ detailsNode[ss.PackagesJson_Source_Identifier].asString() }; sourceDetails.Name = detailsNode[ss.PackagesJson_Source_Name].asString(); sourceDetails.Arg = detailsNode[ss.PackagesJson_Source_Argument].asString(); - sourceDetails.Type = detailsNode[ss.PackagesJson_Source_Type].asString(); + auto sourceType = TryConvertToSourceTypeEnum(detailsNode[ss.PackagesJson_Source_Type].asString()); + THROW_HR_IF(APPINSTALLER_CLI_ERROR_JSON_INVALID_FILE, !sourceType.has_value()); + sourceDetails.Type = sourceType.value(); PackageCollection::Source source{ std::move(sourceDetails) }; for (const auto& packageNode : sourceNode[ss.PackagesJson_Packages]) @@ -238,7 +240,7 @@ namespace AppInstaller::CLI sourceDetailsNode[ss.PackagesJson_Source_Name] = source.Details.Name; sourceDetailsNode[ss.PackagesJson_Source_Argument] = source.Details.Arg; sourceDetailsNode[ss.PackagesJson_Source_Identifier] = source.Details.Identifier; - sourceDetailsNode[ss.PackagesJson_Source_Type] = source.Details.Type; + sourceDetailsNode[ss.PackagesJson_Source_Type] = std::string{ SourceTypeEnumToString(source.Details.Type) }; sourceNode[ss.PackagesJson_Source_Details] = std::move(sourceDetailsNode); sourceNode[ss.PackagesJson_Packages] = Json::Value{ Json::ValueType::arrayValue }; diff --git a/src/AppInstallerCLICore/Workflows/ConfigurationFlow.cpp b/src/AppInstallerCLICore/Workflows/ConfigurationFlow.cpp index 5cf513823a..084eb020b5 100644 --- a/src/AppInstallerCLICore/Workflows/ConfigurationFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/ConfigurationFlow.cpp @@ -1495,7 +1495,7 @@ namespace AppInstaller::CLI::Workflow ConfigurationUnit CreateWinGetSourceUnit(const PackageCollection::Source& source, std::wstring_view unitType) { - std::string sourceUnitId = source.Details.Name + '_' + source.Details.Type; + std::string sourceUnitId = source.Details.Name + '_' + std::string{ Repository::SourceTypeEnumToString(source.Details.Type) }; std::wstring sourceUnitIdWide = Utility::ConvertToUTF16(sourceUnitId); ConfigurationUnit unit; @@ -1512,7 +1512,7 @@ namespace AppInstaller::CLI::Workflow ValueSet settings; settings.Insert(s_Setting_WinGetSource_Name, PropertyValue::CreateString(Utility::ConvertToUTF16(source.Details.Name))); settings.Insert(s_Setting_WinGetSource_Arg, PropertyValue::CreateString(Utility::ConvertToUTF16(source.Details.Arg))); - settings.Insert(s_Setting_WinGetSource_Type, PropertyValue::CreateString(Utility::ConvertToUTF16(source.Details.Type))); + settings.Insert(s_Setting_WinGetSource_Type, PropertyValue::CreateString(Utility::ConvertToUTF16(Repository::SourceTypeEnumToString(source.Details.Type)))); if (WI_IsFlagSet(source.Details.TrustLevel, Repository::SourceTrustLevel::Trusted)) { settings.Insert(s_Setting_WinGetSource_TrustLevel, PropertyValue::CreateString(L"trusted")); diff --git a/src/AppInstallerCLICore/Workflows/SourceFlow.cpp b/src/AppInstallerCLICore/Workflows/SourceFlow.cpp index acccb9a421..d7b64be29b 100644 --- a/src/AppInstallerCLICore/Workflows/SourceFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/SourceFlow.cpp @@ -50,7 +50,7 @@ namespace AppInstaller::CLI::Workflow auto sourceList = context.Get(); std::string_view name = context.Args.GetArg(Args::Type::SourceName); std::string_view arg = context.Args.GetArg(Args::Type::SourceArg); - std::string_view type = context.Args.GetArg(Args::Type::SourceType); + Repository::SourceType type = Repository::TryConvertToSourceTypeEnum(context.Args.GetArg(Args::Type::SourceType)).value_or(Repository::Source::GetDefaultSourceType()); // In the absence of a specified type, the default is Microsoft.PreIndexed.Package for comparison. // The default type assignment to the source takes place during the add operation (Source::Add in Repository.cpp). @@ -59,10 +59,6 @@ namespace AppInstaller::CLI::Workflow // For example, the following commands would be allowed, but they acts as different alias to same source: // winget source add "mysource1" "https:\\mysource" --trust - level trusted // winget source add "mysource2" "https:\\mysource" --trust - level trusted - if (type.empty()) - { - type = Repository::Source::GetDefaultSourceType(); - } for (const auto& details : sourceList) { @@ -83,7 +79,7 @@ namespace AppInstaller::CLI::Workflow } } - if (!details.Arg.empty() && details.Arg == arg && details.Type == type) + if (!details.Arg.empty() && details.Arg == arg && type == details.Type) { context.Reporter.Error() << Resource::String::SourceAddAlreadyExistsDifferentName << std::endl << " "_liv << details.Name << " -> "_liv << details.Arg << std::endl; @@ -118,7 +114,7 @@ namespace AppInstaller::CLI::Workflow { std::string_view name = context.Args.GetArg(Args::Type::SourceName); std::string_view arg = context.Args.GetArg(Args::Type::SourceArg); - std::string_view type = context.Args.GetArg(Args::Type::SourceType); + Repository::SourceType type = Repository::TryConvertToSourceTypeEnum(context.Args.GetArg(Args::Type::SourceType)).value_or(Repository::Source::GetDefaultSourceType()); Repository::SourceTrustLevel trustLevel = Repository::SourceTrustLevel::None; if (context.Args.Contains(Execution::Args::Type::SourceTrustLevel)) @@ -183,7 +179,7 @@ namespace AppInstaller::CLI::Workflow Execution::TableOutput<2> table(context.Reporter, { Resource::String::SourceListField, Resource::String::SourceListValue }); table.OutputLine({ Resource::LocString(Resource::String::SourceListName), source.Name }); - table.OutputLine({ Resource::LocString(Resource::String::SourceListType), source.Type }); + table.OutputLine({ Resource::LocString(Resource::String::SourceListType), std::string{ Repository::SourceTypeEnumToString(source.Type) } }); table.OutputLine({ Resource::LocString(Resource::String::SourceListArg), source.Arg }); table.OutputLine({ Resource::LocString(Resource::String::SourceListData), source.Data }); table.OutputLine({ Resource::LocString(Resource::String::SourceListIdentifier), source.Identifier }); @@ -401,7 +397,7 @@ namespace AppInstaller::CLI::Workflow { SourceFromPolicy s; s.Name = source.Name; - s.Type = source.Type; + s.Type = std::string{ Repository::SourceTypeEnumToString(source.Type) }; s.Arg = source.Arg; s.Data = source.Data; s.Identifier = source.Identifier; diff --git a/src/AppInstallerCLITests/ARPChanges.cpp b/src/AppInstallerCLITests/ARPChanges.cpp index 66b4481c05..f8da3215fe 100644 --- a/src/AppInstallerCLITests/ARPChanges.cpp +++ b/src/AppInstallerCLITests/ARPChanges.cpp @@ -102,7 +102,7 @@ struct ARPTestContext : public Context }; // Inject our source - TestHook_SetSourceFactoryOverride(std::string{ Repository::Microsoft::PredefinedInstalledSourceFactory::Type() }, SourceFactory); + TestHook_SetSourceFactoryOverride(SourceType::PredefinedInstalled, SourceFactory); Source = std::make_shared(); Source->SearchFunction = [&](const SearchRequest& request) diff --git a/src/AppInstallerCLITests/CompositeSource.cpp b/src/AppInstallerCLITests/CompositeSource.cpp index 8160c8df5e..cd1259adb6 100644 --- a/src/AppInstallerCLITests/CompositeSource.cpp +++ b/src/AppInstallerCLITests/CompositeSource.cpp @@ -330,7 +330,7 @@ struct CompositeWithTrackingTestSetup : public CompositeTestSetup CompositeWithTrackingTestSetup() : TrackingFactory([&](const SourceDetails&) { return Tracking; }) { Tracking = std::make_shared(SourceDetails{}, SQLiteIndex::CreateNew(SQLITE_MEMORY_DB_CONNECTION_TARGET)); - TestHook_SetSourceFactoryOverride(std::string{ PackageTrackingCatalogSourceFactory::Type() }, TrackingFactory); + TestHook_SetSourceFactoryOverride(SourceType::PackageTracking, TrackingFactory); } ~CompositeWithTrackingTestSetup() diff --git a/src/AppInstallerCLITests/PackageCollection.cpp b/src/AppInstallerCLITests/PackageCollection.cpp index 7a76c82578..2b7c68fb97 100644 --- a/src/AppInstallerCLITests/PackageCollection.cpp +++ b/src/AppInstallerCLITests/PackageCollection.cpp @@ -84,7 +84,7 @@ namespace const auto& jsonSourceDetails = GetAndValidateJsonProperty(*jsonSourceItr, s_PackagesJson_Source_Details, Json::ValueType::objectValue); ValidateJsonStringProperty(jsonSourceDetails, s_PackagesJson_Source_Name, sourceItr->Details.Name); ValidateJsonStringProperty(jsonSourceDetails, s_PackagesJson_Source_Argument, sourceItr->Details.Arg); - ValidateJsonStringProperty(jsonSourceDetails, s_PackagesJson_Source_Type, sourceItr->Details.Type); + ValidateJsonStringProperty(jsonSourceDetails, s_PackagesJson_Source_Type, SourceTypeEnumToString(sourceItr->Details.Type)); ValidateJsonStringProperty(jsonSourceDetails, s_PackagesJson_Source_Identifier, sourceItr->Details.Identifier); const auto& jsonPackages = GetAndValidateJsonProperty(*jsonSourceItr, s_PackagesJson_Packages, Json::ValueType::arrayValue); @@ -137,7 +137,7 @@ TEST_CASE("PackageCollection_Write_SingleSource", "[PackageCollection]") PackageCollection::Source source; source.Details.Name = "TestSource"; source.Details.Arg = "https://aka.ms/winget"; - source.Details.Type = "Microsoft.PreIndexed.Package"; + source.Details.Type = SourceType::PreIndexedPackage; source.Details.Identifier = "TestSourceId"; source.Packages.emplace_back(LocIndString{ "test.package1"sv }, Version{ "1.0.1" }, Channel{ "" }); @@ -157,14 +157,14 @@ TEST_CASE("PackageCollection_Write_MultipleSources", "[PackageCollection]") PackageCollection::Source source1; source1.Details.Name = "TestSource"; source1.Details.Arg = "https://aka.ms/winget"; - source1.Details.Type = "Microsoft.PreIndexed.Package"; + source1.Details.Type = SourceType::PreIndexedPackage; source1.Details.Identifier = "TestSourceId"; source1.Packages.emplace_back(LocIndString{ "test.package1"sv }, Version{ "1.0.1" }, Channel{ "" }); PackageCollection::Source source2; source2.Details.Name = "TestSource2"; source2.Details.Arg = "https://aka.ms/winget"; - source2.Details.Type = "*Test"; + source2.Details.Type = SourceType::ConfigurableTest; source2.Details.Identifier = "SecondId"; source2.Packages.emplace_back(LocIndString{ "test.package2"sv }, Version{ "2.1.0" }, Channel{ "Beta" }); @@ -182,7 +182,7 @@ TEST_CASE("PackageCollection_Write_OverrideAndCustomSwitches", "[PackageCollecti PackageCollection::Source source; source.Details.Name = "TestSource"; source.Details.Arg = "https://aka.ms/winget"; - source.Details.Type = "Microsoft.PreIndexed.Package"; + source.Details.Type = SourceType::PreIndexedPackage; source.Details.Identifier = "TestSourceId"; PackageCollection::Package packageWithOverride{ LocIndString{ "test.withOverride"sv }, Version{ "1.0" }, Channel{ "" } }; @@ -246,7 +246,7 @@ TEST_CASE("PackageCollection_Read_SingleSource_1_0", "[PackageCollection]") PackageCollection::Source source; source.Details.Name = "TestSource"; source.Details.Arg = "https://aka.ms/winget"; - source.Details.Type = "Microsoft.PreIndexed.Package"; + source.Details.Type = SourceType::PreIndexedPackage; source.Details.Identifier = "TestSourceId"; source.Packages.emplace_back(LocIndString{ "test.WithVersion"sv }, Version{ "0.1" }, Channel{ "Preview" }); @@ -296,7 +296,7 @@ TEST_CASE("PackageCollection_Read_SingleSource_2_0", "[PackageCollection]") PackageCollection::Source source; source.Details.Name = "TestSource"; source.Details.Arg = "https://aka.ms/winget"; - source.Details.Type = "Microsoft.PreIndexed.Package"; + source.Details.Type = SourceType::PreIndexedPackage; source.Details.Identifier = "TestSourceId"; source.Packages.emplace_back(LocIndString{ "test.WithVersion"sv }, Version{ "0.1" }, Channel{ "Preview" }); @@ -378,7 +378,7 @@ TEST_CASE("PackageCollection_WriteRead_OverrideAndCustomSwitches", "[PackageColl PackageCollection::Source source; source.Details.Name = "TestSource"; source.Details.Arg = "https://aka.ms/winget"; - source.Details.Type = "Microsoft.PreIndexed.Package"; + source.Details.Type = SourceType::PreIndexedPackage; source.Details.Identifier = "TestSourceId"; PackageCollection::Package packageWithOverride{ LocIndString{ "test.withOverride"sv }, Version{ "1.0" }, Channel{ "" } }; @@ -437,7 +437,7 @@ TEST_CASE("PackageCollection_Read_MultipleSources_1_0", "[PackageCollection]") "Argument": "//secondSource", "Identifier": "Id2", "Name": "Second", - "Type": "*TestSource" + "Type": "Microsoft.Test.Configurable" }, "Packages": [ { @@ -457,14 +457,14 @@ TEST_CASE("PackageCollection_Read_MultipleSources_1_0", "[PackageCollection]") PackageCollection::Source source1; source1.Details.Name = "First"; source1.Details.Arg = "//firstSource"; - source1.Details.Type = "Microsoft.PreIndexed.Package"; + source1.Details.Type = SourceType::PreIndexedPackage; source1.Details.Identifier = "Id1"; source1.Packages.emplace_back(LocIndString{ "test"sv }, Version{ "" }, Channel{ "" }); PackageCollection::Source source2; source2.Details.Name = "Second"; source2.Details.Arg = "//secondSource"; - source2.Details.Type = "*TestSource"; + source2.Details.Type = SourceType::ConfigurableTest; source2.Details.Identifier = "Id2"; source2.Packages.emplace_back(LocIndString{ "test2"sv }, Version{ "1.0" }, Channel{ "" }); @@ -503,7 +503,7 @@ TEST_CASE("PackageCollection_Read_MultipleSources_2_0", "[PackageCollection]") "Argument": "//secondSource", "Identifier": "Id2", "Name": "Second", - "Type": "*TestSource" + "Type": "Microsoft.Test.Configurable" }, "Packages": [ { @@ -523,14 +523,14 @@ TEST_CASE("PackageCollection_Read_MultipleSources_2_0", "[PackageCollection]") PackageCollection::Source source1; source1.Details.Name = "First"; source1.Details.Arg = "//firstSource"; - source1.Details.Type = "Microsoft.PreIndexed.Package"; + source1.Details.Type = SourceType::PreIndexedPackage; source1.Details.Identifier = "Id1"; source1.Packages.emplace_back(LocIndString{ "test"sv }, Version{ "" }, Channel{ "" }); PackageCollection::Source source2; source2.Details.Name = "Second"; source2.Details.Arg = "//secondSource"; - source2.Details.Type = "*TestSource"; + source2.Details.Type = SourceType::ConfigurableTest; source2.Details.Identifier = "Id2"; source2.Packages.emplace_back(LocIndString{ "test2"sv }, Version{ "1.0" }, Channel{ "" }); @@ -569,7 +569,7 @@ TEST_CASE("PackageCollection_Read_RepeatedSource", "[PackageCollection]") "Argument": "//secondSource", "Identifier": "Id2", "Name": "Second", - "Type": "*TestSource" + "Type": "Microsoft.Test.Configurable" }, "Packages": [ { @@ -583,7 +583,7 @@ TEST_CASE("PackageCollection_Read_RepeatedSource", "[PackageCollection]") "Argument": "//secondSource", "Identifier": "Id2", "Name": "Second", - "Type": "*TestSource" + "Type": "Microsoft.Test.Configurable" }, "Packages": [ { @@ -603,14 +603,14 @@ TEST_CASE("PackageCollection_Read_RepeatedSource", "[PackageCollection]") PackageCollection::Source source1; source1.Details.Name = "First"; source1.Details.Arg = "//firstSource"; - source1.Details.Type = "Microsoft.PreIndexed.Package"; + source1.Details.Type = SourceType::PreIndexedPackage; source1.Details.Identifier = "Id1"; source1.Packages.emplace_back(LocIndString{ "test"sv }, Version{ "" }, Channel{ "" }); PackageCollection::Source source2; source2.Details.Name = "Second"; source2.Details.Arg = "//secondSource"; - source2.Details.Type = "*TestSource"; + source2.Details.Type = SourceType::ConfigurableTest; source2.Details.Identifier = "Id2"; source2.Packages.emplace_back(LocIndString{ "test2"sv }, Version{ "1.0" }, Channel{ "" }); source2.Packages.emplace_back(LocIndString{ "test3"sv }, Version{ "1.2" }, Channel{ "" }); diff --git a/src/AppInstallerCLITests/PackageTrackingCatalog.cpp b/src/AppInstallerCLITests/PackageTrackingCatalog.cpp index d0a6354a1a..0b38079b5d 100644 --- a/src/AppInstallerCLITests/PackageTrackingCatalog.cpp +++ b/src/AppInstallerCLITests/PackageTrackingCatalog.cpp @@ -29,7 +29,7 @@ namespace details.Identifier = "*SimpleTestSetup"; details.Name = "TestName"; - details.Type = "TestType"; + details.Type = SourceType::ConfigurableTest; details.Arg = testManifest.GetPath().parent_path().u8string(); details.Data = ""; diff --git a/src/AppInstallerCLITests/PreIndexedPackageSource.cpp b/src/AppInstallerCLITests/PreIndexedPackageSource.cpp index fe36dd1b31..653c14d3a3 100644 --- a/src/AppInstallerCLITests/PreIndexedPackageSource.cpp +++ b/src/AppInstallerCLITests/PreIndexedPackageSource.cpp @@ -79,7 +79,7 @@ TEST_CASE("PIPS_Add", "[pips]") SourceDetails details; details.Name = "TestName"; - details.Type = AppInstaller::Repository::Microsoft::PreIndexedPackageSourceFactory::Type(); + details.Type = AppInstaller::Repository::SourceType::PreIndexedPackage; details.Arg = dir; ProgressCallback callback; @@ -117,7 +117,7 @@ TEST_CASE("PIPS_UpdateSameVersion", "[pips]") SourceDetails details; details.Name = "TestName"; - details.Type = AppInstaller::Repository::Microsoft::PreIndexedPackageSourceFactory::Type(); + details.Type = AppInstaller::Repository::SourceType::PreIndexedPackage; details.Arg = dir; TestProgress callback; @@ -156,7 +156,7 @@ TEST_CASE("PIPS_UpdateNewVersion", "[pips]") SourceDetails details; details.Name = "TestName"; - details.Type = AppInstaller::Repository::Microsoft::PreIndexedPackageSourceFactory::Type(); + details.Type = AppInstaller::Repository::SourceType::PreIndexedPackage; details.Arg = dir; TestProgress callback; @@ -205,7 +205,7 @@ TEST_CASE("PIPS_Remove", "[pips]") SourceDetails details; details.Name = "TestName"; - details.Type = AppInstaller::Repository::Microsoft::PreIndexedPackageSourceFactory::Type(); + details.Type = AppInstaller::Repository::SourceType::PreIndexedPackage; details.Arg = dir; ProgressCallback callback; diff --git a/src/AppInstallerCLITests/PredefinedInstalledSource.cpp b/src/AppInstallerCLITests/PredefinedInstalledSource.cpp index 4939f99cda..6e41f6d398 100644 --- a/src/AppInstallerCLITests/PredefinedInstalledSource.cpp +++ b/src/AppInstallerCLITests/PredefinedInstalledSource.cpp @@ -138,7 +138,7 @@ void VerifyEntryAgainstIndex(const SQLiteIndex& index, SQLiteIndex::IdType manif std::shared_ptr CreatePredefinedInstalledSource(Factory::Filter filter = Factory::Filter::None) { SourceDetails details; - details.Type = Factory::Type(); + details.Type = AppInstaller::Repository::SourceType::PredefinedInstalled; details.Arg = Factory::FilterToString(filter); TestProgress progress; diff --git a/src/AppInstallerCLITests/SQLiteIndexSource.cpp b/src/AppInstallerCLITests/SQLiteIndexSource.cpp index 296d0f2c0b..9190e9ee32 100644 --- a/src/AppInstallerCLITests/SQLiteIndexSource.cpp +++ b/src/AppInstallerCLITests/SQLiteIndexSource.cpp @@ -38,7 +38,7 @@ static std::shared_ptr SimpleTestSetup(const std::string& fil index.AddManifest(manifest, relativePath); details.Name = "TestName"; - details.Type = "TestType"; + details.Type = SourceType::ConfigurableTest; details.Arg = sourceFilesDirectoryPath.u8string(); details.Data = ""; details.Identifier = "SimpleTestSetup"; diff --git a/src/AppInstallerCLITests/SourceFlow.cpp b/src/AppInstallerCLITests/SourceFlow.cpp index 1bea3bbc92..3852840ad8 100644 --- a/src/AppInstallerCLITests/SourceFlow.cpp +++ b/src/AppInstallerCLITests/SourceFlow.cpp @@ -45,7 +45,7 @@ TEST_CASE("SourceAddFlow_Agreement", "[SourceAddFlow][workflow]") auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForSourceAddWithAgreements(context); context.Args.AddArg(Execution::Args::Type::SourceName, "TestSource"sv); - context.Args.AddArg(Execution::Args::Type::SourceType, "Microsoft.Test"sv); + context.Args.AddArg(Execution::Args::Type::SourceType, "Microsoft.Rest"sv); context.Args.AddArg(Execution::Args::Type::SourceArg, "TestArg"sv); context.Args.AddArg(Execution::Args::Type::AcceptSourceAgreements); @@ -72,7 +72,7 @@ TEST_CASE("SourceAddFlow_Agreement_Prompt_Yes", "[SourceAddFlow][workflow]") auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForSourceAddWithAgreements(context); context.Args.AddArg(Execution::Args::Type::SourceName, "TestSource"sv); - context.Args.AddArg(Execution::Args::Type::SourceType, "Microsoft.Test"sv); + context.Args.AddArg(Execution::Args::Type::SourceType, "Microsoft.Rest"sv); context.Args.AddArg(Execution::Args::Type::SourceArg, "TestArg"sv); SourceAddCommand sourceAdd({}); @@ -98,7 +98,7 @@ TEST_CASE("SourceAddFlow_Agreement_Prompt_No", "[SourceAddFlow][workflow]") auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForSourceAddWithAgreements(context, false); context.Args.AddArg(Execution::Args::Type::SourceName, "TestSource"sv); - context.Args.AddArg(Execution::Args::Type::SourceType, "Microsoft.Test"sv); + context.Args.AddArg(Execution::Args::Type::SourceType, "Microsoft.Rest"sv); context.Args.AddArg(Execution::Args::Type::SourceArg, "TestArg"sv); SourceAddCommand sourceAdd({}); @@ -115,6 +115,39 @@ TEST_CASE("SourceAddFlow_Agreement_Prompt_No", "[SourceAddFlow][workflow]") REQUIRE(context.GetTerminationHR() == APPINSTALLER_CLI_ERROR_SOURCE_AGREEMENTS_NOT_ACCEPTED); } +TEST_CASE("SourceAddCommand_ValidateArguments_SourceType", "[SourceAddFlow][workflow]") +{ + SourceAddCommand sourceAdd({}); + + SECTION("Valid external source type") + { + Execution::Args args; + args.AddArg(Execution::Args::Type::SourceName, "TestSource"sv); + args.AddArg(Execution::Args::Type::SourceArg, "https://testsource.invalid"sv); + args.AddArg(Execution::Args::Type::SourceType, "microsoft.rest"sv); + + REQUIRE_NOTHROW(sourceAdd.ValidateArguments(args)); + } + + SECTION("Invalid source type") + { + Execution::Args args; + args.AddArg(Execution::Args::Type::SourceName, "TestSource"sv); + args.AddArg(Execution::Args::Type::SourceType, "Microsoft.Unknown"sv); + + REQUIRE_THROWS(sourceAdd.ValidateArguments(args)); + } + + SECTION("Internal source type not accepted by source add") + { + Execution::Args args; + args.AddArg(Execution::Args::Type::SourceName, "TestSource"sv); + args.AddArg(Execution::Args::Type::SourceType, "Microsoft.Predefined.Installed"sv); + + REQUIRE_THROWS(sourceAdd.ValidateArguments(args)); + } +} + TEST_CASE("OpenSource_WithCustomHeader", "[OpenSource][CustomHeader]") { SetSetting(Stream::UserSources, R"(Sources:)"sv); @@ -122,7 +155,7 @@ TEST_CASE("OpenSource_WithCustomHeader", "[OpenSource][CustomHeader]") SourceDetails details; details.Name = "restsource"; - details.Type = "Microsoft.Rest"; + details.Type = SourceType::ConfigurableTest; details.Arg = "thisIsTheArg"; details.Data = "thisIsTheData"; diff --git a/src/AppInstallerCLITests/Sources.cpp b/src/AppInstallerCLITests/Sources.cpp index 730dfa02f2..8bb50c2d72 100644 --- a/src/AppInstallerCLITests/Sources.cpp +++ b/src/AppInstallerCLITests/Sources.cpp @@ -60,7 +60,7 @@ constexpr std::string_view s_DefaultSourcesTombstoned = R"( constexpr std::string_view s_SingleSource = R"( Sources: - Name: testName - Type: testType + Type: Microsoft.Test.Configurable Arg: testArg Data: testData IsTombstone: false @@ -114,12 +114,12 @@ constexpr std::string_view s_SingleSourceMetadataUpdate = R"( constexpr std::string_view s_DoubleSource = R"( Sources: - Name: testName - Type: testType + Type: Microsoft.Test.Configurable Arg: testArg Data: testData IsTombstone: false - Name: testName2 - Type: testType + Type: Microsoft.Test.Configurable Arg: testArg2 Data: testData2 IsTombstone: false @@ -136,19 +136,19 @@ constexpr std::string_view s_DoubleSourceMetadata = R"( constexpr std::string_view s_ThreeSources = R"( Sources: - Name: testName - Type: testType + Type: Microsoft.Test.Configurable Arg: testArg Data: testData IsTombstone: false Priority: 1 - Name: testName2 - Type: testType2 + Type: Microsoft.Test.Configurable Arg: testArg2 Data: testData2 IsTombstone: false Priority: 5 - Name: testName3 - Type: testType3 + Type: Microsoft.Test.Configurable Arg: testArg3 Data: testData3 IsTombstone: false @@ -183,7 +183,7 @@ constexpr std::string_view s_ThreeSourcesMetadata = R"( constexpr std::string_view s_SingleSource_MissingArg = R"( Sources: - Name: testName - Type: testType + Type: Microsoft.Test.Configurable Data: testData IsTombstone: false )"sv; @@ -191,12 +191,12 @@ constexpr std::string_view s_SingleSource_MissingArg = R"( constexpr std::string_view s_TwoSource_AggregateSourceTest = R"( Sources: - Name: winget - Type: testType + Type: Microsoft.Test.Configurable Arg: testArg Data: testData IsTombstone: false - Name: msstore - Type: testType + Type: Microsoft.Test.Configurable Arg: testArg Data: testData IsTombstone: false @@ -214,7 +214,7 @@ constexpr std::string_view s_DefaultSourceAsUserSource = R"( constexpr std::string_view s_UserSourceNamedLikeDefault = R"( Sources: - Name: winget - Type: testType + Type: Microsoft.Test.Configurable Arg: testArg Data: testData IsTombstone: false @@ -223,7 +223,7 @@ constexpr std::string_view s_UserSourceNamedLikeDefault = R"( constexpr std::string_view s_SingleSource_AllProperties= R"( Sources: - Name: testName - Type: testType + Type: Microsoft.Test.Configurable Arg: testArg Data: testData IsTombstone: false @@ -340,7 +340,7 @@ TEST_CASE("RepoSources_DefaultSourceOverride", "[sources]") REQUIRE(beforeOverride[2].Name == "winget-font"); REQUIRE(beforeOverride[2].Arg == "https://cdn.winget.microsoft.com/fonts"); REQUIRE(beforeOverride[2].Data == "Microsoft.Winget.Fonts.Source_8wekyb3d8bbwe"); - REQUIRE(beforeOverride[2].Type == "Microsoft.PreIndexed.Package"); + REQUIRE(beforeOverride[2].Type == SourceType::PreIndexedPackage); REQUIRE(beforeOverride[2].Origin == SourceOrigin::Default); REQUIRE(beforeOverride[2].Explicit == true); REQUIRE(beforeOverride[2].Priority == 0); @@ -373,7 +373,7 @@ TEST_CASE("RepoSources_SingleSource", "[sources]") REQUIRE(sources.size() == c_DefaultSourceCount + 1); REQUIRE(sources[0].Name == "testName"); - REQUIRE(sources[0].Type == "testType"); + REQUIRE(sources[0].Type == SourceType::ConfigurableTest); REQUIRE(sources[0].Arg == "testArg"); REQUIRE(sources[0].Data == "testData"); REQUIRE(sources[0].Origin == SourceOrigin::User); @@ -391,7 +391,7 @@ TEST_CASE("RepoSources_SingleSource_AllProperties", "[sources]") REQUIRE(sources.size() == c_DefaultSourceCount + 1); REQUIRE(sources[0].Name == "testName"); - REQUIRE(sources[0].Type == "testType"); + REQUIRE(sources[0].Type == SourceType::ConfigurableTest); REQUIRE(sources[0].Arg == "testArg"); REQUIRE(sources[0].Data == "testData"); REQUIRE(sources[0].Origin == SourceOrigin::User); @@ -434,7 +434,7 @@ TEST_CASE("RepoSources_ThreeSources", "[sources]") INFO("Source #" << index << " [" << i << "]"); REQUIRE(sources[index].Name == "testName"s + suffixStrings[i]); - REQUIRE(sources[index].Type == "testType"s + suffixStrings[i]); + REQUIRE(sources[index].Type == SourceType::ConfigurableTest); REQUIRE(sources[index].Arg == "testArg"s + suffixStrings[i]); REQUIRE(sources[index].Data == "testData"s + suffixStrings[i]); REQUIRE(sources[index].LastUpdateTime == ConvertUnixEpochToSystemClock(i)); @@ -463,7 +463,7 @@ TEST_CASE("RepoSources_AddSource", "[sources]") SourceDetails details; details.Name = "thisIsTheName"; - details.Type = "thisIsTheType"; + details.Type = SourceType::ConfigurableTest; details.Arg = "thisIsTheArg"; details.Data = "thisIsTheData"; details.TrustLevel = Repository::SourceTrustLevel::None; @@ -502,7 +502,7 @@ TEST_CASE("RepoSources_AddMultipleSources", "[sources]") SourceDetails details; details.Name = "thisIsTheName"; - details.Type = "thisIsTheType"; + details.Type = SourceType::ConfigurableTest; details.Arg = "thisIsTheArg"; details.Data = "thisIsTheData"; @@ -529,7 +529,7 @@ TEST_CASE("RepoSources_AddMultipleSources", "[sources]") SourceDetails details2; details2.Name = details.Name + suffix[1]; - details2.Type = details.Type + suffix[1]; + details2.Type = details.Type; details2.Arg = details.Arg + suffix[1]; details2.Data = details.Data + suffix[1]; TestSourceFactory factory2{ SourcesTestSource::Create }; @@ -545,7 +545,7 @@ TEST_CASE("RepoSources_AddMultipleSources", "[sources]") { INFO("Source #" << i); REQUIRE(sources[i].Name == details.Name + suffix[i]); - REQUIRE(sources[i].Type == details.Type + suffix[i]); + REQUIRE(sources[i].Type == details.Type); REQUIRE(sources[i].Arg == details.Arg + suffix[i]); REQUIRE(sources[i].Data == details.Data + suffix[i]); REQUIRE(sources[i].LastUpdateTime != ConvertUnixEpochToSystemClock(0)); @@ -564,7 +564,7 @@ TEST_CASE("RepoSources_UpdateSource", "[sources]") SourceDetails details; details.Name = "thisIsTheName"; - details.Type = "thisIsTheType"; + details.Type = SourceType::ConfigurableTest; details.Arg = "thisIsTheArg"; details.Data = "thisIsTheData"; @@ -618,7 +618,7 @@ TEST_CASE("RepoSources_UpdateSourceRetries", "[sources]") SourceDetails details; details.Name = "thisIsTheName"; - details.Type = "thisIsTheType"; + details.Type = SourceType::ConfigurableTest; details.Arg = "thisIsTheArg"; details.Data = "thisIsTheData"; @@ -654,7 +654,7 @@ TEST_CASE("RepoSources_RemoveSource", "[sources]") SourceDetails details; details.Name = "thisIsTheName"; - details.Type = "thisIsTheType"; + details.Type = SourceType::ConfigurableTest; details.Arg = "thisIsTheArg"; details.Data = "thisIsTheData"; @@ -708,7 +708,7 @@ TEST_CASE("RepoSources_UpdateOnOpen", "[sources]") TestHook_ClearSourceFactoryOverrides(); std::string name = "testName"; - std::string type = "testType"; + SourceType type = SourceType::ConfigurableTest; std::string arg = "testArg"; std::string data = "testData"; @@ -754,7 +754,7 @@ TEST_CASE("RepoSources_DropSourceByName", "[sources]") { INFO("Source #" << i); REQUIRE(sources[i].Name == "testName"s + suffix[i]); - REQUIRE(sources[i].Type == "testType"s + suffix[i]); + REQUIRE(sources[i].Type == SourceType::ConfigurableTest); REQUIRE(sources[i].Arg == "testArg"s + suffix[i]); REQUIRE(sources[i].Data == "testData"s + suffix[i]); REQUIRE(sources[i].LastUpdateTime == ConvertUnixEpochToSystemClock(i + 1)); @@ -855,7 +855,7 @@ TEST_CASE("RepoSources_SearchAcrossMultipleSources", "[sources]") { TestHook_ClearSourceFactoryOverrides(); TestSourceFactory factory{ SourcesTestSource::Create }; - TestHook_SetSourceFactoryOverride("testType", factory); + TestHook_SetSourceFactoryOverride(SourceType::ConfigurableTest, factory); SetSetting(Stream::UserSources, s_TwoSource_AggregateSourceTest); @@ -908,7 +908,7 @@ TEST_CASE("RepoSources_GroupPolicy_DefaultSource", "[sources][groupPolicy]") ProgressCallback progress; SourceDetails details; details.Name = "winget"; - details.Type = "Microsoft.PreIndexed.Package"; + details.Type = SourceType::PreIndexedPackage; details.Arg = "https://cdn.winget.microsoft.com/cache"; REQUIRE_POLICY_EXCEPTION( AddSource(details, progress), @@ -931,7 +931,7 @@ TEST_CASE("RepoSources_GroupPolicy_DefaultSource", "[sources][groupPolicy]") SourceDetails details; details.Name = "winget"; - details.Type = "someType"; + details.Type = SourceType::ConfigurableTest; details.Arg = "notWingetRealArg"; details.Data = "someData"; @@ -965,7 +965,7 @@ TEST_CASE("RepoSources_GroupPolicy_DefaultSource", "[sources][groupPolicy]") REQUIRE(sources.size() == c_DefaultSourceCount); REQUIRE(sources[0].Name == "winget"); - REQUIRE(sources[0].Type == "testType"); + REQUIRE(sources[0].Type == SourceType::ConfigurableTest); REQUIRE(sources[0].Arg == "testArg"); REQUIRE(sources[0].Data == "testData"); REQUIRE(sources[0].Origin == SourceOrigin::User); @@ -1028,7 +1028,7 @@ TEST_CASE("RepoSources_GroupPolicy_AdditionalSources", "[sources][groupPolicy]") { SourceFromPolicy source; source.Name = "name" + suffix[i]; - source.Type = "type" + suffix[i]; + source.Type = std::string{ SourceTypeEnumToString(SourceType::ConfigurableTest) }; source.Arg = "arg" + suffix[i]; source.Data = "data" + suffix[i]; source.Identifier = "id" + suffix[i]; @@ -1047,7 +1047,7 @@ TEST_CASE("RepoSources_GroupPolicy_AdditionalSources", "[sources][groupPolicy]") for (size_t i = 0; i < policySources.size(); ++i) { REQUIRE(sources[i].Name == policySources[i].Name); - REQUIRE(sources[i].Type == policySources[i].Type); + REQUIRE(SourceTypeEnumToString(sources[i].Type) == policySources[i].Type); REQUIRE(sources[i].Arg == policySources[i].Arg); REQUIRE(sources[i].Data == policySources[i].Data); REQUIRE(sources[i].Identifier == policySources[i].Identifier); @@ -1059,7 +1059,7 @@ TEST_CASE("RepoSources_GroupPolicy_AdditionalSources", "[sources][groupPolicy]") // User sources with the same name as an additional source are ignored. SourceFromPolicy policySource; policySource.Name = "testName"; - policySource.Type = "notTestType"; + policySource.Type = std::string{ SourceTypeEnumToString(SourceType::ConfigurableTest) }; policySource.Arg = "notTestArg"; policySource.Data = "notTestData"; policySource.Identifier = "notTestId"; @@ -1074,7 +1074,7 @@ TEST_CASE("RepoSources_GroupPolicy_AdditionalSources", "[sources][groupPolicy]") REQUIRE(sources[1].Origin == SourceOrigin::Default); REQUIRE(sources[0].Name == policySource.Name); - REQUIRE(sources[0].Type == policySource.Type); + REQUIRE(SourceTypeEnumToString(sources[0].Type) == policySource.Type); REQUIRE(sources[0].Arg == policySource.Arg); REQUIRE(sources[0].Data == policySource.Data); REQUIRE(sources[0].Identifier == policySource.Identifier); @@ -1085,7 +1085,7 @@ TEST_CASE("RepoSources_GroupPolicy_AdditionalSources", "[sources][groupPolicy]") // An additional source cannot be removed. SourceFromPolicy policySource; policySource.Name = "name"; - policySource.Type = "type"; + policySource.Type = std::string{ SourceTypeEnumToString(SourceType::ConfigurableTest) }; policySource.Arg = "arg"; policySource.Data = "data"; policySource.Identifier = "id"; @@ -1093,7 +1093,7 @@ TEST_CASE("RepoSources_GroupPolicy_AdditionalSources", "[sources][groupPolicy]") bool removeCalledOnFactory = false; TestSourceFactory factory{ SourcesTestSource::Create }; factory.OnRemove = [&](const SourceDetails&) { removeCalledOnFactory = true; }; - TestHook_SetSourceFactoryOverride(policySource.Type, factory); + TestHook_SetSourceFactoryOverride(ConvertToSourceTypeEnum(policySource.Type), factory); policies.SetValue({ policySource }); SetSetting(Stream::UserSources, s_EmptySources); @@ -1109,7 +1109,7 @@ TEST_CASE("RepoSources_GroupPolicy_AdditionalSources", "[sources][groupPolicy]") // An additional source with the same name as a default overrides it. SourceFromPolicy policySource; policySource.Name = "winget"; - policySource.Type = "notDefaultType"; + policySource.Type = std::string{ SourceTypeEnumToString(SourceType::ConfigurableTest) }; policySource.Arg = "notDefaultArg"; policySource.Data = "notDefaultData"; policySource.Identifier = "notDefaultId"; @@ -1121,7 +1121,7 @@ TEST_CASE("RepoSources_GroupPolicy_AdditionalSources", "[sources][groupPolicy]") REQUIRE(sources.size() == c_DefaultSourceCount); REQUIRE(sources[0].Name == policySource.Name); - REQUIRE(sources[0].Type == policySource.Type); + REQUIRE(SourceTypeEnumToString(sources[0].Type) == policySource.Type); REQUIRE(sources[0].Arg == policySource.Arg); REQUIRE(sources[0].Data == policySource.Data); REQUIRE(sources[0].Identifier == policySource.Identifier); @@ -1142,7 +1142,7 @@ TEST_CASE("RepoSources_GroupPolicy_AllowedSources", "[sources][groupPolicy]") // We should be able to add sources in the allow list. SourceFromPolicy policySource; policySource.Name = "testName"; - policySource.Type = "testType"; + policySource.Type = std::string{ SourceTypeEnumToString(SourceType::ConfigurableTest) }; policySource.Arg = "testArg"; policySource.Data = "testData"; policySource.Identifier = "testId"; @@ -1159,12 +1159,12 @@ TEST_CASE("RepoSources_GroupPolicy_AllowedSources", "[sources][groupPolicy]") sd.Data = policySource.Data; sd.Identifier = policySource.Identifier; }; - TestHook_SetSourceFactoryOverride(policySource.Type, factory); + TestHook_SetSourceFactoryOverride(ConvertToSourceTypeEnum(policySource.Type), factory); ProgressCallback progress; SourceDetails details; details.Name = policySource.Name; - details.Type = policySource.Type; + details.Type = SourceType::ConfigurableTest; details.Arg = policySource.Arg; AddSource(details, progress); @@ -1176,7 +1176,7 @@ TEST_CASE("RepoSources_GroupPolicy_AllowedSources", "[sources][groupPolicy]") REQUIRE(sources[1].Origin == SourceOrigin::Default); REQUIRE(sources[0].Name == policySource.Name); - REQUIRE(sources[0].Type == policySource.Type); + REQUIRE(SourceTypeEnumToString(sources[0].Type) == policySource.Type); REQUIRE(sources[0].Arg == policySource.Arg); REQUIRE(sources[0].Data == policySource.Data); REQUIRE(sources[0].Identifier == policySource.Identifier); @@ -1187,7 +1187,7 @@ TEST_CASE("RepoSources_GroupPolicy_AllowedSources", "[sources][groupPolicy]") // We should not be allowed to add anything not matching the allow list. SourceFromPolicy policySource; policySource.Name = "testName"; - policySource.Type = "testType"; + policySource.Type = std::string{ SourceTypeEnumToString(SourceType::ConfigurableTest) }; policySource.Arg = "testArg"; policySource.Data = "testData"; policySource.Identifier = "testId"; @@ -1198,7 +1198,7 @@ TEST_CASE("RepoSources_GroupPolicy_AllowedSources", "[sources][groupPolicy]") ProgressCallback progress; SourceDetails details; details.Name = "notAllowed"; - details.Type = "type"; + details.Type = SourceType::ConfigurableTest; details.Arg = "arg"; bool addCalledOnFactory = false; @@ -1225,7 +1225,7 @@ TEST_CASE("RepoSources_GroupPolicy_AllowedSources", "[sources][groupPolicy]") ProgressCallback progress; SourceDetails details; details.Name = "name"; - details.Type = "type"; + details.Type = SourceType::ConfigurableTest; details.Arg = "arg"; bool addCalledOnFactory = false; @@ -1257,7 +1257,7 @@ TEST_CASE("RepoSources_OpenMultipleWithSingleFailure", "[sources]") { TestHook_ClearSourceFactoryOverrides(); TestSourceFactory factory{ FailingSourcesTestSource::CreateFailWinget }; - TestHook_SetSourceFactoryOverride("testType", factory); + TestHook_SetSourceFactoryOverride(SourceType::ConfigurableTest, factory); SetSetting(Stream::UserSources, s_TwoSource_AggregateSourceTest); @@ -1288,7 +1288,7 @@ TEST_CASE("RepoSources_OpenMultipleWithTotalFailure", "[sources]") { TestHook_ClearSourceFactoryOverrides(); TestSourceFactory factory{ FailingSourcesTestSource::CreateFailAll }; - TestHook_SetSourceFactoryOverride("testType", factory); + TestHook_SetSourceFactoryOverride(SourceType::ConfigurableTest, factory); SetSetting(Stream::UserSources, s_TwoSource_AggregateSourceTest); @@ -1309,7 +1309,7 @@ TEST_CASE("RepoSources_UpdateSettingsDuringAction_SourcesUpdate", "[sources]") std::string unusedSourceName = "unusedName"; std::string unusedSourceArg = "unusedArg"; - std::string testSourceType = "testType"; + SourceType testSourceType = SourceType::ConfigurableTest; TestHook_ClearSourceFactoryOverrides(); TestSourceFactory factory{ FailingSourcesTestSource::CreateFailAll }; @@ -1392,7 +1392,7 @@ TEST_CASE("RepoSources_UpdateSettingsDuringAction_MetadataUpdate", "[sources]") std::string unusedSourceName = "unusedName"; std::string unusedSourceArg = "unusedArg"; - std::string testSourceType = "testType"; + SourceType testSourceType = SourceType::ConfigurableTest; TestHook_ClearSourceFactoryOverrides(); TestSourceFactory factory{ FailingSourcesTestSource::CreateFailAll }; @@ -1462,7 +1462,12 @@ TEST_CASE("RepoSources_RestoringWellKnownSource", "[sources]") SECTION("with well known name") { - Source addStoreBack{ details.Name, details.Arg, details.Type, Repository::SourceTrustLevel::None, {} }; + Source addStoreBack{ + details.Name, + details.Arg, + details.Type, + Repository::SourceTrustLevel::None, + {} }; REQUIRE(addStoreBack.Add(progress)); Source storeAfterAdd{ details.Name }; @@ -1473,7 +1478,12 @@ TEST_CASE("RepoSources_RestoringWellKnownSource", "[sources]") SECTION("with different name") { std::string newName = details.Name + "_new"; - Source addStoreBack{ newName, details.Arg, details.Type, Repository::SourceTrustLevel::None, {} }; + Source addStoreBack{ + newName, + details.Arg, + details.Type, + Repository::SourceTrustLevel::None, + {} }; REQUIRE(addStoreBack.Add(progress)); Source storeAfterAdd{ newName }; @@ -1537,3 +1547,24 @@ TEST_CASE("RepoSources_MicrosoftStore_CertificatePinningLifetimeCheck", "[source double lifetimePercentage = source.GetDetails().CertificatePinningConfiguration.GetRemainingLifetimePercentage(); REQUIRE(lifetimePercentage > 0.25); } + +TEST_CASE("RepoSources_SourceTypeEnumConversion", "[sources]") +{ + REQUIRE(ConvertToSourceTypeEnum("Microsoft.PreIndexed.Package"sv) == SourceType::PreIndexedPackage); + REQUIRE(ConvertToSourceTypeEnum("microsoft.rest"sv) == SourceType::Rest); + REQUIRE(ConvertToSourceTypeEnum("Microsoft.Predefined.Installed"sv) == SourceType::PredefinedInstalled); + REQUIRE(ConvertToSourceTypeEnum("Microsoft.Predefined.Writeable"sv) == SourceType::PredefinedWriteable); + REQUIRE(ConvertToSourceTypeEnum("Microsoft.PackageTracking"sv) == SourceType::PackageTracking); +#ifndef AICLI_DISABLE_TEST_HOOKS + REQUIRE(ConvertToSourceTypeEnum("Microsoft.Test.Configurable"sv) == SourceType::ConfigurableTest); +#endif + + REQUIRE(TryConvertToSourceTypeEnum("Microsoft.Unknown"sv) == std::nullopt); + REQUIRE_THROWS_HR(ConvertToSourceTypeEnum("Microsoft.Unknown"sv), APPINSTALLER_CLI_ERROR_INVALID_SOURCE_TYPE); + + REQUIRE(TryConvertToSourceTypeEnum(""sv).value_or(Source::GetDefaultSourceType()) == Source::GetDefaultSourceType()); + REQUIRE(SourceTypeEnumToString(SourceType::PreIndexedPackage) == "Microsoft.PreIndexed.Package"sv); + REQUIRE(SourceTypeEnumToString(SourceType::Rest) == "Microsoft.Rest"sv); + REQUIRE(Source::GetDefaultSourceType() == SourceType::PreIndexedPackage); + REQUIRE(SourceTypeEnumToString(Source::GetDefaultSourceType()) == "Microsoft.PreIndexed.Package"sv); +} diff --git a/src/AppInstallerCLITests/TestData/ImportFile-Bad-UnknownPackage.json b/src/AppInstallerCLITests/TestData/ImportFile-Bad-UnknownPackage.json index 7c22a36dd3..631768531e 100644 --- a/src/AppInstallerCLITests/TestData/ImportFile-Bad-UnknownPackage.json +++ b/src/AppInstallerCLITests/TestData/ImportFile-Bad-UnknownPackage.json @@ -17,7 +17,7 @@ "Argument": "//arg", "Identifier": "*TestSource", "Name": "TestSource", - "Type": "Microsoft.TestSource" + "Type": "Microsoft.Test.Configurable" } } ], diff --git a/src/AppInstallerCLITests/TestData/ImportFile-Bad-UnknownPackageVersion.json b/src/AppInstallerCLITests/TestData/ImportFile-Bad-UnknownPackageVersion.json index ebc0626b1f..80d3099e7d 100644 --- a/src/AppInstallerCLITests/TestData/ImportFile-Bad-UnknownPackageVersion.json +++ b/src/AppInstallerCLITests/TestData/ImportFile-Bad-UnknownPackageVersion.json @@ -13,7 +13,7 @@ "Argument": "//arg", "Identifier": "*TestSource", "Name": "TestSource", - "Type": "Microsoft.TestSource" + "Type": "Microsoft.Test.Configurable" } } ], diff --git a/src/AppInstallerCLITests/TestData/ImportFile-Bad-UnknownSource.json b/src/AppInstallerCLITests/TestData/ImportFile-Bad-UnknownSource.json index 7d66f87f98..5fd78f6d62 100644 --- a/src/AppInstallerCLITests/TestData/ImportFile-Bad-UnknownSource.json +++ b/src/AppInstallerCLITests/TestData/ImportFile-Bad-UnknownSource.json @@ -13,7 +13,7 @@ "Argument": "//bad", "Identifier": "*BadSource", "Name": "TestSource", - "Type": "Microsoft.TestSource" + "Type": "Microsoft.Test.Configurable" } } ], diff --git a/src/AppInstallerCLITests/TestData/ImportFile-Good-AlreadyInstalled.json b/src/AppInstallerCLITests/TestData/ImportFile-Good-AlreadyInstalled.json index 137c2d9366..ebdbddd0ba 100644 --- a/src/AppInstallerCLITests/TestData/ImportFile-Good-AlreadyInstalled.json +++ b/src/AppInstallerCLITests/TestData/ImportFile-Good-AlreadyInstalled.json @@ -13,7 +13,7 @@ "Argument": "//arg", "Identifier": "*TestSource", "Name": "TestSource", - "Type": "Microsoft.TestSource" + "Type": "Microsoft.Test.Configurable" } } ], diff --git a/src/AppInstallerCLITests/TestData/ImportFile-Good-Dependencies.json b/src/AppInstallerCLITests/TestData/ImportFile-Good-Dependencies.json index e1e1a5ac1a..0c9c7f073e 100644 --- a/src/AppInstallerCLITests/TestData/ImportFile-Good-Dependencies.json +++ b/src/AppInstallerCLITests/TestData/ImportFile-Good-Dependencies.json @@ -17,7 +17,7 @@ "Argument": "//arg", "Identifier": "*TestSource", "Name": "TestSource", - "Type": "Microsoft.TestSource" + "Type": "Microsoft.Test.Configurable" } } ], diff --git a/src/AppInstallerCLITests/TestData/ImportFile-Good-MachineScope.json b/src/AppInstallerCLITests/TestData/ImportFile-Good-MachineScope.json index a80b455e47..0a95371140 100644 --- a/src/AppInstallerCLITests/TestData/ImportFile-Good-MachineScope.json +++ b/src/AppInstallerCLITests/TestData/ImportFile-Good-MachineScope.json @@ -14,7 +14,7 @@ "Argument": "//arg", "Identifier": "*TestSource", "Name": "TestSource", - "Type": "Microsoft.TestSource" + "Type": "Microsoft.Test.Configurable" } } ], diff --git a/src/AppInstallerCLITests/TestData/ImportFile-Good-WithCustomSwitches.json b/src/AppInstallerCLITests/TestData/ImportFile-Good-WithCustomSwitches.json index 5271e795be..702b79a0d1 100644 --- a/src/AppInstallerCLITests/TestData/ImportFile-Good-WithCustomSwitches.json +++ b/src/AppInstallerCLITests/TestData/ImportFile-Good-WithCustomSwitches.json @@ -13,7 +13,7 @@ "Argument": "//arg", "Identifier": "*TestSource", "Name": "TestSource", - "Type": "Microsoft.TestSource" + "Type": "Microsoft.Test.Configurable" } } ], diff --git a/src/AppInstallerCLITests/TestData/ImportFile-Good-WithLicenseAgreement.json b/src/AppInstallerCLITests/TestData/ImportFile-Good-WithLicenseAgreement.json index e035111554..5530376f79 100644 --- a/src/AppInstallerCLITests/TestData/ImportFile-Good-WithLicenseAgreement.json +++ b/src/AppInstallerCLITests/TestData/ImportFile-Good-WithLicenseAgreement.json @@ -13,7 +13,7 @@ "Argument": "//arg", "Identifier": "*TestSource", "Name": "TestSource", - "Type": "Microsoft.TestSource" + "Type": "Microsoft.Test.Configurable" } } ], diff --git a/src/AppInstallerCLITests/TestData/ImportFile-Good-WithOverrideArgs.json b/src/AppInstallerCLITests/TestData/ImportFile-Good-WithOverrideArgs.json index 6f5309d556..5b1b5e8ad1 100644 --- a/src/AppInstallerCLITests/TestData/ImportFile-Good-WithOverrideArgs.json +++ b/src/AppInstallerCLITests/TestData/ImportFile-Good-WithOverrideArgs.json @@ -13,7 +13,7 @@ "Argument": "//arg", "Identifier": "*TestSource", "Name": "TestSource", - "Type": "Microsoft.TestSource" + "Type": "Microsoft.Test.Configurable" } } ], diff --git a/src/AppInstallerCLITests/TestData/ImportFile-Good.json b/src/AppInstallerCLITests/TestData/ImportFile-Good.json index de132544e6..a154cb5c50 100644 --- a/src/AppInstallerCLITests/TestData/ImportFile-Good.json +++ b/src/AppInstallerCLITests/TestData/ImportFile-Good.json @@ -17,7 +17,7 @@ "Argument": "//arg", "Identifier": "*TestSource", "Name": "TestSource", - "Type": "Microsoft.TestSource" + "Type": "Microsoft.Test.Configurable" } } ], diff --git a/src/AppInstallerCLITests/TestHooks.h b/src/AppInstallerCLITests/TestHooks.h index 8c976a1381..e1617ae543 100644 --- a/src/AppInstallerCLITests/TestHooks.h +++ b/src/AppInstallerCLITests/TestHooks.h @@ -38,7 +38,7 @@ namespace AppInstaller namespace Repository { - void TestHook_SetSourceFactoryOverride(const std::string& type, std::function()>&& factory); + void TestHook_SetSourceFactoryOverride(SourceType type, std::function()>&& factory); void TestHook_ClearSourceFactoryOverrides(); void TestHook_SetExtractIconFromArpEntryResult_Override(std::vector* result); } diff --git a/src/AppInstallerCLITests/TestSource.cpp b/src/AppInstallerCLITests/TestSource.cpp index f864b95a98..18f4bd20f4 100644 --- a/src/AppInstallerCLITests/TestSource.cpp +++ b/src/AppInstallerCLITests/TestSource.cpp @@ -492,7 +492,12 @@ namespace TestCommon Repository::SourceEdit additionalProperties; additionalProperties.Explicit = details.Explicit; additionalProperties.Priority = details.Priority; - Repository::Source source{ details.Name, details.Arg, details.Type, Repository::SourceTrustLevel::None, additionalProperties }; + Repository::Source source{ + details.Name, + details.Arg, + details.Type, + Repository::SourceTrustLevel::None, + additionalProperties }; return source.Add(progress); } diff --git a/src/AppInstallerCLITests/TestSource.h b/src/AppInstallerCLITests/TestSource.h index c6ced92427..a4af9fa1de 100644 --- a/src/AppInstallerCLITests/TestSource.h +++ b/src/AppInstallerCLITests/TestSource.h @@ -128,7 +128,7 @@ namespace TestCommon AppInstaller::Repository::SearchResult Search(const AppInstaller::Repository::SearchRequest& request) const override; void* CastTo(AppInstaller::Repository::ISourceType type) override; - AppInstaller::Repository::SourceDetails Details = { "TestSource", "Microsoft.TestSource", "//arg", "", "*TestSource" }; + AppInstaller::Repository::SourceDetails Details = { "TestSource", AppInstaller::Repository::SourceType::ConfigurableTest, "//arg", "", "*TestSource" }; AppInstaller::Repository::SourceInformation Information; std::function SearchFunction; diff --git a/src/AppInstallerRepositoryCore/Microsoft/PreIndexedPackageSourceFactory.cpp b/src/AppInstallerRepositoryCore/Microsoft/PreIndexedPackageSourceFactory.cpp index 53a73e1835..549fa3afcf 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/PreIndexedPackageSourceFactory.cpp +++ b/src/AppInstallerRepositoryCore/Microsoft/PreIndexedPackageSourceFactory.cpp @@ -199,8 +199,7 @@ namespace AppInstaller::Repository::Microsoft std::shared_ptr Create(const SourceDetails& details) override final { - // With more than one source implementation, we will probably need to probe first - THROW_HR_IF(E_INVALIDARG, !details.Type.empty() && details.Type != PreIndexedPackageSourceFactory::Type()); + THROW_HR_IF(E_INVALIDARG, details.Type != SourceType::PreIndexedPackage); return CreateInternal(details); } @@ -209,16 +208,9 @@ namespace AppInstaller::Repository::Microsoft bool Add(SourceDetails& details, IProgressCallback& progress) override final { - if (details.Type.empty()) - { - // With more than one source implementation, we will probably need to probe first - details.Type = PreIndexedPackageSourceFactory::Type(); - AICLI_LOG(Repo, Info, << "Initializing source type: " << details.Name << " => " << details.Type); - } - else - { - THROW_HR_IF(E_INVALIDARG, details.Type != PreIndexedPackageSourceFactory::Type()); - } + // Source type is normalized before reaching the factory; this path only accepts PreIndexedPackage. + THROW_HR_IF(E_INVALIDARG, details.Type != SourceType::PreIndexedPackage); + AICLI_LOG(Repo, Info, << "Initializing source type: " << details.Name << " => " << SourceTypeEnumToString(details.Type)); PreIndexedPackageInfo packageInfo(details, [](const std::string& packageLocation) { @@ -286,7 +278,7 @@ namespace AppInstaller::Repository::Microsoft bool Remove(const SourceDetails& details, IProgressCallback& progress) override final { - THROW_HR_IF(E_INVALIDARG, details.Type != PreIndexedPackageSourceFactory::Type()); + THROW_HR_IF(E_INVALIDARG, details.Type != SourceType::PreIndexedPackage); auto lock = LockExclusive(details, progress); if (!lock) { @@ -318,7 +310,7 @@ namespace AppInstaller::Repository::Microsoft bool UpdateBase(const SourceDetails& details, bool isBackground, IProgressCallback& progress) { - THROW_HR_IF(E_INVALIDARG, details.Type != PreIndexedPackageSourceFactory::Type()); + THROW_HR_IF(E_INVALIDARG, details.Type != SourceType::PreIndexedPackage); std::optional currentVersion = GetCurrentVersion(details); PreIndexedPackageUpdateCheck updateCheck(details); diff --git a/src/AppInstallerRepositoryCore/Microsoft/PredefinedInstalledSourceFactory.cpp b/src/AppInstallerRepositoryCore/Microsoft/PredefinedInstalledSourceFactory.cpp index 40edd79e16..321801adcc 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/PredefinedInstalledSourceFactory.cpp +++ b/src/AppInstallerRepositoryCore/Microsoft/PredefinedInstalledSourceFactory.cpp @@ -423,7 +423,7 @@ namespace AppInstaller::Repository::Microsoft std::shared_ptr Create(const SourceDetails& details) override final { - THROW_HR_IF(E_INVALIDARG, details.Type != PredefinedInstalledSourceFactory::Type()); + THROW_HR_IF(E_INVALIDARG, details.Type != SourceType::PredefinedInstalled); return std::make_shared(details); } diff --git a/src/AppInstallerRepositoryCore/Microsoft/PredefinedWriteableSourceFactory.cpp b/src/AppInstallerRepositoryCore/Microsoft/PredefinedWriteableSourceFactory.cpp index 23c645a485..a0efae604f 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/PredefinedWriteableSourceFactory.cpp +++ b/src/AppInstallerRepositoryCore/Microsoft/PredefinedWriteableSourceFactory.cpp @@ -85,7 +85,7 @@ namespace AppInstaller::Repository::Microsoft std::shared_ptr PredefinedWriteableSourceFactoryImpl::Create(const SourceDetails& details) { - THROW_HR_IF(E_INVALIDARG, details.Type != PredefinedWriteableSourceFactory::Type()); + THROW_HR_IF(E_INVALIDARG, details.Type != SourceType::PredefinedWriteable); return std::make_shared(details); } diff --git a/src/AppInstallerRepositoryCore/PackageTrackingCatalog.cpp b/src/AppInstallerRepositoryCore/PackageTrackingCatalog.cpp index bcbfb91292..188c0a326e 100644 --- a/src/AppInstallerRepositoryCore/PackageTrackingCatalog.cpp +++ b/src/AppInstallerRepositoryCore/PackageTrackingCatalog.cpp @@ -110,7 +110,7 @@ namespace AppInstaller::Repository std::shared_ptr Create(const SourceDetails& details) override final { - THROW_HR_IF(E_INVALIDARG, !Utility::CaseInsensitiveEquals(details.Type, PackageTrackingCatalogSourceFactory::Type())); + THROW_HR_IF(E_INVALIDARG, details.Type != SourceType::PackageTracking); return std::make_shared(details); } @@ -127,7 +127,7 @@ namespace AppInstaller::Repository bool Remove(const SourceDetails& details, IProgressCallback& progress) override final { - THROW_HR_IF(E_INVALIDARG, !Utility::CaseInsensitiveEquals(details.Type, PackageTrackingCatalogSourceFactory::Type())); + THROW_HR_IF(E_INVALIDARG, details.Type != SourceType::PackageTracking); std::string pathName = Utility::MakeSuitablePathPart(details.Data); @@ -172,7 +172,7 @@ namespace AppInstaller::Repository // Create fake details for the source while stashing some information that might be helpful for debugging SourceDetails details; - details.Type = PackageTrackingCatalogSourceFactory::Type(); + details.Type = SourceType::PackageTracking; details.Identifier = "*Tracking"; details.Name = "Tracking for "s + source.GetDetails().Name; details.Origin = SourceOrigin::PackageTracking; @@ -182,7 +182,7 @@ namespace AppInstaller::Repository PackageTrackingCatalog result; result.m_implementation = std::make_shared(); - result.m_implementation->Source = SourceCast(ISourceFactory::GetForType(details.Type)->Create(details)->Open(dummyProgress)); + result.m_implementation->Source = SourceCast(ISourceFactory::GetForType(SourceType::PackageTracking)->Create(details)->Open(dummyProgress)); return result; } @@ -196,12 +196,12 @@ namespace AppInstaller::Repository // Create details to pass to the factory; the identifier of the source is passed in the Data field. SourceDetails dummyDetails; - dummyDetails.Type = PackageTrackingCatalogSourceFactory::Type(); + dummyDetails.Type = SourceType::PackageTracking; dummyDetails.Data = identifier; ProgressCallback dummyProgress; - ISourceFactory::GetForType(dummyDetails.Type)->Remove(dummyDetails, dummyProgress); + ISourceFactory::GetForType(SourceType::PackageTracking)->Remove(dummyDetails, dummyProgress); } PackageTrackingCatalog::operator bool() const diff --git a/src/AppInstallerRepositoryCore/Public/winget/RepositorySource.h b/src/AppInstallerRepositoryCore/Public/winget/RepositorySource.h index f772a8f8a1..3c277de440 100644 --- a/src/AppInstallerRepositoryCore/Public/winget/RepositorySource.h +++ b/src/AppInstallerRepositoryCore/Public/winget/RepositorySource.h @@ -61,6 +61,26 @@ namespace AppInstaller::Repository // Gets the full trust level string name for display. std::string GetSourceTrustLevelForDisplay(SourceTrustLevel trustLevel); + // Defines known source types. + enum class SourceType + { + PreIndexedPackage, + Rest, + PredefinedInstalled, + PredefinedWriteable, + PackageTracking, + ConfigurableTest, + }; + + // Converts a source type string to the corresponding SourceType enum. + SourceType ConvertToSourceTypeEnum(std::string_view sourceType); + + // Attempts to convert a source type string to the corresponding SourceType enum. + std::optional TryConvertToSourceTypeEnum(std::string_view sourceType); + + // Converts a SourceType enum to the corresponding canonical string. + std::string_view SourceTypeEnumToString(SourceType sourceType); + std::string_view ToString(SourceOrigin origin); // Fields that require user agreements. @@ -120,7 +140,9 @@ namespace AppInstaller::Repository std::string Name; // The type of the source. - std::string Type; + // Defaults to PreIndexedPackage if not specified, moved here from call sites during migration from + // String type to Enum type to avoid conversion in multiple places. + SourceType Type = SourceType::PreIndexedPackage; // The argument used when adding the source. std::string Arg; @@ -240,7 +262,7 @@ namespace AppInstaller::Repository Source(WellKnownSource source); // Constructor for a source to be added. - Source(std::string_view name, std::string_view arg, std::string_view type, SourceTrustLevel trustLevel, const SourceEdit& additionalProperties); + Source(std::string_view name, std::string_view arg, SourceType type, SourceTrustLevel trustLevel, const SourceEdit& additionalProperties); // Constructor for creating a composite source from a list of available sources. Source(const std::vector& availableSources); @@ -373,7 +395,7 @@ namespace AppInstaller::Repository static std::vector GetCurrentSources(); // Get a default source type is the source type used when adding a source without specifying a type. - static std::string_view GetDefaultSourceType(); + static SourceType GetDefaultSourceType(); private: void InitializeSourceReference(std::string_view name); diff --git a/src/AppInstallerRepositoryCore/RepositorySource.cpp b/src/AppInstallerRepositoryCore/RepositorySource.cpp index ac273fc0c8..dc7bdf26f1 100644 --- a/src/AppInstallerRepositoryCore/RepositorySource.cpp +++ b/src/AppInstallerRepositoryCore/RepositorySource.cpp @@ -28,7 +28,7 @@ namespace AppInstaller::Repository namespace { #ifndef AICLI_DISABLE_TEST_HOOKS - static std::map()>> s_Sources_TestHook_SourceFactories; + static std::map()>> s_Sources_TestHook_SourceFactories; #endif std::shared_ptr CreateSourceFromDetails(const SourceDetails& details) @@ -160,31 +160,31 @@ namespace AppInstaller::Repository switch (source) { case PredefinedSource::Installed: - details.Type = Microsoft::PredefinedInstalledSourceFactory::Type(); + details.Type = SourceType::PredefinedInstalled; details.Arg = Microsoft::PredefinedInstalledSourceFactory::FilterToString(Microsoft::PredefinedInstalledSourceFactory::Filter::None); return details; case PredefinedSource::InstalledForceCacheUpdate: - details.Type = Microsoft::PredefinedInstalledSourceFactory::Type(); + details.Type = SourceType::PredefinedInstalled; details.Arg = Microsoft::PredefinedInstalledSourceFactory::FilterToString(Microsoft::PredefinedInstalledSourceFactory::Filter::NoneWithForcedCacheUpdate); return details; case PredefinedSource::InstalledUser: - details.Type = Microsoft::PredefinedInstalledSourceFactory::Type(); + details.Type = SourceType::PredefinedInstalled; details.Arg = Microsoft::PredefinedInstalledSourceFactory::FilterToString(Microsoft::PredefinedInstalledSourceFactory::Filter::User); return details; case PredefinedSource::InstalledMachine: - details.Type = Microsoft::PredefinedInstalledSourceFactory::Type(); + details.Type = SourceType::PredefinedInstalled; details.Arg = Microsoft::PredefinedInstalledSourceFactory::FilterToString(Microsoft::PredefinedInstalledSourceFactory::Filter::Machine); return details; case PredefinedSource::ARP: - details.Type = Microsoft::PredefinedInstalledSourceFactory::Type(); + details.Type = SourceType::PredefinedInstalled; details.Arg = Microsoft::PredefinedInstalledSourceFactory::FilterToString(Microsoft::PredefinedInstalledSourceFactory::Filter::ARP); return details; case PredefinedSource::MSIX: - details.Type = Microsoft::PredefinedInstalledSourceFactory::Type(); + details.Type = SourceType::PredefinedInstalled; details.Arg = Microsoft::PredefinedInstalledSourceFactory::FilterToString(Microsoft::PredefinedInstalledSourceFactory::Filter::MSIX); return details; case PredefinedSource::Installing: - details.Type = Microsoft::PredefinedWriteableSourceFactory::Type(); + details.Type = SourceType::PredefinedWriteable; // As long as there is only one type this is not particularly needed, but Arg is exposed publicly // so this is used here for consistency with other predefined sources. details.Arg = Microsoft::PredefinedWriteableSourceFactory::TypeToString(Microsoft::PredefinedWriteableSourceFactory::WriteableType::Installing); @@ -276,46 +276,106 @@ namespace AppInstaller::Repository }; } - std::unique_ptr ISourceFactory::GetForType(std::string_view type) + std::unique_ptr ISourceFactory::GetForType(SourceType type) { #ifndef AICLI_DISABLE_TEST_HOOKS // Tests can ensure case matching - auto itr = s_Sources_TestHook_SourceFactories.find(std::string(type)); + auto itr = s_Sources_TestHook_SourceFactories.find(type); if (itr != s_Sources_TestHook_SourceFactories.end()) { return itr->second(); } +#endif - if (Utility::CaseInsensitiveEquals(Microsoft::ConfigurableTestSourceFactory::Type(), type)) + switch (type) { + case SourceType::PreIndexedPackage: + return Microsoft::PreIndexedPackageSourceFactory::Create(); + case SourceType::Rest: + return Rest::RestSourceFactory::Create(); + case SourceType::PredefinedInstalled: + return Microsoft::PredefinedInstalledSourceFactory::Create(); + case SourceType::PredefinedWriteable: + return Microsoft::PredefinedWriteableSourceFactory::Create(); + case SourceType::PackageTracking: + return PackageTrackingCatalogSourceFactory::Create(); +#ifndef AICLI_DISABLE_TEST_HOOKS + case SourceType::ConfigurableTest: return Microsoft::ConfigurableTestSourceFactory::Create(); - } +#else + case SourceType::ConfigurableTest: + break; #endif + } - // For now, enable an empty type to represent the only one we have. - if (type.empty() || - Utility::CaseInsensitiveEquals(Microsoft::PreIndexedPackageSourceFactory::Type(), type)) + THROW_HR(APPINSTALLER_CLI_ERROR_INVALID_SOURCE_TYPE); + } + + std::optional TryConvertToSourceTypeEnum(std::string_view sourceType) + { + if (Utility::CaseInsensitiveEquals(Microsoft::PreIndexedPackageSourceFactory::Type(), sourceType)) { - return Microsoft::PreIndexedPackageSourceFactory::Create(); + return SourceType::PreIndexedPackage; } - // Should always come from code, so no need for case insensitivity - else if (Microsoft::PredefinedInstalledSourceFactory::Type() == type) + + if (Utility::CaseInsensitiveEquals(Rest::RestSourceFactory::Type(), sourceType)) { - return Microsoft::PredefinedInstalledSourceFactory::Create(); + return SourceType::Rest; } - // Should always come from code, so no need for case insensitivity - else if (Microsoft::PredefinedWriteableSourceFactory::Type() == type) + + // Internal source types are code-defined and expected in canonical case. + if (Microsoft::PredefinedInstalledSourceFactory::Type() == sourceType) { - return Microsoft::PredefinedWriteableSourceFactory::Create(); + return SourceType::PredefinedInstalled; } - // Should always come from code, so no need for case insensitivity - else if (PackageTrackingCatalogSourceFactory::Type() == type) + + if (Microsoft::PredefinedWriteableSourceFactory::Type() == sourceType) { - return PackageTrackingCatalogSourceFactory::Create(); + return SourceType::PredefinedWriteable; } - else if (Utility::CaseInsensitiveEquals(Rest::RestSourceFactory::Type(), type)) + + if (PackageTrackingCatalogSourceFactory::Type() == sourceType) { - return Rest::RestSourceFactory::Create(); + return SourceType::PackageTracking; + } + +#ifndef AICLI_DISABLE_TEST_HOOKS + if (Utility::CaseInsensitiveEquals(Microsoft::ConfigurableTestSourceFactory::Type(), sourceType)) + { + return SourceType::ConfigurableTest; + } +#endif + + return {}; + } + + SourceType ConvertToSourceTypeEnum(std::string_view sourceType) + { + auto sourceTypeEnum = TryConvertToSourceTypeEnum(sourceType); + THROW_HR_IF(APPINSTALLER_CLI_ERROR_INVALID_SOURCE_TYPE, !sourceTypeEnum.has_value()); + return sourceTypeEnum.value(); + } + + std::string_view SourceTypeEnumToString(SourceType sourceType) + { + switch (sourceType) + { + case SourceType::PreIndexedPackage: + return Microsoft::PreIndexedPackageSourceFactory::Type(); + case SourceType::Rest: + return Rest::RestSourceFactory::Type(); + case SourceType::PredefinedInstalled: + return Microsoft::PredefinedInstalledSourceFactory::Type(); + case SourceType::PredefinedWriteable: + return Microsoft::PredefinedWriteableSourceFactory::Type(); + case SourceType::PackageTracking: + return PackageTrackingCatalogSourceFactory::Type(); + case SourceType::ConfigurableTest: +#ifndef AICLI_DISABLE_TEST_HOOKS + return Microsoft::ConfigurableTestSourceFactory::Type(); +#else + break; +#endif } THROW_HR(APPINSTALLER_CLI_ERROR_INVALID_SOURCE_TYPE); @@ -462,11 +522,10 @@ namespace AppInstaller::Repository m_sourceReferences.emplace_back(CreateSourceFromDetails(details)); } - Source::Source(std::string_view name, std::string_view arg, std::string_view type, SourceTrustLevel trustLevel, const SourceEdit& additionalProperties) + Source::Source(std::string_view name, std::string_view arg, SourceType type, SourceTrustLevel trustLevel, const SourceEdit& additionalProperties) { m_isSourceToBeAdded = true; SourceDetails details; - std::optional wellKnownSourceCheck = CheckForWellKnownSourceMatch(name, arg, type); if (wellKnownSourceCheck) @@ -907,14 +966,8 @@ namespace AppInstaller::Repository auto& sourceDetails = m_sourceReferences[0]->GetDetails(); - // If the source type is empty, use a default. - // AddSourceForDetails will also check for empty, but we need the actual type before that for validation. - if (sourceDetails.Type.empty()) - { - sourceDetails.Type = GetDefaultSourceType(); - } - - AICLI_LOG(Repo, Info, << "Adding source: Name[" << sourceDetails.Name << "], Type[" << sourceDetails.Type << "], Arg[" << sourceDetails.Arg << "]"); + // If the source type was empty, the constructor of sourceDetails will have defaulted it + AICLI_LOG(Repo, Info, << "Adding source: Name[" << sourceDetails.Name << "], Type[" << SourceTypeEnumToString(sourceDetails.Type) << "], Arg[" << sourceDetails.Arg << "]"); // Check all sources for the given name. SourceList sourceList; @@ -1131,13 +1184,13 @@ namespace AppInstaller::Repository } } - std::string_view Source::GetDefaultSourceType() + SourceType Source::GetDefaultSourceType() { - return ISourceFactory::GetForType("")->TypeName(); + return SourceType::PreIndexedPackage; } #ifndef AICLI_DISABLE_TEST_HOOKS - void TestHook_SetSourceFactoryOverride(const std::string& type, std::function()>&& factory) + void TestHook_SetSourceFactoryOverride(SourceType type, std::function()>&& factory) { s_Sources_TestHook_SourceFactories[type] = std::move(factory); } diff --git a/src/AppInstallerRepositoryCore/Rest/RestSourceFactory.cpp b/src/AppInstallerRepositoryCore/Rest/RestSourceFactory.cpp index 8074f348b8..dc9e2001fb 100644 --- a/src/AppInstallerRepositoryCore/Rest/RestSourceFactory.cpp +++ b/src/AppInstallerRepositoryCore/Rest/RestSourceFactory.cpp @@ -117,21 +117,15 @@ namespace AppInstaller::Repository::Rest std::shared_ptr Create(const SourceDetails& details) override final { - THROW_HR_IF(E_INVALIDARG, !Utility::CaseInsensitiveEquals(details.Type, RestSourceFactory::Type())); + THROW_HR_IF(E_INVALIDARG, details.Type != SourceType::Rest); return std::make_shared(details); } bool Add(SourceDetails& details, IProgressCallback&) override final { - if (details.Type.empty()) - { - details.Type = RestSourceFactory::Type(); - } - else - { - THROW_HR_IF(E_INVALIDARG, !Utility::CaseInsensitiveEquals(details.Type, RestSourceFactory::Type())); - } + // Source type is normalized before reaching the factory; this path only accepts Rest. + THROW_HR_IF(E_INVALIDARG, details.Type != SourceType::Rest); // Check if URL is remote and secure THROW_HR_IF(APPINSTALLER_CLI_ERROR_SOURCE_NOT_REMOTE, !Utility::IsUrlRemote(details.Arg)); @@ -142,13 +136,13 @@ namespace AppInstaller::Repository::Rest bool Update(const SourceDetails& details, IProgressCallback&) override final { - THROW_HR_IF(E_INVALIDARG, !Utility::CaseInsensitiveEquals(details.Type, RestSourceFactory::Type())); + THROW_HR_IF(E_INVALIDARG, details.Type != SourceType::Rest); return true; } bool Remove(const SourceDetails& details, IProgressCallback&) override final { - THROW_HR_IF(E_INVALIDARG, !Utility::CaseInsensitiveEquals(details.Type, RestSourceFactory::Type())); + THROW_HR_IF(E_INVALIDARG, details.Type != SourceType::Rest); return true; } }; diff --git a/src/AppInstallerRepositoryCore/SourceFactory.h b/src/AppInstallerRepositoryCore/SourceFactory.h index 85a6ffd25a..aafb371f0c 100644 --- a/src/AppInstallerRepositoryCore/SourceFactory.h +++ b/src/AppInstallerRepositoryCore/SourceFactory.h @@ -42,6 +42,6 @@ namespace AppInstaller::Repository virtual bool Remove(const SourceDetails& details, IProgressCallback& progress) = 0; // Gets the factory for the given type. - static std::unique_ptr GetForType(std::string_view type); + static std::unique_ptr GetForType(SourceType type); }; } diff --git a/src/AppInstallerRepositoryCore/SourceList.cpp b/src/AppInstallerRepositoryCore/SourceList.cpp index 6462aeb98e..5b290a39b7 100644 --- a/src/AppInstallerRepositoryCore/SourceList.cpp +++ b/src/AppInstallerRepositoryCore/SourceList.cpp @@ -3,8 +3,6 @@ #include "pch.h" #include "SourceList.h" #include "SourcePolicy.h" -#include "Microsoft/PreIndexedPackageSourceFactory.h" -#include "Rest/RestSourceFactory.h" #include #include @@ -185,7 +183,7 @@ namespace AppInstaller::Repository { out << YAML::BeginMap; out << YAML::Key << s_SourcesYaml_Source_Name << YAML::Value << details.Name; - out << YAML::Key << s_SourcesYaml_Source_Type << YAML::Value << details.Type; + out << YAML::Key << s_SourcesYaml_Source_Type << YAML::Value << SourceTypeEnumToString(details.Type); out << YAML::Key << s_SourcesYaml_Source_Arg << YAML::Value << details.Arg; out << YAML::Key << s_SourcesYaml_Source_Data << YAML::Value << details.Data; out << YAML::Key << s_SourcesYaml_Source_Identifier << YAML::Value << details.Identifier; @@ -209,7 +207,7 @@ namespace AppInstaller::Repository { return left.Arg == right.Arg && left.Identifier == right.Identifier && - Utility::CaseInsensitiveEquals(left.Type, right.Type); + left.Type == right.Type; } bool ShouldBeHidden(const SourceDetailsInternal& details) @@ -312,24 +310,24 @@ namespace AppInstaller::Repository return {}; } - std::optional CheckForWellKnownSourceMatch(std::string_view name, std::string_view arg, std::string_view type) + std::optional CheckForWellKnownSourceMatch(std::string_view name, std::string_view arg, SourceType type) { - if (name == s_Source_WingetCommunityDefault_Name && arg == s_Source_WingetCommunityDefault_Arg && type == Microsoft::PreIndexedPackageSourceFactory::Type()) + if (name == s_Source_WingetCommunityDefault_Name && arg == s_Source_WingetCommunityDefault_Arg && type == SourceType::PreIndexedPackage) { return WellKnownSource::WinGet; } - if (name == s_Source_MSStoreDefault_Name && arg == s_Source_MSStoreDefault_Arg && type == Rest::RestSourceFactory::Type()) + if (name == s_Source_MSStoreDefault_Name && arg == s_Source_MSStoreDefault_Arg && type == SourceType::Rest) { return WellKnownSource::MicrosoftStore; } - if (name == s_Source_DesktopFrameworks_Name && arg == s_Source_DesktopFrameworks_Arg && type == Microsoft::PreIndexedPackageSourceFactory::Type()) + if (name == s_Source_DesktopFrameworks_Name && arg == s_Source_DesktopFrameworks_Arg && type == SourceType::PreIndexedPackage) { return WellKnownSource::DesktopFrameworks; } - if (name == s_Source_WingetCommunityFont_Name && arg == s_Source_WingetCommunityFont_Arg && type == Rest::RestSourceFactory::Type()) + if (name == s_Source_WingetCommunityFont_Name && arg == s_Source_WingetCommunityFont_Arg && type == SourceType::PreIndexedPackage) { return WellKnownSource::WinGetFont; } @@ -346,7 +344,7 @@ namespace AppInstaller::Repository SourceDetailsInternal details; details.Origin = SourceOrigin::Default; details.Name = s_Source_WingetCommunityDefault_Name; - details.Type = Microsoft::PreIndexedPackageSourceFactory::Type(); + details.Type = SourceType::PreIndexedPackage; details.Arg = s_Source_WingetCommunityDefault_Arg; details.Data = s_Source_WingetCommunityDefault_Data; details.Identifier = s_Source_WingetCommunityDefault_Identifier; @@ -358,7 +356,7 @@ namespace AppInstaller::Repository SourceDetailsInternal details; details.Origin = SourceOrigin::Default; details.Name = s_Source_MSStoreDefault_Name; - details.Type = Rest::RestSourceFactory::Type(); + details.Type = SourceType::Rest; details.Arg = s_Source_MSStoreDefault_Arg; details.Identifier = s_Source_MSStoreDefault_Identifier; details.TrustLevel = SourceTrustLevel::Trusted; @@ -391,7 +389,7 @@ namespace AppInstaller::Repository SourceDetailsInternal details; details.Origin = SourceOrigin::Default; details.Name = s_Source_DesktopFrameworks_Name; - details.Type = Microsoft::PreIndexedPackageSourceFactory::Type(); + details.Type = SourceType::PreIndexedPackage; details.Arg = s_Source_DesktopFrameworks_Arg; details.Data = s_Source_DesktopFrameworks_Data; details.Identifier = s_Source_DesktopFrameworks_Identifier; @@ -404,7 +402,7 @@ namespace AppInstaller::Repository SourceDetailsInternal details; details.Origin = SourceOrigin::Default; details.Name = s_Source_WingetCommunityFont_Name; - details.Type = Microsoft::PreIndexedPackageSourceFactory::Type(); + details.Type = SourceType::PreIndexedPackage; details.Arg = s_Source_WingetCommunityFont_Arg; details.Data = s_Source_WingetCommunityFont_Data; details.Identifier = s_Source_WingetCommunityFont_Identifier; @@ -820,7 +818,6 @@ namespace AppInstaller::Repository { std::string_view name = m_userSourcesStream.GetName(); if (!TryReadScalar(name, settingValue, source, s_SourcesYaml_Source_Name, details.Name)) { return false; } - if (!TryReadScalar(name, settingValue, source, s_SourcesYaml_Source_Type, details.Type)) { return false; } if (!TryReadScalar(name, settingValue, source, s_SourcesYaml_Source_Arg, details.Arg)) { return false; } if (!TryReadScalar(name, settingValue, source, s_SourcesYaml_Source_Data, details.Data)) { return false; } if (!TryReadScalar(name, settingValue, source, s_SourcesYaml_Source_IsTombstone, details.IsTombstone)) { return false; } @@ -829,6 +826,20 @@ namespace AppInstaller::Repository TryReadScalar(name, settingValue, source, s_SourcesYaml_Source_IsOverride, details.IsOverride, false); TryReadScalar(name, settingValue, source, s_SourcesYaml_Source_Priority, details.Priority, false); + std::string type; + if (!TryReadScalar(name, settingValue, source, s_SourcesYaml_Source_Type, type)) { return false; } + if (type.empty() && (details.IsTombstone || details.IsOverride)) + { + // Legacy tombstone/override entries may carry empty type strings. + details.Type = Source::GetDefaultSourceType(); + } + else + { + auto sourceType = TryConvertToSourceTypeEnum(type); + if (!sourceType) { return false; } + details.Type = sourceType.value(); + } + int64_t trustLevelValue; if (TryReadScalar(name, settingValue, source, s_SourcesYaml_Source_TrustLevel, trustLevelValue, false)) { @@ -884,7 +895,13 @@ namespace AppInstaller::Repository AICLI_LOG(Repo, Verbose, << "... with configured source " << additionalSource.Name); SourceDetailsInternal details; details.Name = additionalSource.Name; - details.Type = additionalSource.Type; + auto sourceType = TryConvertToSourceTypeEnum(additionalSource.Type); + if (!sourceType) + { + AICLI_LOG(Repo, Warning, << "Invalid source type from policy source: " << additionalSource.Type); + continue; + } + details.Type = sourceType.value(); details.Arg = additionalSource.Arg; details.Data = additionalSource.Data; details.Identifier = additionalSource.Identifier; diff --git a/src/AppInstallerRepositoryCore/SourceList.h b/src/AppInstallerRepositoryCore/SourceList.h index 2230715209..b1f7c5e269 100644 --- a/src/AppInstallerRepositoryCore/SourceList.h +++ b/src/AppInstallerRepositoryCore/SourceList.h @@ -11,7 +11,7 @@ namespace AppInstaller::Repository std::string_view GetWellKnownSourceName(WellKnownSource source); std::string_view GetWellKnownSourceArg(WellKnownSource source); std::string_view GetWellKnownSourceIdentifier(WellKnownSource source); - std::optional CheckForWellKnownSourceMatch(std::string_view name, std::string_view arg, std::string_view type); + std::optional CheckForWellKnownSourceMatch(std::string_view name, std::string_view arg, SourceType type); // SourceDetails with additional data used internally. struct SourceDetailsInternal : public SourceDetails diff --git a/src/AppInstallerRepositoryCore/SourcePolicy.cpp b/src/AppInstallerRepositoryCore/SourcePolicy.cpp index 22fabc7a80..9f9f5521ff 100644 --- a/src/AppInstallerRepositoryCore/SourcePolicy.cpp +++ b/src/AppInstallerRepositoryCore/SourcePolicy.cpp @@ -2,8 +2,6 @@ // Licensed under the MIT License. #include "pch.h" #include "SourcePolicy.h" -#include "Microsoft/PreIndexedPackageSourceFactory.h" -#include "Rest/RestSourceFactory.h" using namespace AppInstaller::Settings; @@ -38,7 +36,7 @@ namespace AppInstaller::Repository } template - std::optional FindSourceInPolicy(std::string_view name, std::string_view type, std::string_view arg) + std::optional FindSourceInPolicy(std::string_view name, SourceType type, std::string_view arg) { auto sourcesOpt = GroupPolicies().GetValue

(); if (!sourcesOpt.has_value()) @@ -52,7 +50,9 @@ namespace AppInstaller::Repository sources.end(), [&](const SourceFromPolicy& policySource) { - return Utility::ICUCaseInsensitiveEquals(name, policySource.Name) && Utility::ICUCaseInsensitiveEquals(type, policySource.Type) && arg == policySource.Arg; + return Utility::ICUCaseInsensitiveEquals(name, policySource.Name) && + Utility::ICUCaseInsensitiveEquals(SourceTypeEnumToString(type), policySource.Type) && + arg == policySource.Arg; }); if (source == sources.end()) @@ -64,7 +64,7 @@ namespace AppInstaller::Repository } template - bool IsSourceInPolicy(std::string_view name, std::string_view type, std::string_view arg) + bool IsSourceInPolicy(std::string_view name, SourceType type, std::string_view arg) { return FindSourceInPolicy

(name, type, arg).has_value(); } @@ -74,7 +74,7 @@ namespace AppInstaller::Repository // If it does it returns None, otherwise it returns which policy is blocking it. // Note that this applies to user sources that are being added as well as user sources // that already existed when the Group Policy came into effect. - TogglePolicy::Policy GetPolicyBlockingUserSource(std::string_view name, std::string_view type, std::string_view arg, bool isTombstone) + TogglePolicy::Policy GetPolicyBlockingUserSource(std::string_view name, SourceType type, std::string_view arg, bool isTombstone) { // Reasons for not allowing: // 1. The source is a tombstone for default source that is explicitly enabled @@ -116,19 +116,19 @@ namespace AppInstaller::Repository // - Do a case-insensitive check as the domain portion of the URL is case-insensitive, // and we don't need case sensitivity for the rest as we control the domain. if (Utility::CaseInsensitiveEquals(arg, GetWellKnownSourceArg(WellKnownSource::WinGet)) && - Utility::CaseInsensitiveEquals(type, Microsoft::PreIndexedPackageSourceFactory::Type())) + type == SourceType::PreIndexedPackage) { return IsWellKnownSourceEnabled(WellKnownSource::WinGet) ? TogglePolicy::Policy::None : TogglePolicy::Policy::DefaultSource; } if (Utility::CaseInsensitiveEquals(arg, GetWellKnownSourceArg(WellKnownSource::MicrosoftStore)) && - Utility::CaseInsensitiveEquals(type, Rest::RestSourceFactory::Type())) + type == SourceType::Rest) { return IsWellKnownSourceEnabled(WellKnownSource::MicrosoftStore) ? TogglePolicy::Policy::None : TogglePolicy::Policy::MSStoreSource; } if (Utility::CaseInsensitiveEquals(arg, GetWellKnownSourceArg(WellKnownSource::WinGetFont)) && - Utility::CaseInsensitiveEquals(type, Microsoft::PreIndexedPackageSourceFactory::Type())) + type == SourceType::PreIndexedPackage) { return IsWellKnownSourceEnabled(WellKnownSource::WinGetFont) ? TogglePolicy::Policy::None : TogglePolicy::Policy::FontSource; } @@ -138,19 +138,19 @@ namespace AppInstaller::Repository // (as it didn't match above). We only care if Group Policy requires the default source. if (name == GetWellKnownSourceName(WellKnownSource::WinGet) && IsWellKnownSourceEnabled(WellKnownSource::WinGet, true)) { - AICLI_LOG(Repo, Warning, << "User source is not allowed as it shadows the default source. Name [" << name << "]. Arg [" << arg << "] Type [" << type << ']'); + AICLI_LOG(Repo, Warning, << "User source is not allowed as it shadows the default source. Name [" << name << "]. Arg [" << arg << "] Type [" << SourceTypeEnumToString(type) << ']'); return TogglePolicy::Policy::DefaultSource; } if (name == GetWellKnownSourceName(WellKnownSource::MicrosoftStore) && IsWellKnownSourceEnabled(WellKnownSource::MicrosoftStore, true)) { - AICLI_LOG(Repo, Warning, << "User source is not allowed as it shadows a default MS Store source. Name [" << name << "]. Arg [" << arg << "] Type [" << type << ']'); + AICLI_LOG(Repo, Warning, << "User source is not allowed as it shadows a default MS Store source. Name [" << name << "]. Arg [" << arg << "] Type [" << SourceTypeEnumToString(type) << ']'); return TogglePolicy::Policy::MSStoreSource; } if (name == GetWellKnownSourceName(WellKnownSource::WinGetFont) && IsWellKnownSourceEnabled(WellKnownSource::WinGetFont, true)) { - AICLI_LOG(Repo, Warning, << "User source is not allowed as it shadows a default font source. Name [" << name << "]. Arg [" << arg << "] Type [" << type << ']'); + AICLI_LOG(Repo, Warning, << "User source is not allowed as it shadows a default font source. Name [" << name << "]. Arg [" << arg << "] Type [" << SourceTypeEnumToString(type) << ']'); return TogglePolicy::Policy::FontSource; } @@ -169,7 +169,7 @@ namespace AppInstaller::Repository { if (!IsSourceInPolicy(name, type, arg)) { - AICLI_LOG(Repo, Warning, << "Source is not in the Group Policy allowed list. Name [" << name << "]. Arg [" << arg << "] Type [" << type << ']'); + AICLI_LOG(Repo, Warning, << "Source is not in the Group Policy allowed list. Name [" << name << "]. Arg [" << arg << "] Type [" << SourceTypeEnumToString(type) << ']'); return TogglePolicy::Policy::AllowedSources; } } @@ -177,7 +177,7 @@ namespace AppInstaller::Repository return TogglePolicy::Policy::None; } - bool IsUserSourceAllowedByPolicy(std::string_view name, std::string_view type, std::string_view arg, bool isTombstone) + bool IsUserSourceAllowedByPolicy(std::string_view name, SourceType type, std::string_view arg, bool isTombstone) { return GetPolicyBlockingUserSource(name, type, arg, isTombstone) == TogglePolicy::Policy::None; } diff --git a/src/AppInstallerRepositoryCore/SourcePolicy.h b/src/AppInstallerRepositoryCore/SourcePolicy.h index 3f3afb3c13..694fb9700a 100644 --- a/src/AppInstallerRepositoryCore/SourcePolicy.h +++ b/src/AppInstallerRepositoryCore/SourcePolicy.h @@ -13,10 +13,10 @@ namespace AppInstaller::Repository // If it does it returns None, otherwise it returns which policy is blocking it. // Note that this applies to user sources that are being added as well as user sources // that already existed when the Group Policy came into effect. - Settings::TogglePolicy::Policy GetPolicyBlockingUserSource(std::string_view name, std::string_view type, std::string_view arg, bool isTombstone); + Settings::TogglePolicy::Policy GetPolicyBlockingUserSource(std::string_view name, SourceType type, std::string_view arg, bool isTombstone); // Helper that converts the result of GetPolicyBlockingUserSource into a bool. - bool IsUserSourceAllowedByPolicy(std::string_view name, std::string_view type, std::string_view arg, bool isTombstone); + bool IsUserSourceAllowedByPolicy(std::string_view name, SourceType type, std::string_view arg, bool isTombstone); // Determines if a well known source is enabled; if onlyExplicit is true, it must be explicitly enabled by group policy. bool IsWellKnownSourceEnabled(WellKnownSource source, bool onlyExplicit = false); diff --git a/src/Microsoft.Management.Deployment/PackageCatalogInfo.cpp b/src/Microsoft.Management.Deployment/PackageCatalogInfo.cpp index 0306cb58b9..ed4b83a1d6 100644 --- a/src/Microsoft.Management.Deployment/PackageCatalogInfo.cpp +++ b/src/Microsoft.Management.Deployment/PackageCatalogInfo.cpp @@ -25,7 +25,7 @@ namespace winrt::Microsoft::Management::Deployment::implementation } hstring PackageCatalogInfo::Type() { - return winrt::to_hstring(m_sourceDetails.Type); + return winrt::to_hstring(::AppInstaller::Repository::SourceTypeEnumToString(m_sourceDetails.Type)); } hstring PackageCatalogInfo::Argument() { diff --git a/src/Microsoft.Management.Deployment/PackageCatalogProgress.cpp b/src/Microsoft.Management.Deployment/PackageCatalogProgress.cpp index 5a5169ad34..6563cde987 100644 --- a/src/Microsoft.Management.Deployment/PackageCatalogProgress.cpp +++ b/src/Microsoft.Management.Deployment/PackageCatalogProgress.cpp @@ -3,10 +3,9 @@ #include "pch.h" #include "PackageCatalogProgress.h" #include "AppInstallerStrings.h" -#include "Microsoft/PredefinedInstalledSourceFactory.h" +#include using namespace AppInstaller; -using namespace AppInstaller::Repository; namespace winrt::Microsoft::Management::Deployment { @@ -14,8 +13,8 @@ namespace winrt::Microsoft::Management::Deployment { std::shared_ptr CreatePackageCatalogProgressSink(std::string sourceType, std::function progressReporter, bool removeOperation) { - if (sourceType.empty() - || Utility::CaseInsensitiveEquals( Repository::Microsoft::PredefinedInstalledSourceFactory::Type(), sourceType)) + auto sourceTypeEnum = ::AppInstaller::Repository::TryConvertToSourceTypeEnum(sourceType).value_or(::AppInstaller::Repository::Source::GetDefaultSourceType()); + if (sourceTypeEnum == ::AppInstaller::Repository::SourceType::PreIndexedPackage || sourceTypeEnum == ::AppInstaller::Repository::SourceType::PredefinedInstalled) { std::vector> progressWeights; diff --git a/src/Microsoft.Management.Deployment/PackageCatalogReference.cpp b/src/Microsoft.Management.Deployment/PackageCatalogReference.cpp index 44fa2ad604..55fd500c7b 100644 --- a/src/Microsoft.Management.Deployment/PackageCatalogReference.cpp +++ b/src/Microsoft.Management.Deployment/PackageCatalogReference.cpp @@ -361,7 +361,9 @@ namespace winrt::Microsoft::Management::Deployment::implementation auto report_progress{ co_await winrt::get_progress_token() }; co_await winrt::resume_background(); - auto packageCatalogProgressSink = winrt::Microsoft::Management::Deployment::ProgressSinkFactory::CreatePackageCatalogProgressSink(this->m_sourceReference.GetDetails().Type, report_progress); + auto packageCatalogProgressSink = winrt::Microsoft::Management::Deployment::ProgressSinkFactory::CreatePackageCatalogProgressSink( + std::string{ ::AppInstaller::Repository::SourceTypeEnumToString(this->m_sourceReference.GetDetails().Type) }, + report_progress); packageCatalogProgressSink->BeginProgress(); ::AppInstaller::ProgressCallback progress(packageCatalogProgressSink.get()); diff --git a/src/Microsoft.Management.Deployment/PackageManager.cpp b/src/Microsoft.Management.Deployment/PackageManager.cpp index 6470aad0ed..4d97638023 100644 --- a/src/Microsoft.Management.Deployment/PackageManager.cpp +++ b/src/Microsoft.Management.Deployment/PackageManager.cpp @@ -68,28 +68,17 @@ namespace winrt::Microsoft::Management::Deployment::implementation return *addPackageCatalogResult; } - void CheckForDuplicateSource(const std::string& name, const std::string& type, const std::string& sourceUri) + void CheckForDuplicateSource(const std::string& name, ::AppInstaller::Repository::SourceType type, const std::string& sourceUri) { auto sourceList = ::AppInstaller::Repository::Source::GetCurrentSources(); - std::string sourceType = type; - - // [NOTE:] If the source type is not specified, the default source type will be used for validation.In cases where the source type is empty, - // it remains unassigned until the add operation, at which point it is assigned.Without this default assignment, an empty string could be - // compared to the default type, potentially allowing different source names with the same URI to be seen as unique. - // To avoid this, assign the default source type prior to comparison. - if (sourceType.empty()) - { - // This method of obtaining the default source type is slightly expensive as it requires creating a SourceFactory object - // and fetching the type name.Nonetheless, it future-proofs the code against any changes in the SourceFactory's default type. - sourceType = ::AppInstaller::Repository::Source::GetDefaultSourceType(); - } - + // source type has already been normalized to enum (with default applied) at input boundary. for (const auto& source : sourceList) { THROW_HR_IF(APPINSTALLER_CLI_ERROR_SOURCE_NAME_ALREADY_EXISTS, ::AppInstaller::Utility::ICUCaseInsensitiveEquals(source.Name, name)); - bool sourceUriAlreadyExists = !source.Arg.empty() && source.Arg == sourceUri && source.Type == sourceType; + bool sourceUriAlreadyExists = !source.Arg.empty() && source.Arg == sourceUri && + source.Type == type; THROW_HR_IF(APPINSTALLER_CLI_ERROR_SOURCE_ARG_ALREADY_EXISTS, sourceUriAlreadyExists); } } @@ -97,7 +86,7 @@ namespace winrt::Microsoft::Management::Deployment::implementation ::AppInstaller::Repository::Source CreateSourceFromOptions(const winrt::Microsoft::Management::Deployment::AddPackageCatalogOptions& options) { std::string name = winrt::to_string(options.Name()); - std::string type = winrt::to_string(options.Type()); + auto sourceType = ::AppInstaller::Repository::TryConvertToSourceTypeEnum(winrt::to_string(options.Type())).value_or(::AppInstaller::Repository::Source::GetDefaultSourceType()); std::string sourceUri = winrt::to_string(options.SourceUri()); AppInstaller::Repository::SourceTrustLevel trustLevel = AppInstaller::Repository::SourceTrustLevel::None; @@ -106,13 +95,13 @@ namespace winrt::Microsoft::Management::Deployment::implementation trustLevel = AppInstaller::Repository::SourceTrustLevel::Trusted; } - CheckForDuplicateSource(name, type, sourceUri); + CheckForDuplicateSource(name, sourceType, sourceUri); ::AppInstaller::Repository::SourceEdit additionalProperties; additionalProperties.Explicit = options.Explicit(); additionalProperties.Priority = options.Priority(); - ::AppInstaller::Repository::Source source = ::AppInstaller::Repository::Source{ name, sourceUri, type, trustLevel, additionalProperties }; + ::AppInstaller::Repository::Source source = ::AppInstaller::Repository::Source{ name, sourceUri, sourceType, trustLevel, additionalProperties }; std::string customHeader = winrt::to_string(options.CustomHeader()); if (!customHeader.empty()) @@ -1374,8 +1363,10 @@ namespace winrt::Microsoft::Management::Deployment::implementation auto report_progress{ co_await winrt::get_progress_token() }; co_await winrt::resume_background(); - std::string type = winrt::to_string(options.Type()); - auto packageCatalogProgressSink = winrt::Microsoft::Management::Deployment::ProgressSinkFactory::CreatePackageCatalogProgressSink(type, report_progress ); + auto sourceType = ::AppInstaller::Repository::TryConvertToSourceTypeEnum(winrt::to_string(options.Type())).value_or(::AppInstaller::Repository::Source::GetDefaultSourceType()); + auto packageCatalogProgressSink = winrt::Microsoft::Management::Deployment::ProgressSinkFactory::CreatePackageCatalogProgressSink( + std::string{ ::AppInstaller::Repository::SourceTypeEnumToString(sourceType) }, + report_progress); packageCatalogProgressSink->BeginProgress(); ::AppInstaller::ProgressCallback progress(packageCatalogProgressSink.get()); @@ -1413,7 +1404,13 @@ namespace winrt::Microsoft::Management::Deployment::implementation auto report_progress{ co_await winrt::get_progress_token() }; co_await winrt::resume_background(); - auto packageCatalogProgressSink = winrt::Microsoft::Management::Deployment::ProgressSinkFactory::CreatePackageCatalogProgressSink(matchingSource.value().Type, report_progress, true); + std::string normalizedSourceType = std::string{ + ::AppInstaller::Repository::SourceTypeEnumToString(matchingSource.value().Type) }; + + auto packageCatalogProgressSink = winrt::Microsoft::Management::Deployment::ProgressSinkFactory::CreatePackageCatalogProgressSink( + normalizedSourceType, + report_progress, + true); packageCatalogProgressSink->BeginProgress(); ::AppInstaller::Repository::Source sourceToRemove = ::AppInstaller::Repository::Source{ matchingSource.value().Name };