winget-cli

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

commit 827a43af1a9c25b64c748e5064afaa7603f05229
parent c70d0f2bcb8498f7ec06c12a7e4a102611b8e107
Author: JohnMcPMS <johnmcp@microsoft.com>
Date:   Mon, 21 Sep 2020 23:58:43 -0700

Refactor source and search functionality (#575)

Starting with `ISource`, a unique identifier is added to enable tying installs to them regardless of the name given to the source by the user.  `Search` is not changed at face value, but the result struct now returns a collection of `IPackage`s rather than the previous `IApplication`.  `IPackage` is a collection of `IPackageVersion`s in different states.  Currently that is [0, 1] possible installed versions, and [0, N] available versions.

`IPackageVersion` contains functionality to get individual properties of the specific version, as well as the manifest.

All other changes fall out of the above.
Diffstat:
Msrc/AppInstallerCLICore/Workflows/CompletionFlow.cpp | 28+++++++++++++---------------
Msrc/AppInstallerCLICore/Workflows/ShowFlow.cpp | 6+++---
Msrc/AppInstallerCLICore/Workflows/WorkflowBase.cpp | 60+++++++++++++++++++++++++++++++++++-------------------------
Msrc/AppInstallerCLICore/Workflows/WorkflowBase.h | 4++--
Msrc/AppInstallerCLITests/SQLiteIndex.cpp | 158++++++++++++++++++++++++++++++++++++++++++++-----------------------------------
Msrc/AppInstallerCLITests/SQLiteIndexSource.cpp | 67++++++++++++++++++++++++++++++++++---------------------------------
Msrc/AppInstallerCLITests/Sources.cpp | 38++++++++++++++++++++++----------------
Msrc/AppInstallerCLITests/WorkFlow.cpp | 73+++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------
Msrc/AppInstallerRepositoryCore/AggregatedSource.cpp | 16+++++++++++-----
Msrc/AppInstallerRepositoryCore/AggregatedSource.h | 14+++++++++++---
Msrc/AppInstallerRepositoryCore/Microsoft/PreIndexedPackageSourceFactory.cpp | 4++--
Msrc/AppInstallerRepositoryCore/Microsoft/SQLiteIndex.cpp | 19+++++++------------
Msrc/AppInstallerRepositoryCore/Microsoft/SQLiteIndex.h | 15++++++---------
Msrc/AppInstallerRepositoryCore/Microsoft/SQLiteIndexSource.cpp | 136+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------
Msrc/AppInstallerRepositoryCore/Microsoft/SQLiteIndexSource.h | 12+++++++++---
Msrc/AppInstallerRepositoryCore/Microsoft/Schema/1_0/Interface.h | 11+++++------
Msrc/AppInstallerRepositoryCore/Microsoft/Schema/1_0/Interface_1_0.cpp | 66+++++++++++++++++++++++++++++++-----------------------------------
Msrc/AppInstallerRepositoryCore/Microsoft/Schema/1_0/ManifestTable.cpp | 22+++++++++++++++++-----
Msrc/AppInstallerRepositoryCore/Microsoft/Schema/1_0/ManifestTable.h | 23+++++++++++++----------
Msrc/AppInstallerRepositoryCore/Microsoft/Schema/1_0/OneToOneTable.cpp | 6+++---
Msrc/AppInstallerRepositoryCore/Microsoft/Schema/1_0/OneToOneTable.h | 12++++++------
Msrc/AppInstallerRepositoryCore/Microsoft/Schema/1_0/PathPartTable.cpp | 2+-
Msrc/AppInstallerRepositoryCore/Microsoft/Schema/1_0/PathPartTable.h | 2+-
Msrc/AppInstallerRepositoryCore/Microsoft/Schema/1_0/SearchResultsTable.h | 12++++++------
Msrc/AppInstallerRepositoryCore/Microsoft/Schema/1_0/SearchResultsTable_1_0.cpp | 26+++++++++++++-------------
Msrc/AppInstallerRepositoryCore/Microsoft/Schema/1_1/Interface.h | 4++--
Msrc/AppInstallerRepositoryCore/Microsoft/Schema/1_1/Interface_1_1.cpp | 12++++++------
Msrc/AppInstallerRepositoryCore/Microsoft/Schema/1_1/SearchResultsTable.h | 4++--
Msrc/AppInstallerRepositoryCore/Microsoft/Schema/1_1/SearchResultsTable_1_1.cpp | 6+++---
Msrc/AppInstallerRepositoryCore/Microsoft/Schema/ISQLiteIndex.h | 17+++++++----------
Msrc/AppInstallerRepositoryCore/Public/AppInstallerRepositorySearch.h | 101++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------------
Msrc/AppInstallerRepositoryCore/Public/AppInstallerRepositorySource.h | 8+++++++-
Msrc/AppInstallerRepositoryCore/RepositorySource.cpp | 6+++---
Msrc/AppInstallerRepositoryCore/SQLiteStatementBuilder.cpp | 4++--
Msrc/AppInstallerRepositoryCore/SQLiteStatementBuilder.h | 4++--
Msrc/AppInstallerRepositoryCore/SQLiteTempTable.cpp | 2+-
Msrc/AppInstallerRepositoryCore/SQLiteTempTable.h | 2+-
Msrc/AppInstallerRepositoryCore/SQLiteWrapper.cpp | 10+++++-----
Msrc/AppInstallerRepositoryCore/SQLiteWrapper.h | 10+++++-----
39 files changed, 612 insertions(+), 410 deletions(-)

diff --git a/src/AppInstallerCLICore/Workflows/CompletionFlow.cpp b/src/AppInstallerCLICore/Workflows/CompletionFlow.cpp @@ -57,7 +57,7 @@ namespace AppInstaller::CLI::Workflow { if (searchResult.Matches[i].MatchCriteria.Value.empty()) { - OutputCompletionString(stream, searchResult.Matches[i].Application->GetId()); + OutputCompletionString(stream, searchResult.Matches[i].Package->GetLatestAvailableVersion()->GetProperty(Repository::PackageVersionProperty::Id)); } else { @@ -71,12 +71,11 @@ namespace AppInstaller::CLI::Workflow const std::string& word = context.Get<Data::CompletionData>().Word(); auto stream = context.Reporter.Completion(); - for (const auto& vc : context.Get<Execution::Data::SearchResult>().Matches[0].Application->GetVersions()) + for (const auto& vc : context.Get<Execution::Data::SearchResult>().Matches[0].Package->GetAvailableVersionKeys()) { - std::string version = vc.GetVersion().ToString(); - if (word.empty() || Utility::CaseInsensitiveStartsWith(version, word)) + if (word.empty() || Utility::CaseInsensitiveStartsWith(vc.Version, word)) { - OutputCompletionString(stream, version); + OutputCompletionString(stream, vc.Version); } } } @@ -88,13 +87,12 @@ namespace AppInstaller::CLI::Workflow std::vector<std::string> channels; - for (const auto& vc : context.Get<Execution::Data::SearchResult>().Matches[0].Application->GetVersions()) + for (const auto& vc : context.Get<Execution::Data::SearchResult>().Matches[0].Package->GetAvailableVersionKeys()) { - std::string channel = vc.GetChannel().ToString(); - if ((word.empty() || Utility::CaseInsensitiveStartsWith(channel, word)) && - std::find(channels.begin(), channels.end(), channel) == channels.end()) + if ((word.empty() || Utility::CaseInsensitiveStartsWith(vc.Channel, word)) && + std::find(channels.begin(), channels.end(), vc.Channel) == channels.end()) { - channels.emplace_back(std::move(channel)); + channels.emplace_back(vc.Channel); } } @@ -121,31 +119,31 @@ namespace AppInstaller::CLI::Workflow case Execution::Args::Type::Id: context << Workflow::OpenSource << - Workflow::SearchSourceForCompletionField(Repository::ApplicationMatchField::Id) << + Workflow::SearchSourceForCompletionField(Repository::PackageMatchField::Id) << Workflow::CompleteWithMatchedField; break; case Execution::Args::Type::Name: context << Workflow::OpenSource << - Workflow::SearchSourceForCompletionField(Repository::ApplicationMatchField::Name) << + Workflow::SearchSourceForCompletionField(Repository::PackageMatchField::Name) << Workflow::CompleteWithMatchedField; break; case Execution::Args::Type::Moniker: context << Workflow::OpenSource << - Workflow::SearchSourceForCompletionField(Repository::ApplicationMatchField::Moniker) << + Workflow::SearchSourceForCompletionField(Repository::PackageMatchField::Moniker) << Workflow::CompleteWithMatchedField; break; case Execution::Args::Type::Tag: context << Workflow::OpenSource << - Workflow::SearchSourceForCompletionField(Repository::ApplicationMatchField::Tag) << + Workflow::SearchSourceForCompletionField(Repository::PackageMatchField::Tag) << Workflow::CompleteWithMatchedField; break; case Execution::Args::Type::Command: context << Workflow::OpenSource << - Workflow::SearchSourceForCompletionField(Repository::ApplicationMatchField::Command) << + Workflow::SearchSourceForCompletionField(Repository::PackageMatchField::Command) << Workflow::CompleteWithMatchedField; break; case Execution::Args::Type::Version: diff --git a/src/AppInstallerCLICore/Workflows/ShowFlow.cpp b/src/AppInstallerCLICore/Workflows/ShowFlow.cpp @@ -84,12 +84,12 @@ namespace AppInstaller::CLI::Workflow void ShowAppVersions(Execution::Context& context) { - auto app = context.Get<Execution::Data::SearchResult>().Matches.at(0).Application.get(); + auto versions = context.Get<Execution::Data::SearchResult>().Matches.at(0).Package->GetAvailableVersionKeys(); Execution::TableOutput<2> table(context.Reporter, { Resource::String::ShowVersion, Resource::String::ShowChannel }); - for (auto& version : app->GetVersions()) + for (const auto& version : versions) { - table.OutputLine({ version.GetVersion().ToString(), version.GetChannel().ToString() }); + table.OutputLine({ version.Version, version.Channel }); } table.Complete(); } diff --git a/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp b/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp @@ -16,9 +16,9 @@ namespace AppInstaller::CLI::Workflow { std::string GetMatchCriteriaDescriptor(const ResultMatch& match) { - if (match.MatchCriteria.Field != ApplicationMatchField::Id && match.MatchCriteria.Field != ApplicationMatchField::Name) + if (match.MatchCriteria.Field != PackageMatchField::Id && match.MatchCriteria.Field != PackageMatchField::Name) { - std::string result{ ApplicationMatchFieldToString(match.MatchCriteria.Field) }; + std::string result{ PackageMatchFieldToString(match.MatchCriteria.Field) }; result += ": "; result += match.MatchCriteria.Value; return result; @@ -40,27 +40,27 @@ namespace AppInstaller::CLI::Workflow if (args.Contains(Execution::Args::Type::Id)) { - searchRequest.Filters.emplace_back(ApplicationMatchFilter(ApplicationMatchField::Id, matchType, args.GetArg(Execution::Args::Type::Id))); + searchRequest.Filters.emplace_back(PackageMatchFilter(PackageMatchField::Id, matchType, args.GetArg(Execution::Args::Type::Id))); } if (args.Contains(Execution::Args::Type::Name)) { - searchRequest.Filters.emplace_back(ApplicationMatchFilter(ApplicationMatchField::Name, matchType, args.GetArg(Execution::Args::Type::Name))); + searchRequest.Filters.emplace_back(PackageMatchFilter(PackageMatchField::Name, matchType, args.GetArg(Execution::Args::Type::Name))); } if (args.Contains(Execution::Args::Type::Moniker)) { - searchRequest.Filters.emplace_back(ApplicationMatchFilter(ApplicationMatchField::Moniker, matchType, args.GetArg(Execution::Args::Type::Moniker))); + searchRequest.Filters.emplace_back(PackageMatchFilter(PackageMatchField::Moniker, matchType, args.GetArg(Execution::Args::Type::Moniker))); } if (args.Contains(Execution::Args::Type::Tag)) { - searchRequest.Filters.emplace_back(ApplicationMatchFilter(ApplicationMatchField::Tag, matchType, args.GetArg(Execution::Args::Type::Tag))); + searchRequest.Filters.emplace_back(PackageMatchFilter(PackageMatchField::Tag, matchType, args.GetArg(Execution::Args::Type::Tag))); } if (args.Contains(Execution::Args::Type::Command)) { - searchRequest.Filters.emplace_back(ApplicationMatchFilter(ApplicationMatchField::Command, matchType, args.GetArg(Execution::Args::Type::Command))); + searchRequest.Filters.emplace_back(PackageMatchFilter(PackageMatchField::Command, matchType, args.GetArg(Execution::Args::Type::Command))); } if (args.Contains(Execution::Args::Type::Count)) @@ -184,9 +184,9 @@ namespace AppInstaller::CLI::Workflow if (args.Contains(Execution::Args::Type::Query)) { std::string_view query = args.GetArg(Execution::Args::Type::Query); - searchRequest.Inclusions.emplace_back(ApplicationMatchFilter(ApplicationMatchField::Id, matchType, query)); - searchRequest.Inclusions.emplace_back(ApplicationMatchFilter(ApplicationMatchField::Name, matchType, query)); - searchRequest.Inclusions.emplace_back(ApplicationMatchFilter(ApplicationMatchField::Moniker, matchType, query)); + searchRequest.Inclusions.emplace_back(PackageMatchFilter(PackageMatchField::Id, matchType, query)); + searchRequest.Inclusions.emplace_back(PackageMatchFilter(PackageMatchField::Name, matchType, query)); + searchRequest.Inclusions.emplace_back(PackageMatchFilter(PackageMatchField::Moniker, matchType, query)); } SearchSourceApplyFilters(context, searchRequest, matchType); @@ -224,9 +224,9 @@ namespace AppInstaller::CLI::Workflow SearchRequest searchRequest; std::string_view query = context.Get<Execution::Data::CompletionData>().Word(); - searchRequest.Inclusions.emplace_back(ApplicationMatchFilter(ApplicationMatchField::Id, matchType, query)); - searchRequest.Inclusions.emplace_back(ApplicationMatchFilter(ApplicationMatchField::Name, matchType, query)); - searchRequest.Inclusions.emplace_back(ApplicationMatchFilter(ApplicationMatchField::Moniker, matchType, query)); + searchRequest.Inclusions.emplace_back(PackageMatchFilter(PackageMatchField::Id, matchType, query)); + searchRequest.Inclusions.emplace_back(PackageMatchFilter(PackageMatchField::Name, matchType, query)); + searchRequest.Inclusions.emplace_back(PackageMatchFilter(PackageMatchField::Moniker, matchType, query)); SearchSourceApplyFilters(context, searchRequest, matchType); @@ -238,7 +238,7 @@ namespace AppInstaller::CLI::Workflow const std::string& word = context.Get<Execution::Data::CompletionData>().Word(); SearchRequest searchRequest; - searchRequest.Inclusions.emplace_back(ApplicationMatchFilter(m_field, MatchType::StartsWith, word)); + searchRequest.Inclusions.emplace_back(PackageMatchFilter(m_field, MatchType::StartsWith, word)); // If filters are provided, be generous with the search no matter the intended result. SearchSourceApplyFilters(context, searchRequest, MatchType::Substring); @@ -255,10 +255,15 @@ namespace AppInstaller::CLI::Workflow for (size_t i = 0; i < searchResult.Matches.size(); ++i) { - auto app = searchResult.Matches[i].Application.get(); - auto allVersions = app->GetVersions(); - - table.OutputLine({ app->GetName(), app->GetId(), allVersions.at(0).GetVersion().ToString(), GetMatchCriteriaDescriptor(searchResult.Matches[i]), searchResult.Matches[i].SourceName }); + auto latestVersion = searchResult.Matches[i].Package->GetLatestAvailableVersion(); + + table.OutputLine({ + latestVersion->GetProperty(PackageVersionProperty::Name), + latestVersion->GetProperty(PackageVersionProperty::Id), + latestVersion->GetProperty(PackageVersionProperty::Version), + GetMatchCriteriaDescriptor(searchResult.Matches[i]), + searchResult.Matches[i].SourceName + }); } table.Complete(); @@ -297,19 +302,24 @@ namespace AppInstaller::CLI::Workflow AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_MULTIPLE_APPLICATIONS_FOUND); } - auto app = searchResult.Matches.at(0).Application.get(); - Logging::Telemetry().LogAppFound(app->GetName(), app->GetId()); + auto latestVersion = searchResult.Matches.at(0).Package->GetLatestAvailableVersion(); + Logging::Telemetry().LogAppFound(latestVersion->GetProperty(PackageVersionProperty::Name), latestVersion->GetProperty(PackageVersionProperty::Id)); }; } void GetManifestFromSearchResult(Execution::Context& context) { - auto app = context.Get<Execution::Data::SearchResult>().Matches.at(0).Application.get(); - std::string_view version = context.Args.GetArg(Execution::Args::Type::Version); std::string_view channel = context.Args.GetArg(Execution::Args::Type::Channel); - std::optional<Manifest::Manifest> manifest = app->GetManifest(version, channel); + PackageVersionKey key("", version, channel); + auto requestedVersion = context.Get<Execution::Data::SearchResult>().Matches.at(0).Package->GetAvailableVersion(key); + + std::optional<Manifest::Manifest> manifest; + if (requestedVersion) + { + manifest = requestedVersion->GetManifest(); + } if (!manifest) { @@ -362,8 +372,8 @@ namespace AppInstaller::CLI::Workflow void ReportSearchResultIdentity(Execution::Context& context) { - auto app = context.Get<Execution::Data::SearchResult>().Matches.at(0).Application.get(); - ReportIdentity(context, app->GetName(), app->GetId()); + auto latestVersion = context.Get<Execution::Data::SearchResult>().Matches.at(0).Package->GetLatestAvailableVersion(); + ReportIdentity(context, latestVersion->GetProperty(PackageVersionProperty::Name), latestVersion->GetProperty(PackageVersionProperty::Id)); } void ReportManifestIdentity(Execution::Context& context) diff --git a/src/AppInstallerCLICore/Workflows/WorkflowBase.h b/src/AppInstallerCLICore/Workflows/WorkflowBase.h @@ -82,12 +82,12 @@ namespace AppInstaller::CLI::Workflow // Outputs: None struct SearchSourceForCompletionField : public WorkflowTask { - SearchSourceForCompletionField(Repository::ApplicationMatchField field) : WorkflowTask("SearchSourceForCompletionField"), m_field(field) {} + SearchSourceForCompletionField(Repository::PackageMatchField field) : WorkflowTask("SearchSourceForCompletionField"), m_field(field) {} void operator()(Execution::Context& context) const override; private: - Repository::ApplicationMatchField m_field; + Repository::PackageMatchField m_field; }; // Outputs the search results. diff --git a/src/AppInstallerCLITests/SQLiteIndex.cpp b/src/AppInstallerCLITests/SQLiteIndex.cpp @@ -179,6 +179,37 @@ bool ArePackageFamilyNameAndProductCodeSupported(const SQLiteIndex& index, const return (index.GetVersion() >= Schema::Version{ 1, 1 } && testVersion >= Schema::Version{ 1, 1 }); } +std::string GetPropertyStringByKey(const SQLiteIndex& index, SQLite::rowid_t id, PackageVersionProperty property, std::string_view version, std::string_view channel) +{ + auto manifestId = index.GetManifestIdByKey(id, version, channel); + REQUIRE(manifestId); + auto result = index.GetPropertyByManifestId(manifestId.value(), property); + REQUIRE(result); + return result.value(); +} + +std::string GetPropertyStringById(const SQLiteIndex& index, SQLite::rowid_t id, PackageVersionProperty property) +{ + auto versions = index.GetVersionKeysById(id); + REQUIRE(!versions.empty()); + return GetPropertyStringByKey(index, id, property, versions[0].GetVersion().ToString(), versions[0].GetChannel().ToString()); +} + +std::string GetIdStringById(const SQLiteIndex& index, SQLite::rowid_t id) +{ + return GetPropertyStringById(index, id, PackageVersionProperty::Id); +} + +std::string GetNameStringById(const SQLiteIndex& index, SQLite::rowid_t id) +{ + return GetPropertyStringById(index, id, PackageVersionProperty::Name); +} + +std::string GetPathStringByKey(const SQLiteIndex& index, SQLite::rowid_t id, std::string_view version, std::string_view channel) +{ + return GetPropertyStringByKey(index, id, PackageVersionProperty::RelativePath, version, channel); +} + TEST_CASE("SQLiteIndexCreateLatestAndReopen", "[sqliteindex]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; @@ -654,7 +685,7 @@ TEST_CASE("SQLiteIndex_IdCaseInsensitivity", "[sqliteindex][V1_0]") auto results = index.Search({}); REQUIRE(results.Matches.size() == 1); - REQUIRE(manifest1.Id == index.GetIdStringById(results.Matches[0].first)); + REQUIRE(manifest1.Id == GetIdStringById(index, results.Matches[0].first)); } { @@ -664,7 +695,7 @@ TEST_CASE("SQLiteIndex_IdCaseInsensitivity", "[sqliteindex][V1_0]") auto results = index.Search({}); REQUIRE(results.Matches.size() == 1); - REQUIRE(manifest2.Id == index.GetIdStringById(results.Matches[0].first)); + REQUIRE(manifest2.Id == GetIdStringById(index, results.Matches[0].first)); } { @@ -676,7 +707,7 @@ TEST_CASE("SQLiteIndex_IdCaseInsensitivity", "[sqliteindex][V1_0]") auto results = index.Search({}); REQUIRE(results.Matches.size() == 1); - REQUIRE(manifest1.Id == index.GetIdStringById(results.Matches[0].first)); + REQUIRE(manifest1.Id == GetIdStringById(index, results.Matches[0].first)); } { @@ -686,7 +717,7 @@ TEST_CASE("SQLiteIndex_IdCaseInsensitivity", "[sqliteindex][V1_0]") auto results = index.Search({}); REQUIRE(results.Matches.size() == 1); - REQUIRE(manifest1.Id == index.GetIdStringById(results.Matches[0].first)); + REQUIRE(manifest1.Id == GetIdStringById(index, results.Matches[0].first)); } { @@ -810,7 +841,7 @@ TEST_CASE("SQLiteIndex_Search_IdExactMatch", "[sqliteindex]") auto results = index.Search(request); REQUIRE(results.Matches.size() == 1); - REQUIRE(results.Matches[0].second.Field == ApplicationMatchField::Id); + REQUIRE(results.Matches[0].second.Field == PackageMatchField::Id); REQUIRE(results.Matches[0].second.Type == MatchType::Exact); REQUIRE(results.Matches[0].second.Value == manifest.Id); } @@ -835,7 +866,7 @@ TEST_CASE("SQLiteIndex_Search_MultipleMatch", "[sqliteindex]") auto results = index.Search(request); REQUIRE(results.Matches.size() == 1); - auto result = index.GetVersionsById(results.Matches[0].first); + auto result = index.GetVersionKeysById(results.Matches[0].first); REQUIRE(result.size() == 2); } @@ -874,9 +905,8 @@ TEST_CASE("SQLiteIndex_IdString", "[sqliteindex]") auto results = index.Search(request); REQUIRE(results.Matches.size() == 1); - auto result = index.GetIdStringById(results.Matches[0].first); - REQUIRE(result.has_value()); - REQUIRE(result.value() == manifest.Id); + auto result = GetIdStringById(index, results.Matches[0].first); + REQUIRE(result == manifest.Id); } TEST_CASE("SQLiteIndex_NameString", "[sqliteindex]") @@ -896,9 +926,8 @@ TEST_CASE("SQLiteIndex_NameString", "[sqliteindex]") auto results = index.Search(request); REQUIRE(results.Matches.size() == 1); - auto result = index.GetNameStringById(results.Matches[0].first); - REQUIRE(result.has_value()); - REQUIRE(result.value() == manifest.Name); + auto result = GetNameStringById(index, results.Matches[0].first); + REQUIRE(result == manifest.Name); } TEST_CASE("SQLiteIndex_PathString", "[sqliteindex]") @@ -918,13 +947,11 @@ TEST_CASE("SQLiteIndex_PathString", "[sqliteindex]") auto results = index.Search(request); REQUIRE(results.Matches.size() == 1); - auto specificResult = index.GetPathStringByKey(results.Matches[0].first, manifest.Version, manifest.Channel); - REQUIRE(specificResult.has_value()); - REQUIRE(specificResult.value() == relativePath); + auto specificResult = GetPathStringByKey(index, results.Matches[0].first, manifest.Version, manifest.Channel); + REQUIRE(specificResult == relativePath); - auto latestResult = index.GetPathStringByKey(results.Matches[0].first, "", manifest.Channel); - REQUIRE(latestResult.has_value()); - REQUIRE(latestResult.value() == relativePath); + auto latestResult = GetPathStringByKey(index, results.Matches[0].first, "", manifest.Channel); + REQUIRE(latestResult == relativePath); } TEST_CASE("SQLiteIndex_Versions", "[sqliteindex]") @@ -944,7 +971,7 @@ TEST_CASE("SQLiteIndex_Versions", "[sqliteindex]") auto results = index.Search(request); REQUIRE(results.Matches.size() == 1); - auto result = index.GetVersionsById(results.Matches[0].first); + auto result = index.GetVersionKeysById(results.Matches[0].first); REQUIRE(result.size() == 1); REQUIRE(result[0].GetVersion().ToString() == manifest.Version); REQUIRE(result[0].GetChannel().ToString() == manifest.Channel); @@ -981,12 +1008,12 @@ TEST_CASE("SQLiteIndex_Search_VersionSorting", "[sqliteindex]") TestPrepareForRead(index); SearchRequest request; - request.Filters.emplace_back(ApplicationMatchField::Id, MatchType::Exact, "Id"); + request.Filters.emplace_back(PackageMatchField::Id, MatchType::Exact, "Id"); auto results = index.Search(request); REQUIRE(results.Matches.size() == 1); - auto result = index.GetVersionsById(results.Matches[0].first); + auto result = index.GetVersionKeysById(results.Matches[0].first); REQUIRE(result.size() == sortedList.size()); for (size_t i = 0; i < result.size(); ++i) @@ -1031,25 +1058,22 @@ TEST_CASE("SQLiteIndex_PathString_VersionSorting", "[sqliteindex]") TestPrepareForRead(index); SearchRequest request; - request.Filters.emplace_back(ApplicationMatchField::Id, MatchType::Exact, "Id"); + request.Filters.emplace_back(PackageMatchField::Id, MatchType::Exact, "Id"); auto results = index.Search(request); REQUIRE(results.Matches.size() == 1); - auto result = index.GetPathStringByKey(results.Matches[0].first, "", ""); - REQUIRE(result.has_value()); - REQUIRE(result.value() == "Path3"); + auto result = GetPathStringByKey(index, results.Matches[0].first, "", ""); + REQUIRE(result == "Path3"); - result = index.GetPathStringByKey(results.Matches[0].first, "", "alpha"); - REQUIRE(result.has_value()); - REQUIRE(result.value() == "Path2"); + result = GetPathStringByKey(index, results.Matches[0].first, "", "alpha"); + REQUIRE(result == "Path2"); - result = index.GetPathStringByKey(results.Matches[0].first, "", "beta"); - REQUIRE(result.has_value()); - REQUIRE(result.value() == "Path5"); + result = GetPathStringByKey(index, results.Matches[0].first, "", "beta"); + REQUIRE(result == "Path5"); - result = index.GetPathStringByKey(results.Matches[0].first, "", "gamma"); - REQUIRE(!result.has_value()); + auto nonResult = index.GetManifestIdByKey(results.Matches[0].first, "", "gamma"); + REQUIRE(!nonResult.has_value()); } TEST_CASE("SQLiteIndex_PathString_CaseInsensitive", "[sqliteindex]") @@ -1071,18 +1095,18 @@ TEST_CASE("SQLiteIndex_PathString_CaseInsensitive", "[sqliteindex]") TestPrepareForRead(index); SearchRequest request; - request.Filters.emplace_back(ApplicationMatchField::Id, MatchType::Exact, "Id"); + request.Filters.emplace_back(PackageMatchField::Id, MatchType::Exact, "Id"); auto results = index.Search(request); REQUIRE(results.Matches.size() == 1); - auto result = index.GetPathStringByKey(results.Matches[0].first, "", "Alpha"); + auto result = index.GetManifestIdByKey(results.Matches[0].first, "", "Alpha"); REQUIRE(result.has_value()); - result = index.GetPathStringByKey(results.Matches[0].first, "13.2.0-BugFix", ""); + result = index.GetManifestIdByKey(results.Matches[0].first, "13.2.0-BugFix", ""); REQUIRE(result.has_value()); - result = index.GetPathStringByKey(results.Matches[0].first, "13.2.0-BugFix", "BETA"); + result = index.GetManifestIdByKey(results.Matches[0].first, "13.2.0-BugFix", "BETA"); REQUIRE(!result.has_value()); } @@ -1103,7 +1127,7 @@ TEST_CASE("SQLiteIndex_SearchResultsTableSearches", "[sqliteindex][V1_0]") std::string value = "test"; // Perform every type of field and match search - for (auto field : { ApplicationMatchField::Id, ApplicationMatchField::Name, ApplicationMatchField::Moniker, ApplicationMatchField::Tag, ApplicationMatchField::Command }) + for (auto field : { PackageMatchField::Id, PackageMatchField::Name, PackageMatchField::Moniker, PackageMatchField::Tag, PackageMatchField::Command }) { for (auto match : { MatchType::Exact, MatchType::Fuzzy, MatchType::FuzzySubstring, MatchType::Substring, MatchType::Wildcard }) { @@ -1188,8 +1212,8 @@ TEST_CASE("SQLiteIndex_Search_ExactBeforeSubstring", "[sqliteindex]") auto results = index.Search(request); REQUIRE(results.Matches.size() == 2); - REQUIRE(index.GetIdStringById(results.Matches[0].first) == "Id"); - REQUIRE(index.GetIdStringById(results.Matches[1].first) == "Id2"); + REQUIRE(GetIdStringById(index, results.Matches[0].first) == "Id"); + REQUIRE(GetIdStringById(index, results.Matches[1].first) == "Id2"); } TEST_CASE("SQLiteIndex_Search_SingleFilter", "[sqliteindex]") @@ -1206,7 +1230,7 @@ TEST_CASE("SQLiteIndex_Search_SingleFilter", "[sqliteindex]") TestPrepareForRead(index); SearchRequest request; - request.Filters.emplace_back(ApplicationMatchField::Name, MatchType::Substring, "a"); + request.Filters.emplace_back(PackageMatchField::Name, MatchType::Substring, "a"); auto results = index.Search(request); REQUIRE(results.Matches.size() == 2); @@ -1257,14 +1281,13 @@ TEST_CASE("SQLiteIndex_Search_QueryAndFilter", "[sqliteindex]") SearchRequest request; request.Query = RequestMatch(MatchType::Substring, "Id"); - request.Filters.emplace_back(ApplicationMatchField::Name, MatchType::Substring, "Na"); + request.Filters.emplace_back(PackageMatchField::Name, MatchType::Substring, "Na"); auto results = index.Search(request); REQUIRE(results.Matches.size() == 1); - auto result = index.GetIdStringById(results.Matches[0].first); - REQUIRE(result.has_value()); - REQUIRE(result.value() == "Id2"); + auto result = GetIdStringById(index, results.Matches[0].first); + REQUIRE(result == "Id2"); } TEST_CASE("SQLiteIndex_Search_QueryAndMultipleFilters", "[sqliteindex]") @@ -1286,16 +1309,15 @@ TEST_CASE("SQLiteIndex_Search_QueryAndMultipleFilters", "[sqliteindex]") SearchRequest request; request.Query = RequestMatch(MatchType::Substring, "tag"); - request.Filters.emplace_back(ApplicationMatchField::Command, MatchType::Exact, "com3"); - request.Filters.emplace_back(ApplicationMatchField::Tag, MatchType::Substring, "foo"); - request.Filters.emplace_back(ApplicationMatchField::Moniker, MatchType::Substring, "new"); + request.Filters.emplace_back(PackageMatchField::Command, MatchType::Exact, "com3"); + request.Filters.emplace_back(PackageMatchField::Tag, MatchType::Substring, "foo"); + request.Filters.emplace_back(PackageMatchField::Moniker, MatchType::Substring, "new"); auto results = index.Search(request); REQUIRE(results.Matches.size() == 1); - auto result = index.GetIdStringById(results.Matches[0].first); - REQUIRE(result.has_value()); - REQUIRE(result.value() == "Id3"); + auto result = GetIdStringById(index, results.Matches[0].first); + REQUIRE(result == "Id3"); } TEST_CASE("SQLiteIndex_Search_SimpleICULike", "[sqliteindex]") @@ -1313,14 +1335,13 @@ TEST_CASE("SQLiteIndex_Search_SimpleICULike", "[sqliteindex]") SearchRequest request; // Search for anything containing: [lower] a + umlaut - request.Filters.emplace_back(ApplicationMatchField::Id, MatchType::Substring, u8"\xE4"); + request.Filters.emplace_back(PackageMatchField::Id, MatchType::Substring, u8"\xE4"); auto results = index.Search(request); REQUIRE(results.Matches.size() == 1); - auto result = index.GetNameStringById(results.Matches[0].first); - REQUIRE(result.has_value()); - REQUIRE(result.value() == "HasUmlaut"); + auto result = GetNameStringById(index, results.Matches[0].first); + REQUIRE(result == "HasUmlaut"); } TEST_CASE("SQLiteIndex_Search_MaximumResults_Equal", "[sqliteindex]") @@ -1401,7 +1422,7 @@ TEST_CASE("SQLiteIndex_Search_QueryAndInclusion", "[sqliteindex]") SearchRequest request; request.Query = RequestMatch(MatchType::CaseInsensitive, "id3"); - request.Inclusions.emplace_back(ApplicationMatchField::Name, MatchType::Substring, "Na"); + request.Inclusions.emplace_back(PackageMatchField::Name, MatchType::Substring, "Na"); auto results = index.Search(request); REQUIRE(results.Matches.size() == 3); @@ -1421,7 +1442,7 @@ TEST_CASE("SQLiteIndex_Search_InclusionOnly", "[sqliteindex]") TestPrepareForRead(index); SearchRequest request; - request.Inclusions.emplace_back(ApplicationMatchField::Name, MatchType::Substring, "Na"); + request.Inclusions.emplace_back(PackageMatchField::Name, MatchType::Substring, "Na"); auto results = index.Search(request); REQUIRE(results.Matches.size() == 2); @@ -1441,15 +1462,14 @@ TEST_CASE("SQLiteIndex_Search_InclusionAndFilter", "[sqliteindex]") TestPrepareForRead(index); SearchRequest request; - request.Inclusions.emplace_back(ApplicationMatchField::Name, MatchType::Substring, "Na"); - request.Filters.emplace_back(ApplicationMatchField::Name, MatchType::CaseInsensitive, "name"); + request.Inclusions.emplace_back(PackageMatchField::Name, MatchType::Substring, "Na"); + request.Filters.emplace_back(PackageMatchField::Name, MatchType::CaseInsensitive, "name"); auto results = index.Search(request); REQUIRE(results.Matches.size() == 1); - auto result = index.GetIdStringById(results.Matches[0].first); - REQUIRE(result.has_value()); - REQUIRE(result.value() == "Nope"); + auto result = GetIdStringById(index, results.Matches[0].first); + REQUIRE(result == "Nope"); } TEST_CASE("SQLiteIndex_Search_QueryInclusionAndFilter", "[sqliteindex]") @@ -1467,8 +1487,8 @@ TEST_CASE("SQLiteIndex_Search_QueryInclusionAndFilter", "[sqliteindex]") SearchRequest request; request.Query = RequestMatch(MatchType::Substring, "id3"); - request.Inclusions.emplace_back(ApplicationMatchField::Name, MatchType::Substring, "na"); - request.Filters.emplace_back(ApplicationMatchField::Moniker, MatchType::CaseInsensitive, "MONIKER"); + request.Inclusions.emplace_back(PackageMatchField::Name, MatchType::Substring, "na"); + request.Filters.emplace_back(PackageMatchField::Moniker, MatchType::CaseInsensitive, "MONIKER"); auto results = index.Search(request); REQUIRE(results.Matches.size() == 2); @@ -1508,7 +1528,7 @@ TEST_CASE("SQLiteIndex_Search_StartsWith", "[sqliteindex]") TestPrepareForRead(index); SearchRequest request; - request.Inclusions.push_back(ApplicationMatchFilter(ApplicationMatchField::Id, MatchType::StartsWith, "id")); + request.Inclusions.push_back(PackageMatchFilter(PackageMatchField::Id, MatchType::StartsWith, "id")); auto results = index.Search(request); REQUIRE(results.Matches.size() == 2); @@ -1624,7 +1644,7 @@ TEST_CASE("SQLiteIndex_Search_PackageFamilyNameSubstring", "[sqliteindex]") Schema::Version testVersion = TestPrepareForRead(index); SearchRequest request; - request.Inclusions.emplace_back(ApplicationMatchField::PackageFamilyName, MatchType::Substring, "PFN"); + request.Inclusions.emplace_back(PackageMatchField::PackageFamilyName, MatchType::Substring, "PFN"); auto results = index.Search(request); @@ -1652,7 +1672,7 @@ TEST_CASE("SQLiteIndex_Search_ProductCodeSubstring", "[sqliteindex]") Schema::Version testVersion = TestPrepareForRead(index); SearchRequest request; - request.Inclusions.emplace_back(ApplicationMatchField::ProductCode, MatchType::Substring, "PC"); + request.Inclusions.emplace_back(PackageMatchField::ProductCode, MatchType::Substring, "PC"); auto results = index.Search(request); @@ -1680,7 +1700,7 @@ TEST_CASE("SQLiteIndex_Search_PackageFamilyNameMatch", "[sqliteindex]") Schema::Version testVersion = TestPrepareForRead(index); SearchRequest request; - request.Inclusions.emplace_back(ApplicationMatchField::PackageFamilyName, MatchType::Exact, "pfn1"); + request.Inclusions.emplace_back(PackageMatchField::PackageFamilyName, MatchType::Exact, "pfn1"); auto results = index.Search(request); @@ -1708,7 +1728,7 @@ TEST_CASE("SQLiteIndex_Search_ProductCodeMatch", "[sqliteindex]") Schema::Version testVersion = TestPrepareForRead(index); SearchRequest request; - request.Inclusions.emplace_back(ApplicationMatchField::ProductCode, MatchType::Exact, "pc2"); + request.Inclusions.emplace_back(PackageMatchField::ProductCode, MatchType::Exact, "pc2"); auto results = index.Search(request); diff --git a/src/AppInstallerCLITests/SQLiteIndexSource.cpp b/src/AppInstallerCLITests/SQLiteIndexSource.cpp @@ -28,7 +28,7 @@ std::shared_ptr<SQLiteIndexSource> SimpleTestSetup(const std::string& filePath, details.Arg = testManifest.GetPath().parent_path().u8string(); details.Data = ""; - return std::make_shared<SQLiteIndexSource>(details, std::move(index)); + return std::make_shared<SQLiteIndexSource>(details, "*SimpleTestSetup", std::move(index)); } TEST_CASE("SQLiteIndexSource_Search_IdExactMatch", "[sqliteindexsource]") @@ -46,8 +46,8 @@ TEST_CASE("SQLiteIndexSource_Search_IdExactMatch", "[sqliteindexsource]") auto results = source->Search(request); REQUIRE(results.Matches.size() == 1); - REQUIRE(results.Matches[0].Application); - REQUIRE(results.Matches[0].MatchCriteria.Field == ApplicationMatchField::Id); + REQUIRE(results.Matches[0].Package); + REQUIRE(results.Matches[0].MatchCriteria.Field == PackageMatchField::Id); REQUIRE(results.Matches[0].MatchCriteria.Type == MatchType::Exact); REQUIRE(results.Matches[0].MatchCriteria.Value == manifest.Id); } @@ -84,10 +84,10 @@ TEST_CASE("SQLiteIndexSource_Id", "[sqliteindexsource]") auto results = source->Search(request); REQUIRE(results.Matches.size() == 1); - REQUIRE(results.Matches[0].Application); - IApplication* app = results.Matches[0].Application.get(); + REQUIRE(results.Matches[0].Package); + auto latestVersion = results.Matches[0].Package->GetLatestAvailableVersion(); - REQUIRE(app->GetId().get() == manifest.Id); + REQUIRE(latestVersion->GetProperty(PackageVersionProperty::Id).get() == manifest.Id); } TEST_CASE("SQLiteIndexSource_Name", "[sqliteindexsource]") @@ -105,10 +105,10 @@ TEST_CASE("SQLiteIndexSource_Name", "[sqliteindexsource]") auto results = source->Search(request); REQUIRE(results.Matches.size() == 1); - REQUIRE(results.Matches[0].Application); - IApplication* app = results.Matches[0].Application.get(); + REQUIRE(results.Matches[0].Package); + auto latestVersion = results.Matches[0].Package->GetLatestAvailableVersion(); - REQUIRE(app->GetName().get() == manifest.Name); + REQUIRE(latestVersion->GetProperty(PackageVersionProperty::Name).get() == manifest.Name); } TEST_CASE("SQLiteIndexSource_Versions", "[sqliteindexsource]") @@ -126,13 +126,12 @@ TEST_CASE("SQLiteIndexSource_Versions", "[sqliteindexsource]") auto results = source->Search(request); REQUIRE(results.Matches.size() == 1); - REQUIRE(results.Matches[0].Application); - IApplication* app = results.Matches[0].Application.get(); + REQUIRE(results.Matches[0].Package); - auto result = app->GetVersions(); + auto result = results.Matches[0].Package->GetAvailableVersionKeys(); REQUIRE(result.size() == 1); - REQUIRE(result[0].GetVersion().ToString() == manifest.Version); - REQUIRE(result[0].GetChannel().ToString() == manifest.Channel); + REQUIRE(result[0].Version == manifest.Version); + REQUIRE(result[0].Channel == manifest.Channel); } TEST_CASE("SQLiteIndexSource_GetManifest", "[sqliteindexsource]") @@ -150,23 +149,25 @@ TEST_CASE("SQLiteIndexSource_GetManifest", "[sqliteindexsource]") auto results = source->Search(request); REQUIRE(results.Matches.size() == 1); - REQUIRE(results.Matches[0].Application); - IApplication* app = results.Matches[0].Application.get(); - - auto specificResult = app->GetManifest(manifest.Version, manifest.Channel); - REQUIRE(specificResult.has_value()); - REQUIRE(specificResult->Id == manifest.Id); - REQUIRE(specificResult->Name == manifest.Name); - REQUIRE(specificResult->Version == manifest.Version); - REQUIRE(specificResult->Channel == manifest.Channel); - - auto latestResult = app->GetManifest("", manifest.Channel); - REQUIRE(latestResult.has_value()); - REQUIRE(latestResult->Id == manifest.Id); - REQUIRE(latestResult->Name == manifest.Name); - REQUIRE(latestResult->Version == manifest.Version); - REQUIRE(latestResult->Channel == manifest.Channel); - - auto noResult = app->GetManifest("blargle", "flargle"); - REQUIRE(!noResult.has_value()); + REQUIRE(results.Matches[0].Package); + auto package = results.Matches[0].Package.get(); + + auto specificResultVersion = package->GetAvailableVersion(PackageVersionKey("", manifest.Version, manifest.Channel)); + REQUIRE(specificResultVersion); + auto specificResult = specificResultVersion->GetManifest(); + REQUIRE(specificResult.Id == manifest.Id); + REQUIRE(specificResult.Name == manifest.Name); + REQUIRE(specificResult.Version == manifest.Version); + REQUIRE(specificResult.Channel == manifest.Channel); + + auto latestResultVersion = package->GetAvailableVersion(PackageVersionKey("", "", manifest.Channel)); + REQUIRE(latestResultVersion); + auto latestResult = latestResultVersion->GetManifest(); + REQUIRE(latestResult.Id == manifest.Id); + REQUIRE(latestResult.Name == manifest.Name); + REQUIRE(latestResult.Version == manifest.Version); + REQUIRE(latestResult.Channel == manifest.Channel); + + auto noResultVersion = package->GetAvailableVersion(PackageVersionKey("", "blargle", "flargle")); + REQUIRE(!noResultVersion); } diff --git a/src/AppInstallerCLITests/Sources.cpp b/src/AppInstallerCLITests/Sources.cpp @@ -126,21 +126,27 @@ namespace return m_details; } - SearchResult Search(const SearchRequest& request) override + const std::string& GetIdentifier() const override + { + return m_identifier; + } + + SearchResult Search(const SearchRequest& request) const override { UNREFERENCED_PARAMETER(request); SearchResult result; - ApplicationMatchFilter testMatchFilter1{ ApplicationMatchField::Id, MatchType::Exact, "test" }; - ApplicationMatchFilter testMatchFilter2{ ApplicationMatchField::Name, MatchType::Exact, "test" }; - ApplicationMatchFilter testMatchFilter3{ ApplicationMatchField::Id, MatchType::CaseInsensitive, "test" }; - result.Matches.emplace_back(std::unique_ptr<IApplication>(), testMatchFilter1); - result.Matches.emplace_back(std::unique_ptr<IApplication>(), testMatchFilter2); - result.Matches.emplace_back(std::unique_ptr<IApplication>(), testMatchFilter3); + PackageMatchFilter testMatchFilter1{ PackageMatchField::Id, MatchType::Exact, "test" }; + PackageMatchFilter testMatchFilter2{ PackageMatchField::Name, MatchType::Exact, "test" }; + PackageMatchFilter testMatchFilter3{ PackageMatchField::Id, MatchType::CaseInsensitive, "test" }; + result.Matches.emplace_back(std::unique_ptr<IPackage>(), testMatchFilter1); + result.Matches.emplace_back(std::unique_ptr<IPackage>(), testMatchFilter2); + result.Matches.emplace_back(std::unique_ptr<IPackage>(), testMatchFilter3); return result; } SourceDetails m_details; + std::string m_identifier = "*TestSource"; }; // Helper that allows some lambdas to be wrapped into a source factory. @@ -591,12 +597,12 @@ TEST_CASE("RepoSources_SearchAcrossMultipleSources", "[sources]") REQUIRE(result.Matches.size() == 6); REQUIRE_FALSE(result.Truncated); // matches are sorted in expected order - REQUIRE((result.Matches[0].MatchCriteria.Type == MatchType::Exact && result.Matches[0].MatchCriteria.Field == ApplicationMatchField::Id)); - REQUIRE((result.Matches[1].MatchCriteria.Type == MatchType::Exact && result.Matches[1].MatchCriteria.Field == ApplicationMatchField::Id)); - REQUIRE((result.Matches[2].MatchCriteria.Type == MatchType::Exact && result.Matches[2].MatchCriteria.Field == ApplicationMatchField::Name)); - REQUIRE((result.Matches[3].MatchCriteria.Type == MatchType::Exact && result.Matches[3].MatchCriteria.Field == ApplicationMatchField::Name)); - REQUIRE((result.Matches[4].MatchCriteria.Type == MatchType::CaseInsensitive && result.Matches[4].MatchCriteria.Field == ApplicationMatchField::Id)); - REQUIRE((result.Matches[5].MatchCriteria.Type == MatchType::CaseInsensitive && result.Matches[5].MatchCriteria.Field == ApplicationMatchField::Id)); + REQUIRE((result.Matches[0].MatchCriteria.Type == MatchType::Exact && result.Matches[0].MatchCriteria.Field == PackageMatchField::Id)); + REQUIRE((result.Matches[1].MatchCriteria.Type == MatchType::Exact && result.Matches[1].MatchCriteria.Field == PackageMatchField::Id)); + REQUIRE((result.Matches[2].MatchCriteria.Type == MatchType::Exact && result.Matches[2].MatchCriteria.Field == PackageMatchField::Name)); + REQUIRE((result.Matches[3].MatchCriteria.Type == MatchType::Exact && result.Matches[3].MatchCriteria.Field == PackageMatchField::Name)); + REQUIRE((result.Matches[4].MatchCriteria.Type == MatchType::CaseInsensitive && result.Matches[4].MatchCriteria.Field == PackageMatchField::Id)); + REQUIRE((result.Matches[5].MatchCriteria.Type == MatchType::CaseInsensitive && result.Matches[5].MatchCriteria.Field == PackageMatchField::Id)); // when truncate required request.MaximumResults = 3; @@ -604,7 +610,7 @@ TEST_CASE("RepoSources_SearchAcrossMultipleSources", "[sources]") REQUIRE(result.Matches.size() == 3); REQUIRE(result.Truncated); // matches are sorted in expected order - REQUIRE((result.Matches[0].MatchCriteria.Type == MatchType::Exact && result.Matches[0].MatchCriteria.Field == ApplicationMatchField::Id)); - REQUIRE((result.Matches[1].MatchCriteria.Type == MatchType::Exact && result.Matches[1].MatchCriteria.Field == ApplicationMatchField::Id)); - REQUIRE((result.Matches[2].MatchCriteria.Type == MatchType::Exact && result.Matches[2].MatchCriteria.Field == ApplicationMatchField::Name)); + REQUIRE((result.Matches[0].MatchCriteria.Type == MatchType::Exact && result.Matches[0].MatchCriteria.Field == PackageMatchField::Id)); + REQUIRE((result.Matches[1].MatchCriteria.Type == MatchType::Exact && result.Matches[1].MatchCriteria.Field == PackageMatchField::Id)); + REQUIRE((result.Matches[2].MatchCriteria.Type == MatchType::Exact && result.Matches[2].MatchCriteria.Field == PackageMatchField::Name)); } \ No newline at end of file diff --git a/src/AppInstallerCLITests/WorkFlow.cpp b/src/AppInstallerCLITests/WorkFlow.cpp @@ -36,36 +36,71 @@ namespace { struct TestSource : public ISource { - struct TestApplication : public IApplication + struct TestPackageVersion : public IPackageVersion { - TestApplication(const Manifest manifest) : m_manifest(manifest) {} + TestPackageVersion(const Manifest& manifest) : m_manifest(manifest) {} - std::optional<Manifest> GetManifest(const NormalizedString&, const NormalizedString&) override + LocIndString GetProperty(PackageVersionProperty property) const override + { + switch (property) + { + case PackageVersionProperty::Id: + return LocIndString{ m_manifest.Id }; + case PackageVersionProperty::Name: + return LocIndString{ m_manifest.Name }; + case PackageVersionProperty::Version: + return LocIndString{ m_manifest.Version }; + case PackageVersionProperty::Channel: + return LocIndString{ m_manifest.Channel }; + default: + return {}; + } + } + + Manifest GetManifest() const override { return m_manifest; } - LocIndString GetId() override + Manifest m_manifest; + }; + + struct TestPackage : public IPackage + { + TestPackage(const Manifest& manifest) : m_manifest(manifest) {} + + std::shared_ptr<IPackageVersion> GetInstalledVersion() const override + { + return {}; + } + + std::vector<PackageVersionKey> GetAvailableVersionKeys() const override { - return LocIndString{ m_manifest.Id }; + return { { "", m_manifest.Version, m_manifest.Channel } }; } - LocIndString GetName() override + std::shared_ptr<IPackageVersion> GetLatestAvailableVersion() const override { - return LocIndString{ m_manifest.Name }; + return std::make_shared<TestPackageVersion>(m_manifest); } - std::vector<VersionAndChannel> GetVersions() override + std::shared_ptr<IPackageVersion> GetAvailableVersion(const PackageVersionKey& versionKey) const override { - std::vector<VersionAndChannel> result; - result.emplace_back(Version(m_manifest.Version), Channel(m_manifest.Channel)); - return result; + if ((versionKey.Version.empty() || versionKey.Version == m_manifest.Version) && + (versionKey.Channel.empty() || versionKey.Channel == m_manifest.Channel)) + { + return std::make_shared<TestPackageVersion>(m_manifest); + } + else + { + return {}; + } } Manifest m_manifest; }; - SearchResult Search(const SearchRequest& request) override + SearchResult Search(const SearchRequest& request) const override { SearchResult result; @@ -85,28 +120,30 @@ namespace auto manifest = YamlParser::CreateFromPath(TestDataFile("InstallFlowTest_Exe.yaml")); result.Matches.emplace_back( ResultMatch( - std::make_unique<TestApplication>(manifest), - ApplicationMatchFilter(ApplicationMatchField::Id, MatchType::Exact, "TestQueryReturnOne"))); + std::make_unique<TestPackage>(manifest), + PackageMatchFilter(PackageMatchField::Id, MatchType::Exact, "TestQueryReturnOne"))); } else if (input == "TestQueryReturnTwo") { auto manifest = YamlParser::CreateFromPath(TestDataFile("InstallFlowTest_Exe.yaml")); result.Matches.emplace_back( ResultMatch( - std::make_unique<TestApplication>(manifest), - ApplicationMatchFilter(ApplicationMatchField::Id, MatchType::Exact, "TestQueryReturnTwo"))); + std::make_unique<TestPackage>(manifest), + PackageMatchFilter(PackageMatchField::Id, MatchType::Exact, "TestQueryReturnTwo"))); auto manifest2 = YamlParser::CreateFromPath(TestDataFile("Manifest-Good.yaml")); result.Matches.emplace_back( ResultMatch( - std::make_unique<TestApplication>(manifest2), - ApplicationMatchFilter(ApplicationMatchField::Id, MatchType::Exact, "TestQueryReturnTwo"))); + std::make_unique<TestPackage>(manifest2), + PackageMatchFilter(PackageMatchField::Id, MatchType::Exact, "TestQueryReturnTwo"))); } return result; } const SourceDetails& GetDetails() const override { THROW_HR(E_NOTIMPL); } + + const std::string& GetIdentifier() const override { THROW_HR(E_NOTIMPL); } }; struct TestContext; diff --git a/src/AppInstallerRepositoryCore/AggregatedSource.cpp b/src/AppInstallerRepositoryCore/AggregatedSource.cpp @@ -5,23 +5,24 @@ namespace AppInstaller::Repository { - AggregatedSource::AggregatedSource() + AggregatedSource::AggregatedSource(std::string identifier) : + m_identifier(identifier) { m_details.Name = "AggregatedSource"; m_details.IsAggregated = true; } - const SourceDetails& AppInstaller::Repository::AggregatedSource::GetDetails() const + const SourceDetails& AggregatedSource::GetDetails() const { return m_details; } - void AggregatedSource::AddSource(std::shared_ptr<ISource> source) + const std::string& AggregatedSource::GetIdentifier() const { - m_sources.emplace_back(std::move(source)); + return m_identifier; } - SearchResult AggregatedSource::Search(const SearchRequest& request) + SearchResult AggregatedSource::Search(const SearchRequest& request) const { SearchResult result; @@ -47,6 +48,11 @@ namespace AppInstaller::Repository return result; } + void AggregatedSource::AddSource(std::shared_ptr<ISource> source) + { + m_sources.emplace_back(std::move(source)); + } + void AggregatedSource::SortResultMatches(std::vector<ResultMatch>& matches) { struct ResultMatchComparator diff --git a/src/AppInstallerRepositoryCore/AggregatedSource.h b/src/AppInstallerRepositoryCore/AggregatedSource.h @@ -8,7 +8,7 @@ namespace AppInstaller::Repository { struct AggregatedSource : public ISource { - AggregatedSource(); + explicit AggregatedSource(std::string identifier); AggregatedSource(const AggregatedSource&) = delete; AggregatedSource& operator=(const AggregatedSource&) = delete; @@ -21,16 +21,24 @@ namespace AppInstaller::Repository // Get the source's details. const SourceDetails& GetDetails() const override; + // Gets the source's identifier; a unique identifier independent of the name + // that will not change between a remove/add or between additional adds. + // Must be suitable for filesystem names. + const std::string& GetIdentifier() const override; + // Execute a search on the source. - SearchResult Search(const SearchRequest & request) override; + SearchResult Search(const SearchRequest & request) const override; + // Adds a source to be aggregated. void AddSource(std::shared_ptr<ISource> source); private: std::vector<std::shared_ptr<ISource>> m_sources; SourceDetails m_details; + std::string m_identifier; - void SortResultMatches(std::vector<ResultMatch>& matches); + // Sorts a vector of results. + static void SortResultMatches(std::vector<ResultMatch>& matches); }; } diff --git a/src/AppInstallerRepositoryCore/Microsoft/PreIndexedPackageSourceFactory.cpp b/src/AppInstallerRepositoryCore/Microsoft/PreIndexedPackageSourceFactory.cpp @@ -146,7 +146,7 @@ namespace AppInstaller::Repository::Microsoft SQLiteIndex index = SQLiteIndex::Open(indexLocation.u8string(), SQLiteIndex::OpenDisposition::Immutable); - return std::make_shared<SQLiteIndexSource>(details, std::move(index), std::move(lock)); + return std::make_shared<SQLiteIndexSource>(details, GetPackageFamilyNameFromDetails(details), std::move(index), std::move(lock)); } void UpdateInternal(std::string packageLocation, const SourceDetails& details, IProgressCallback& progress) override @@ -251,7 +251,7 @@ namespace AppInstaller::Repository::Microsoft SQLiteIndex index = SQLiteIndex::Open(packageLocation.u8string(), SQLiteIndex::OpenDisposition::Read); - return std::make_shared<SQLiteIndexSource>(details, std::move(index), std::move(lock)); + return std::make_shared<SQLiteIndexSource>(details, GetPackageFamilyNameFromDetails(details), std::move(index), std::move(lock)); } void UpdateInternal(std::string packageLocation, const SourceDetails& details, IProgressCallback& progress) override diff --git a/src/AppInstallerRepositoryCore/Microsoft/SQLiteIndex.cpp b/src/AppInstallerRepositoryCore/Microsoft/SQLiteIndex.cpp @@ -206,31 +206,26 @@ namespace AppInstaller::Repository::Microsoft m_interface->PrepareForPackaging(m_dbconn); } - Schema::ISQLiteIndex::SearchResult SQLiteIndex::Search(const SearchRequest& request) + Schema::ISQLiteIndex::SearchResult SQLiteIndex::Search(const SearchRequest& request) const { AICLI_LOG(Repo, Info, << "Performing search: " << request.ToString()); return m_interface->Search(m_dbconn, request); } - std::optional<std::string> SQLiteIndex::GetIdStringById(IdType id) + std::optional<std::string> SQLiteIndex::GetPropertyByManifestId(IdType manifestId, PackageVersionProperty property) const { - return m_interface->GetIdStringById(m_dbconn, id); + return m_interface->GetPropertyByManifestId(m_dbconn, manifestId, property); } - std::optional<std::string> SQLiteIndex::GetNameStringById(IdType id) + std::optional<SQLiteIndex::IdType> SQLiteIndex::GetManifestIdByKey(IdType id, std::string_view version, std::string_view channel) const { - return m_interface->GetNameStringById(m_dbconn, id); + return m_interface->GetManifestIdByKey(m_dbconn, id, version, channel); } - std::optional<std::string> SQLiteIndex::GetPathStringByKey(IdType id, std::string_view version, std::string_view channel) + std::vector<Utility::VersionAndChannel> SQLiteIndex::GetVersionKeysById(IdType id) const { - return m_interface->GetPathStringByKey(m_dbconn, id, version, channel); - } - - std::vector<Utility::VersionAndChannel> SQLiteIndex::GetVersionsById(IdType id) - { - return m_interface->GetVersionsById(m_dbconn, id); + return m_interface->GetVersionKeysById(m_dbconn, id); } // Recording last write time based on MSDN documentation stating that time returns a POSIX epoch time and thus diff --git a/src/AppInstallerRepositoryCore/Microsoft/SQLiteIndex.h b/src/AppInstallerRepositoryCore/Microsoft/SQLiteIndex.h @@ -88,20 +88,17 @@ namespace AppInstaller::Repository::Microsoft void PrepareForPackaging(); // Performs a search based on the given criteria. - Schema::ISQLiteIndex::SearchResult Search(const SearchRequest& request); + Schema::ISQLiteIndex::SearchResult Search(const SearchRequest& request) const; - // Gets the Id string for the given id, if present. - std::optional<std::string> GetIdStringById(IdType id); + // Gets the string for the given property and manifest id, if present. + std::optional<std::string> GetPropertyByManifestId(IdType manifestId, PackageVersionProperty property) const; - // Gets the Name string for the given id, if present. - std::optional<std::string> GetNameStringById(IdType id); - - // Gets the relative path string for the given { id, version, channel }, if present. + // Gets the manifest id for the given { id, version, channel }, if present. // If version is empty, gets the value for the 'latest' version. - std::optional<std::string> GetPathStringByKey(IdType id, std::string_view version, std::string_view channel); + std::optional<IdType> GetManifestIdByKey(IdType id, std::string_view version, std::string_view channel) const; // Gets all versions and channels for the given id. - std::vector<Utility::VersionAndChannel> GetVersionsById(IdType id); + std::vector<Utility::VersionAndChannel> GetVersionKeysById(IdType id) const; private: // Constructor used to open an existing index. diff --git a/src/AppInstallerRepositoryCore/Microsoft/SQLiteIndexSource.cpp b/src/AppInstallerRepositoryCore/Microsoft/SQLiteIndexSource.cpp @@ -12,36 +12,55 @@ namespace AppInstaller::Repository::Microsoft namespace { - // The IApplication impl for SQLiteIndexSource. - struct Application : public IApplication + // The base for the package objects. + struct SourceReference { - Application(std::shared_ptr<SQLiteIndexSource>& source, SQLiteIndex::IdType id) : - m_id(id), m_source(source) {} + SourceReference(const std::shared_ptr<const SQLiteIndexSource>& source) : + m_source(source) {} - // Inherited via IApplication - LocIndString GetId() override + protected: + std::shared_ptr<const SQLiteIndexSource> GetSource() const { - // Values coming from the index will always be localized/independent. - return LocIndString{ GetSource()->GetIndex().GetIdStringById(m_id).value() }; + std::shared_ptr<const SQLiteIndexSource> source = m_source.lock(); + THROW_HR_IF(E_NOT_VALID_STATE, !source); + return source; } - LocIndString GetName() override - { - // Values coming from the index will always be localized/independent. - return LocIndString{ GetSource()->GetIndex().GetNameStringById(m_id).value() }; - } + private: + std::weak_ptr<const SQLiteIndexSource> m_source; + }; - std::optional<Manifest::Manifest> GetManifest(const Utility::NormalizedString& version, const Utility::NormalizedString& channel) override + // The IPackageVersion impl for SQLiteIndexSource. + struct PackageVersion : public SourceReference, public IPackageVersion + { + PackageVersion(const std::shared_ptr<const SQLiteIndexSource>& source, SQLiteIndex::IdType manifestId) : + SourceReference(source), m_manifestId(manifestId) {} + + // Inherited via IPackageVersion + Utility::LocIndString GetProperty(PackageVersionProperty property) const override { - std::shared_ptr<SQLiteIndexSource> source = GetSource(); - std::optional<std::string> relativePathOpt = source->GetIndex().GetPathStringByKey(m_id, version, channel); - if (!relativePathOpt) + switch (property) { - return {}; + case PackageVersionProperty::SourceId: + return LocIndString{ GetSource()->GetIdentifier() }; + default: + // Values coming from the index will always be localized/independent. + return LocIndString{ GetSource()->GetIndex().GetPropertyByManifestId(m_manifestId, property).value() }; } - std::string relativePath = relativePathOpt.value(); + } - std::string fullPath = source->GetDetails().Arg; + Manifest::Manifest GetManifest() const override + { + std::shared_ptr<const SQLiteIndexSource> source = GetSource(); + std::optional<std::string> relativePathOpt = source->GetIndex().GetPropertyByManifestId(m_manifestId, PackageVersionProperty::RelativePath); + THROW_HR_IF(E_NOT_SET, !relativePathOpt); + return GetManifestFromArgAndRelativePath(source->GetDetails().Arg, relativePathOpt.value()); + } + + private: + static Manifest::Manifest GetManifestFromArgAndRelativePath(const std::string& arg, const std::string& relativePath) + { + std::string fullPath = arg; if (fullPath.back() != '/') { fullPath += '/'; @@ -68,26 +87,70 @@ namespace AppInstaller::Repository::Microsoft } } - std::vector<Utility::VersionAndChannel> GetVersions() override + SQLiteIndex::IdType m_manifestId; + }; + + // The IPackage impl for SQLiteIndexSource. + struct Package : public SourceReference, public IPackage + { + Package(const std::shared_ptr<const SQLiteIndexSource>& source, SQLiteIndex::IdType idId) : + SourceReference(source), m_idId(idId) {} + + // Inherited via IPackage + std::shared_ptr<IPackageVersion> GetInstalledVersion() const override { - return GetSource()->GetIndex().GetVersionsById(m_id); + // Although an index might be the backing store for installed packages, the installed package version + // will be selected by external business logic. + return {}; } - private: - std::shared_ptr<SQLiteIndexSource> GetSource() + std::vector<PackageVersionKey> GetAvailableVersionKeys() const override { - std::shared_ptr<SQLiteIndexSource> source = m_source.lock(); - THROW_HR_IF(E_NOT_VALID_STATE, !source); - return source; + std::shared_ptr<const SQLiteIndexSource> source = GetSource(); + std::vector<Utility::VersionAndChannel> versions = source->GetIndex().GetVersionKeysById(m_idId); + + std::vector<PackageVersionKey> result; + for (const auto& vac : versions) + { + result.emplace_back(source->GetIdentifier(), vac.GetVersion().ToString(), vac.GetChannel().ToString()); + } + return result; } - std::weak_ptr<SQLiteIndexSource> m_source; - SQLiteIndex::IdType m_id; + std::shared_ptr<IPackageVersion> GetLatestAvailableVersion() const override + { + // Although we could potentially increase efficiency here, this should be fine. + std::vector<PackageVersionKey> versions = GetAvailableVersionKeys(); + + if (!versions.empty()) + { + return GetAvailableVersion(versions[0]); + } + + return {}; + } + + std::shared_ptr<IPackageVersion> GetAvailableVersion(const PackageVersionKey& versionKey) const override + { + std::shared_ptr<const SQLiteIndexSource> source = GetSource(); + THROW_HR_IF(E_INVALIDARG, !versionKey.SourceId.empty() && versionKey.SourceId != source->GetIdentifier()); + std::optional<SQLiteIndex::IdType> manifestId = source->GetIndex().GetManifestIdByKey(m_idId, versionKey.Version, versionKey.Channel); + + if (manifestId) + { + return std::make_shared<PackageVersion>(source, manifestId.value()); + } + + return {}; + } + + private: + SQLiteIndex::IdType m_idId; }; } - SQLiteIndexSource::SQLiteIndexSource(const SourceDetails& details, SQLiteIndex&& index, Synchronization::CrossProcessReaderWriteLock&& lock) : - m_details(details), m_lock(std::move(lock)), m_index(std::move(index)) + SQLiteIndexSource::SQLiteIndexSource(const SourceDetails& details, std::string identifier, SQLiteIndex&& index, Synchronization::CrossProcessReaderWriteLock&& lock) : + m_details(details), m_identifier(std::move(identifier)), m_lock(std::move(lock)), m_index(std::move(index)) { } @@ -96,15 +159,20 @@ namespace AppInstaller::Repository::Microsoft return m_details; } - SearchResult SQLiteIndexSource::Search(const SearchRequest& request) + const std::string& SQLiteIndexSource::GetIdentifier() const + { + return m_identifier; + } + + SearchResult SQLiteIndexSource::Search(const SearchRequest& request) const { auto indexResults = m_index.Search(request); SearchResult result; - std::shared_ptr<SQLiteIndexSource> sharedThis = shared_from_this(); + std::shared_ptr<const SQLiteIndexSource> sharedThis = shared_from_this(); for (auto& indexResult : indexResults.Matches) { - result.Matches.emplace_back(std::make_unique<Application>(sharedThis, indexResult.first), std::move(indexResult.second)); + result.Matches.emplace_back(std::make_unique<Package>(sharedThis, indexResult.first), std::move(indexResult.second)); } result.Truncated = indexResults.Truncated; return result; diff --git a/src/AppInstallerRepositoryCore/Microsoft/SQLiteIndexSource.h b/src/AppInstallerRepositoryCore/Microsoft/SQLiteIndexSource.h @@ -13,7 +13,7 @@ namespace AppInstaller::Repository::Microsoft // A source that holds a SQLiteIndex and lock. struct SQLiteIndexSource : public std::enable_shared_from_this<SQLiteIndexSource>, public ISource { - SQLiteIndexSource(const SourceDetails& details, SQLiteIndex&& index, Synchronization::CrossProcessReaderWriteLock&& lock = {}); + SQLiteIndexSource(const SourceDetails& details, std::string identifier, SQLiteIndex&& index, Synchronization::CrossProcessReaderWriteLock&& lock = {}); SQLiteIndexSource(const SQLiteIndexSource&) = delete; SQLiteIndexSource& operator=(const SQLiteIndexSource&) = delete; @@ -26,14 +26,20 @@ namespace AppInstaller::Repository::Microsoft // Get the source's details. const SourceDetails& GetDetails() const override; + // Gets the source's identifier; a unique identifier independent of the name + // that will not change between a remove/add or between additional adds. + // Must be suitable for filesystem names. + const std::string& GetIdentifier() const override; + // Execute a search on the source. - SearchResult Search(const SearchRequest& request) override; + SearchResult Search(const SearchRequest& request) const override; // Gets the index. - SQLiteIndex& GetIndex() { return m_index; } + const SQLiteIndex& GetIndex() const { return m_index; } private: SourceDetails m_details; + std::string m_identifier; Synchronization::CrossProcessReaderWriteLock m_lock; SQLiteIndex m_index; }; diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/Interface.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/Interface.h @@ -20,15 +20,14 @@ namespace AppInstaller::Repository::Microsoft::Schema::V1_0 std::pair<bool, SQLite::rowid_t> UpdateManifest(SQLite::Connection& connection, const Manifest::Manifest& manifest, const std::filesystem::path& relativePath) override; SQLite::rowid_t RemoveManifest(SQLite::Connection& connection, const Manifest::Manifest& manifest, const std::filesystem::path& relativePath) override; void PrepareForPackaging(SQLite::Connection& connection) override; - SearchResult Search(SQLite::Connection& connection, const SearchRequest& request) override; - std::optional<std::string> GetIdStringById(SQLite::Connection& connection, SQLite::rowid_t id) override; - std::optional<std::string> GetNameStringById(SQLite::Connection& connection, SQLite::rowid_t id) override; - std::optional<std::string> GetPathStringByKey(SQLite::Connection& connection, SQLite::rowid_t id, std::string_view version, std::string_view channel) override; - std::vector<Utility::VersionAndChannel> GetVersionsById(SQLite::Connection& connection, SQLite::rowid_t id) override; + SearchResult Search(const SQLite::Connection& connection, const SearchRequest& request) const override; + std::optional<std::string> GetPropertyByManifestId(const SQLite::Connection& connection, SQLite::rowid_t manifestId, PackageVersionProperty property) const override; + std::optional<SQLite::rowid_t> GetManifestIdByKey(const SQLite::Connection& connection, SQLite::rowid_t id, std::string_view version, std::string_view channel) const override; + std::vector<Utility::VersionAndChannel> GetVersionKeysById(const SQLite::Connection& connection, SQLite::rowid_t id) const override; protected: // Creates the search results table. - virtual std::unique_ptr<SearchResultsTable> CreateSearchResultsTable(SQLite::Connection& connection) const; + virtual std::unique_ptr<SearchResultsTable> CreateSearchResultsTable(const SQLite::Connection& connection) const; // Gets the ordering of matches to execute, with more specific matches coming first. virtual std::vector<MatchType> GetMatchTypeOrder(MatchType type) const; diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/Interface_1_0.cpp b/src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/Interface_1_0.cpp @@ -58,7 +58,7 @@ namespace AppInstaller::Repository::Microsoft::Schema::V1_0 } // Gets a manifest id by the given key values. - std::optional<SQLite::rowid_t> GetManifestIdByKey(SQLite::Connection& connection, SQLite::rowid_t id, std::string_view version = "", std::string_view channel = "") + std::optional<SQLite::rowid_t> StaticGetManifestIdByKey(const SQLite::Connection& connection, SQLite::rowid_t id, std::string_view version = "", std::string_view channel = "") { std::optional<SQLite::rowid_t> channelIdOpt = ChannelTable::SelectIdByValue(connection, channel, true); if (!channelIdOpt && !channel.empty()) @@ -348,7 +348,7 @@ namespace AppInstaller::Repository::Microsoft::Schema::V1_0 builder.Execute(connection); } - ISQLiteIndex::SearchResult Interface::Search(SQLite::Connection& connection, const SearchRequest& request) + ISQLiteIndex::SearchResult Interface::Search(const SQLite::Connection& connection, const SearchRequest& request) const { // If an empty request, get everything if (!request.Query && request.Inclusions.empty() && request.Filters.empty()) @@ -358,7 +358,7 @@ namespace AppInstaller::Repository::Microsoft::Schema::V1_0 SearchResult result; for (SQLite::rowid_t id : ids) { - result.Matches.emplace_back(std::make_pair(id, ApplicationMatchFilter(ApplicationMatchField::Id, MatchType::Wildcard, {}))); + result.Matches.emplace_back(std::make_pair(id, PackageMatchFilter(PackageMatchField::Id, MatchType::Wildcard, {}))); } result.Truncated = (request.MaximumResults && IdTable::GetCount(connection) > request.MaximumResults); @@ -400,7 +400,7 @@ namespace AppInstaller::Repository::Microsoft::Schema::V1_0 THROW_HR_IF(E_UNEXPECTED, request.Filters.empty()); // Perform search for just the field matching the first filter - const ApplicationMatchFilter& filter = request.Filters[0]; + const PackageMatchFilter& filter = request.Filters[0]; for (MatchType match : GetMatchTypeOrder(filter.Type)) { @@ -417,7 +417,7 @@ namespace AppInstaller::Repository::Microsoft::Schema::V1_0 // Second phase, for remaining filters, flag matching search results, then remove unflagged values. for (size_t i = filterIndex; i < request.Filters.size(); ++i) { - const ApplicationMatchFilter& filter = request.Filters[i]; + const PackageMatchFilter& filter = request.Filters[i]; resultsTable->PrepareToFilter(); @@ -432,41 +432,37 @@ namespace AppInstaller::Repository::Microsoft::Schema::V1_0 return resultsTable->GetSearchResults(request.MaximumResults); } - std::optional<std::string> Interface::GetIdStringById(SQLite::Connection& connection, SQLite::rowid_t id) + std::optional<std::string> Interface::GetPropertyByManifestId(const SQLite::Connection& connection, SQLite::rowid_t manifestId, PackageVersionProperty property) const { - return IdTable::SelectValueById(connection, id); - } - - std::optional<std::string> Interface::GetNameStringById(SQLite::Connection& connection, SQLite::rowid_t id) - { - std::optional<SQLite::rowid_t> manifestIdOpt = GetManifestIdByKey(connection, id); - - if (!manifestIdOpt) + if (!ManifestTable::ExistsById(connection, manifestId)) { - AICLI_LOG(Repo, Info, << "Did not find manifest by Id id: " << id); + AICLI_LOG(Repo, Info, << "Did not find manifest by id: " << manifestId); return {}; } - auto [name] = ManifestTable::GetValuesById<NameTable>(connection, manifestIdOpt.value()); - return name; - } - - std::optional<std::string> Interface::GetPathStringByKey(SQLite::Connection& connection, SQLite::rowid_t id, std::string_view version, std::string_view channel) - { - std::optional<SQLite::rowid_t> manifestIdOpt = GetManifestIdByKey(connection, id, version, channel); - - if (!manifestIdOpt) + switch (property) { - AICLI_LOG(Repo, Info, << "Did not find manifest for: " << id << ", " << version << ", " << channel); + case AppInstaller::Repository::PackageVersionProperty::Id: + return std::get<0>(ManifestTable::GetValuesById<IdTable>(connection, manifestId)); + case AppInstaller::Repository::PackageVersionProperty::Name: + return std::get<0>(ManifestTable::GetValuesById<NameTable>(connection, manifestId)); + case AppInstaller::Repository::PackageVersionProperty::Version: + return std::get<0>(ManifestTable::GetValuesById<VersionTable>(connection, manifestId)); + case AppInstaller::Repository::PackageVersionProperty::Channel: + return std::get<0>(ManifestTable::GetValuesById<ChannelTable>(connection, manifestId)); + case AppInstaller::Repository::PackageVersionProperty::RelativePath: + return PathPartTable::GetPathById(connection, std::get<0>(ManifestTable::GetIdsById<PathPartTable>(connection, manifestId))); + default: return {}; } + } - auto [pathPartId] = ManifestTable::GetIdsById<PathPartTable>(connection, manifestIdOpt.value()); - - return PathPartTable::GetPathById(connection, pathPartId); + std::optional<SQLite::rowid_t> Interface::GetManifestIdByKey(const SQLite::Connection& connection, SQLite::rowid_t id, std::string_view version, std::string_view channel) const + { + return StaticGetManifestIdByKey(connection, id, version, channel); } - std::vector<Utility::VersionAndChannel> Interface::GetVersionsById(SQLite::Connection& connection, SQLite::rowid_t id) + std::vector<Utility::VersionAndChannel> Interface::GetVersionKeysById(const SQLite::Connection& connection, SQLite::rowid_t id) const { auto versionsAndChannels = ManifestTable::GetAllValuesById<IdTable, VersionTable, ChannelTable>(connection, id); @@ -482,7 +478,7 @@ namespace AppInstaller::Repository::Microsoft::Schema::V1_0 return result; } - std::unique_ptr<SearchResultsTable> Interface::CreateSearchResultsTable(SQLite::Connection& connection) const + std::unique_ptr<SearchResultsTable> Interface::CreateSearchResultsTable(const SQLite::Connection& connection) const { return std::make_unique<SearchResultsTable>(connection); } @@ -514,11 +510,11 @@ namespace AppInstaller::Repository::Microsoft::Schema::V1_0 { for (MatchType match : GetMatchTypeOrder(query.Type)) { - resultsTable.SearchOnField(ApplicationMatchField::Id, match, query.Value); - resultsTable.SearchOnField(ApplicationMatchField::Name, match, query.Value); - resultsTable.SearchOnField(ApplicationMatchField::Moniker, match, query.Value); - resultsTable.SearchOnField(ApplicationMatchField::Command, match, query.Value); - resultsTable.SearchOnField(ApplicationMatchField::Tag, match, query.Value); + resultsTable.SearchOnField(PackageMatchField::Id, match, query.Value); + resultsTable.SearchOnField(PackageMatchField::Name, match, query.Value); + resultsTable.SearchOnField(PackageMatchField::Moniker, match, query.Value); + resultsTable.SearchOnField(PackageMatchField::Command, match, query.Value); + resultsTable.SearchOnField(PackageMatchField::Tag, match, query.Value); } } } diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/ManifestTable.cpp b/src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/ManifestTable.cpp @@ -16,7 +16,7 @@ namespace AppInstaller::Repository::Microsoft::Schema::V1_0 namespace details { std::optional<SQLite::rowid_t> ManifestTableSelectByValueIds( - SQLite::Connection& connection, + const SQLite::Connection& connection, std::initializer_list<std::string_view> values, std::initializer_list<SQLite::rowid_t> ids) { @@ -61,7 +61,7 @@ namespace AppInstaller::Repository::Microsoft::Schema::V1_0 } SQLite::Statement ManifestTableGetIdsById_Statement( - SQLite::Connection& connection, + const SQLite::Connection& connection, SQLite::rowid_t id, std::initializer_list<std::string_view> values) { @@ -81,7 +81,7 @@ namespace AppInstaller::Repository::Microsoft::Schema::V1_0 // JOIN [ids] ON [manifest].[id] = [ids].[rowid] // WHERE [manifest].[rowid] = 1 SQLite::Statement ManifestTableGetValuesById_Statement( - SQLite::Connection& connection, + const SQLite::Connection& connection, SQLite::rowid_t id, std::initializer_list<SQLite::Builder::QualifiedColumn> columns) { @@ -106,7 +106,7 @@ namespace AppInstaller::Repository::Microsoft::Schema::V1_0 } SQLite::Statement ManifestTableGetAllValuesByIds_Statement( - SQLite::Connection& connection, + const SQLite::Connection& connection, std::initializer_list<SQLite::Builder::QualifiedColumn> valueColumns, std::initializer_list<std::string_view> idColumns, std::initializer_list<SQLite::rowid_t> ids) @@ -150,7 +150,7 @@ namespace AppInstaller::Repository::Microsoft::Schema::V1_0 } std::vector<std::string> ManifestTableGetAllValuesByIds( - SQLite::Connection& connection, + const SQLite::Connection& connection, std::initializer_list<SQLite::Builder::QualifiedColumn> valueColumns, std::initializer_list<std::string_view> idColumns, std::initializer_list<SQLite::rowid_t> ids) @@ -352,6 +352,18 @@ namespace AppInstaller::Repository::Microsoft::Schema::V1_0 return connection.GetLastInsertRowID(); } + bool ManifestTable::ExistsById(const SQLite::Connection& connection, SQLite::rowid_t id) + { + SQLite::Builder::StatementBuilder builder; + builder.Select(SQLite::Builder::RowCount).From(s_ManifestTable_Table_Name).Where(SQLite::RowIDName).Equals(id); + + SQLite::Statement countStatement = builder.Prepare(connection); + + THROW_HR_IF(E_UNEXPECTED, !countStatement.Step()); + + return (countStatement.GetColumn<int>(0) != 0); + } + void ManifestTable::DeleteById(SQLite::Connection& connection, SQLite::rowid_t id) { SQLite::Builder::StatementBuilder builder; diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/ManifestTable.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/ManifestTable.h @@ -15,32 +15,32 @@ namespace AppInstaller::Repository::Microsoft::Schema::V1_0 { // Selects a manifest by the given value id. std::optional<SQLite::rowid_t> ManifestTableSelectByValueIds( - SQLite::Connection& connection, + const SQLite::Connection& connection, std::initializer_list<std::string_view> values, std::initializer_list<SQLite::rowid_t> ids); // Gets the requested ids for the manifest with the given rowid. SQLite::Statement ManifestTableGetIdsById_Statement( - SQLite::Connection& connection, + const SQLite::Connection& connection, SQLite::rowid_t id, std::initializer_list<std::string_view> values); // Gets the requested values for the manifest with the given rowid. SQLite::Statement ManifestTableGetValuesById_Statement( - SQLite::Connection& connection, + const SQLite::Connection& connection, SQLite::rowid_t id, std::initializer_list<SQLite::Builder::QualifiedColumn> columns); // Gets all values for rows that match the given ids. SQLite::Statement ManifestTableGetAllValuesByIds_Statement( - SQLite::Connection& connection, + const SQLite::Connection& connection, std::initializer_list<SQLite::Builder::QualifiedColumn> valueColumns, std::initializer_list<std::string_view> idColumns, std::initializer_list<SQLite::rowid_t> ids); // Gets all values for rows that match the given ids. std::vector<std::string> ManifestTableGetAllValuesByIds( - SQLite::Connection& connection, + const SQLite::Connection& connection, std::initializer_list<SQLite::Builder::QualifiedColumn> valueColumns, std::initializer_list<std::string_view> idColumns, std::initializer_list<SQLite::rowid_t> ids); @@ -88,9 +88,12 @@ namespace AppInstaller::Repository::Microsoft::Schema::V1_0 // Insert the given values into the table. static SQLite::rowid_t Insert(SQLite::Connection& connection, std::initializer_list<ManifestOneToOneValue> values); + // Gets a value indicating whether the manifest with rowid id exists. + static bool ExistsById(const SQLite::Connection& connection, SQLite::rowid_t id); + // Select the first rowid of the manifest with the given value. template <typename... Tables> - static std::optional<SQLite::rowid_t> SelectByValueIds(SQLite::Connection& connection, std::initializer_list<SQLite::rowid_t> ids) + static std::optional<SQLite::rowid_t> SelectByValueIds(const SQLite::Connection& connection, std::initializer_list<SQLite::rowid_t> ids) { static_assert(sizeof...(Tables) >= 1); return details::ManifestTableSelectByValueIds(connection, { Tables::ValueName()... }, ids); @@ -98,28 +101,28 @@ namespace AppInstaller::Repository::Microsoft::Schema::V1_0 // Gets the ids requested for the manifest with the given rowid. template <typename... Tables> - static auto GetIdsById(SQLite::Connection& connection, SQLite::rowid_t id) + static auto GetIdsById(const SQLite::Connection& connection, SQLite::rowid_t id) { return details::ManifestTableGetIdsById_Statement(connection, id, { Tables::ValueName()... }).GetRow<Tables::id_t...>(); } // Gets the values requested for the manifest with the given rowid. template <typename... Tables> - static auto GetValuesById(SQLite::Connection& connection, SQLite::rowid_t id) + static auto GetValuesById(const SQLite::Connection& connection, SQLite::rowid_t id) { return details::ManifestTableGetValuesById_Statement(connection, id, { SQLite::Builder::QualifiedColumn{ Tables::TableName(), Tables::ValueName() }... }).GetRow<Tables::value_t...>(); } // Gets the values for rows that match the given ids. template <typename ValueTable, typename... IdTables> - static std::vector<typename ValueTable::value_t> GetAllValuesByIds(SQLite::Connection& connection, std::initializer_list<SQLite::rowid_t> ids) + static std::vector<typename ValueTable::value_t> GetAllValuesByIds(const SQLite::Connection& connection, std::initializer_list<SQLite::rowid_t> ids) { return details::ManifestTableGetAllValuesByIds(connection, { SQLite::Builder::QualifiedColumn{ ValueTable::TableName(), ValueTable::ValueName() } }, { IdTables::ValueName()... }, ids); } // Gets all values for rows that match the given id. template <typename IdTable, typename... ValueTables> - static std::vector<std::tuple<typename ValueTables::value_t...>> GetAllValuesById(SQLite::Connection& connection, SQLite::rowid_t id) + static std::vector<std::tuple<typename ValueTables::value_t...>> GetAllValuesById(const SQLite::Connection& connection, SQLite::rowid_t id) { auto stmt = details::ManifestTableGetAllValuesByIds_Statement(connection, { SQLite::Builder::QualifiedColumn{ ValueTables::TableName(), ValueTables::ValueName() }... }, { IdTable::ValueName() }, { id }); std::vector<std::tuple<typename ValueTables::value_t...>> result; diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/OneToOneTable.cpp b/src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/OneToOneTable.cpp @@ -49,7 +49,7 @@ namespace AppInstaller::Repository::Microsoft::Schema::V1_0 } } - std::optional<SQLite::rowid_t> OneToOneTableSelectIdByValue(SQLite::Connection& connection, std::string_view tableName, std::string_view valueName, std::string_view value, bool useLike) + std::optional<SQLite::rowid_t> OneToOneTableSelectIdByValue(const SQLite::Connection& connection, std::string_view tableName, std::string_view valueName, std::string_view value, bool useLike) { SQLite::Builder::StatementBuilder selectBuilder; selectBuilder.Select(SQLite::RowIDName).From(tableName).Where(valueName); @@ -92,7 +92,7 @@ namespace AppInstaller::Repository::Microsoft::Schema::V1_0 } } - std::vector<SQLite::rowid_t> OneToOneTableGetAllRowIds(SQLite::Connection& connection, std::string_view tableName, std::string_view valueName, size_t limit) + std::vector<SQLite::rowid_t> OneToOneTableGetAllRowIds(const SQLite::Connection& connection, std::string_view tableName, std::string_view valueName, size_t limit) { SQLite::Builder::StatementBuilder selectBuilder; selectBuilder.Select(SQLite::RowIDName).From(tableName).OrderBy(valueName); @@ -165,7 +165,7 @@ namespace AppInstaller::Repository::Microsoft::Schema::V1_0 } } - uint64_t OneToOneTableGetCount(SQLite::Connection& connection, std::string_view tableName) + uint64_t OneToOneTableGetCount(const SQLite::Connection& connection, std::string_view tableName) { SQLite::Builder::StatementBuilder builder; builder.Select(SQLite::Builder::RowCount).From(tableName); diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/OneToOneTable.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/OneToOneTable.h @@ -16,13 +16,13 @@ namespace AppInstaller::Repository::Microsoft::Schema::V1_0 void CreateOneToOneTable(SQLite::Connection& connection, std::string_view tableName, std::string_view valueName, bool useNamedIndeces); // Selects the value from the table, returning the rowid if it exists. - std::optional<SQLite::rowid_t> OneToOneTableSelectIdByValue(SQLite::Connection& connection, std::string_view tableName, std::string_view valueName, std::string_view value, bool useLike = false); + std::optional<SQLite::rowid_t> OneToOneTableSelectIdByValue(const SQLite::Connection& connection, std::string_view tableName, std::string_view valueName, std::string_view value, bool useLike = false); // Selects the value from the table, returning the rowid if it exists. std::optional<std::string> OneToOneTableSelectValueById(SQLite::Connection& connection, std::string_view tableName, std::string_view valueName, SQLite::rowid_t id); // Gets all row ids from the table. - std::vector<SQLite::rowid_t> OneToOneTableGetAllRowIds(SQLite::Connection& connection, std::string_view tableName, std::string_view valueName, size_t limit); + std::vector<SQLite::rowid_t> OneToOneTableGetAllRowIds(const SQLite::Connection& connection, std::string_view tableName, std::string_view valueName, size_t limit); // Ensures that the values exists in the table. SQLite::rowid_t OneToOneTableEnsureExists(SQLite::Connection& connection, std::string_view tableName, std::string_view valueName, std::string_view value, bool overwriteLikeMatch = false); @@ -34,7 +34,7 @@ namespace AppInstaller::Repository::Microsoft::Schema::V1_0 void OneToOneTablePrepareForPackaging(SQLite::Connection& connection, std::string_view tableName, bool useNamedIndeces, bool preserveValuesIndex); // Gets the total number of rows in the table. - uint64_t OneToOneTableGetCount(SQLite::Connection& connection, std::string_view tableName); + uint64_t OneToOneTableGetCount(const SQLite::Connection& connection, std::string_view tableName); // Determines if the table is empty. bool OneToOneTableIsEmpty(SQLite::Connection& connection, std::string_view tableName); @@ -81,7 +81,7 @@ namespace AppInstaller::Repository::Microsoft::Schema::V1_0 } // Selects the value from the table, returning the rowid if it exists. - static std::optional<SQLite::rowid_t> SelectIdByValue(SQLite::Connection& connection, std::string_view value, bool useLike = false) + static std::optional<SQLite::rowid_t> SelectIdByValue(const SQLite::Connection& connection, std::string_view value, bool useLike = false) { return details::OneToOneTableSelectIdByValue(connection, TableInfo::TableName(), TableInfo::ValueName(), value, useLike); } @@ -93,7 +93,7 @@ namespace AppInstaller::Repository::Microsoft::Schema::V1_0 } // Gets all row ids from the table. - static std::vector<SQLite::rowid_t> GetAllRowIds(SQLite::Connection& connection, size_t limit = 0) + static std::vector<SQLite::rowid_t> GetAllRowIds(const SQLite::Connection& connection, size_t limit = 0) { return details::OneToOneTableGetAllRowIds(connection, TableInfo::TableName(), TableInfo::ValueName(), limit); } @@ -123,7 +123,7 @@ namespace AppInstaller::Repository::Microsoft::Schema::V1_0 } // Gets the total number of rows in the table. - static uint64_t GetCount(SQLite::Connection& connection) + static uint64_t GetCount(const SQLite::Connection& connection) { return details::OneToOneTableGetCount(connection, TableInfo::TableName()); } diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/PathPartTable.cpp b/src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/PathPartTable.cpp @@ -198,7 +198,7 @@ namespace AppInstaller::Repository::Microsoft::Schema::V1_0 return { (createIfNotFound ? partsAdded : true), parent.value() }; } - std::optional<std::string> PathPartTable::GetPathById(SQLite::Connection& connection, SQLite::rowid_t id) + std::optional<std::string> PathPartTable::GetPathById(const SQLite::Connection& connection, SQLite::rowid_t id) { SQLite::Builder::StatementBuilder builder; builder.Select({ s_PathPartTable_ParentValue_Name, s_PathPartTable_PartValue_Name }). diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/PathPartTable.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/PathPartTable.h @@ -37,7 +37,7 @@ namespace AppInstaller::Repository::Microsoft::Schema::V1_0 static std::tuple<bool, SQLite::rowid_t> EnsurePathExists(SQLite::Connection& connection, const std::filesystem::path& relativePath, bool createIfNotFound); // Gets the path string using the given id as the leaf. - static std::optional<std::string> GetPathById(SQLite::Connection& connection, SQLite::rowid_t id); + static std::optional<std::string> GetPathById(const SQLite::Connection& connection, SQLite::rowid_t id); // Removes the path that terminates at the given id. // Will not remove a path part if it is referenced. diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/SearchResultsTable.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/SearchResultsTable.h @@ -16,7 +16,7 @@ namespace AppInstaller::Repository::Microsoft::Schema::V1_0 // Table for holding temporary search results. struct SearchResultsTable : public SQLite::TempTable { - SearchResultsTable(SQLite::Connection& connection); + SearchResultsTable(const SQLite::Connection& connection); SearchResultsTable(const SearchResultsTable&) = delete; SearchResultsTable& operator=(const SearchResultsTable&) = delete; @@ -25,7 +25,7 @@ namespace AppInstaller::Repository::Microsoft::Schema::V1_0 SearchResultsTable& operator=(SearchResultsTable&&) = default; // Performs the requested search type on the requested field. - void SearchOnField(ApplicationMatchField field, MatchType match, std::string_view value); + void SearchOnField(PackageMatchField field, MatchType match, std::string_view value); // Removes rows with manifest ids whose sort order is below the highest one. void RemoveDuplicateManifestRows(); @@ -34,7 +34,7 @@ namespace AppInstaller::Repository::Microsoft::Schema::V1_0 void PrepareToFilter(); // Performs the requested filter type on the requested field. - void FilterOnField(ApplicationMatchField field, MatchType match, std::string_view value); + void FilterOnField(PackageMatchField field, MatchType match, std::string_view value); // Completes a filtering pass, removing filtered rows. void CompleteFilter(); @@ -44,17 +44,17 @@ namespace AppInstaller::Repository::Microsoft::Schema::V1_0 protected: // Builds the search statement for the specified field and match type. - std::optional<int> BuildSearchStatement(SQLite::Builder::StatementBuilder& builder, ApplicationMatchField field, MatchType match) const; + std::optional<int> BuildSearchStatement(SQLite::Builder::StatementBuilder& builder, PackageMatchField field, MatchType match) const; virtual std::optional<int> BuildSearchStatement( SQLite::Builder::StatementBuilder& builder, - ApplicationMatchField field, + PackageMatchField field, std::string_view manifestAlias, std::string_view valueAlias, bool useLike) const; private: - SQLite::Connection& m_connection; + const SQLite::Connection& m_connection; int m_sortOrdinalValue = 0; }; } diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/SearchResultsTable_1_0.cpp b/src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/SearchResultsTable_1_0.cpp @@ -77,7 +77,7 @@ namespace AppInstaller::Repository::Microsoft::Schema::V1_0 } } - SearchResultsTable::SearchResultsTable(SQLite::Connection& connection) : + SearchResultsTable::SearchResultsTable(const SQLite::Connection& connection) : m_connection(connection) { using namespace SQLite::Builder; @@ -113,7 +113,7 @@ namespace AppInstaller::Repository::Microsoft::Schema::V1_0 } } - void SearchResultsTable::SearchOnField(ApplicationMatchField field, MatchType match, std::string_view value) + void SearchResultsTable::SearchOnField(PackageMatchField field, MatchType match, std::string_view value) { using namespace SQLite::Builder; @@ -140,7 +140,7 @@ namespace AppInstaller::Repository::Microsoft::Schema::V1_0 if (!bindIndex) { - AICLI_LOG(Repo, Verbose, << "ApplicationMatchField not supported in this version: " << ApplicationMatchFieldToString(field)); + AICLI_LOG(Repo, Verbose, << "PackageMatchField not supported in this version: " << PackageMatchFieldToString(field)); return; } @@ -183,7 +183,7 @@ namespace AppInstaller::Repository::Microsoft::Schema::V1_0 builder.Execute(m_connection); } - void SearchResultsTable::FilterOnField(ApplicationMatchField field, MatchType match, std::string_view value) + void SearchResultsTable::FilterOnField(PackageMatchField field, MatchType match, std::string_view value) { using namespace SQLite::Builder; @@ -204,7 +204,7 @@ namespace AppInstaller::Repository::Microsoft::Schema::V1_0 if (!bindIndex) { - AICLI_LOG(Repo, Verbose, << "ApplicationMatchField not supported in this version: " << ApplicationMatchFieldToString(field)); + AICLI_LOG(Repo, Verbose, << "PackageMatchField not supported in this version: " << PackageMatchFieldToString(field)); return; } @@ -259,7 +259,7 @@ namespace AppInstaller::Repository::Microsoft::Schema::V1_0 } result.Matches.emplace_back(select.GetColumn<SQLite::rowid_t>(0), - ApplicationMatchFilter(select.GetColumn<ApplicationMatchField>(1), select.GetColumn<MatchType>(2), select.GetColumn<std::string>(3))); + PackageMatchFilter(select.GetColumn<PackageMatchField>(1), select.GetColumn<MatchType>(2), select.GetColumn<std::string>(3))); } result.Truncated = (select.GetState() != SQLite::Statement::State::Completed); @@ -267,29 +267,29 @@ namespace AppInstaller::Repository::Microsoft::Schema::V1_0 return result; } - std::optional<int> SearchResultsTable::BuildSearchStatement(SQLite::Builder::StatementBuilder& builder, ApplicationMatchField field, MatchType match) const + std::optional<int> SearchResultsTable::BuildSearchStatement(SQLite::Builder::StatementBuilder& builder, PackageMatchField field, MatchType match) const { return BuildSearchStatement(builder, field, s_SearchResultsTable_SubSelect_ManifestAlias, s_SearchResultsTable_SubSelect_ValueAlias, MatchUsesLike(match)); } std::optional<int> SearchResultsTable::BuildSearchStatement( SQLite::Builder::StatementBuilder& builder, - ApplicationMatchField field, + PackageMatchField field, std::string_view manifestAlias, std::string_view valueAlias, bool useLike) const { switch (field) { - case ApplicationMatchField::Id: + case PackageMatchField::Id: return ManifestTable::BuildSearchStatement<IdTable>(builder, manifestAlias, valueAlias, useLike); - case ApplicationMatchField::Name: + case PackageMatchField::Name: return ManifestTable::BuildSearchStatement<NameTable>(builder, manifestAlias, valueAlias, useLike); - case ApplicationMatchField::Moniker: + case PackageMatchField::Moniker: return ManifestTable::BuildSearchStatement<MonikerTable>(builder, manifestAlias, valueAlias, useLike); - case ApplicationMatchField::Tag: + case PackageMatchField::Tag: return ManifestTable::BuildSearchStatement<TagsTable>(builder, manifestAlias, valueAlias, useLike); - case ApplicationMatchField::Command: + case PackageMatchField::Command: return ManifestTable::BuildSearchStatement<CommandsTable>(builder, manifestAlias, valueAlias, useLike); default: return {}; diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/1_1/Interface.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/1_1/Interface.h @@ -17,10 +17,10 @@ namespace AppInstaller::Repository::Microsoft::Schema::V1_1 std::pair<bool, SQLite::rowid_t> UpdateManifest(SQLite::Connection& connection, const Manifest::Manifest& manifest, const std::filesystem::path& relativePath) override; SQLite::rowid_t RemoveManifest(SQLite::Connection& connection, const Manifest::Manifest& manifest, const std::filesystem::path& relativePath) override; void PrepareForPackaging(SQLite::Connection& connection) override; - SearchResult Search(SQLite::Connection& connection, const SearchRequest& request) override; + SearchResult Search(const SQLite::Connection& connection, const SearchRequest& request) const override; protected: - std::unique_ptr<V1_0::SearchResultsTable> CreateSearchResultsTable(SQLite::Connection& connection) const override; + std::unique_ptr<V1_0::SearchResultsTable> CreateSearchResultsTable(const SQLite::Connection& connection) const override; void PerformQuerySearch(V1_0::SearchResultsTable& resultsTable, const RequestMatch& query) const override; }; } diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/1_1/Interface_1_1.cpp b/src/AppInstallerRepositoryCore/Microsoft/Schema/1_1/Interface_1_1.cpp @@ -173,14 +173,14 @@ namespace AppInstaller::Repository::Microsoft::Schema::V1_1 builder.Execute(connection); } - ISQLiteIndex::SearchResult Interface::Search(SQLite::Connection& connection, const SearchRequest& request) + ISQLiteIndex::SearchResult Interface::Search(const SQLite::Connection& connection, const SearchRequest& request) const { // Update any system reference strings to be folded SearchRequest foldedRequest = request; - auto foldIfNeeded = [](ApplicationMatchFilter& filter) + auto foldIfNeeded = [](PackageMatchFilter& filter) { - if ((filter.Field == ApplicationMatchField::PackageFamilyName || filter.Field == ApplicationMatchField::ProductCode) && + if ((filter.Field == PackageMatchField::PackageFamilyName || filter.Field == PackageMatchField::ProductCode) && filter.Type == MatchType::Exact) { filter.Value = Utility::FoldCase(filter.Value); @@ -200,7 +200,7 @@ namespace AppInstaller::Repository::Microsoft::Schema::V1_1 return V1_0::Interface::Search(connection, foldedRequest); } - std::unique_ptr<V1_0::SearchResultsTable> Interface::CreateSearchResultsTable(SQLite::Connection& connection) const + std::unique_ptr<V1_0::SearchResultsTable> Interface::CreateSearchResultsTable(const SQLite::Connection& connection) const { return std::make_unique<SearchResultsTable>(connection); } @@ -210,8 +210,8 @@ namespace AppInstaller::Repository::Microsoft::Schema::V1_1 // First, do an exact match search for the folded system reference strings // We do this first because it is exact, and likely won't match anything else if it matches this. std::string foldedQuery = Utility::FoldCase(query.Value); - resultsTable.SearchOnField(ApplicationMatchField::PackageFamilyName, MatchType::Exact, foldedQuery); - resultsTable.SearchOnField(ApplicationMatchField::ProductCode, MatchType::Exact, foldedQuery); + resultsTable.SearchOnField(PackageMatchField::PackageFamilyName, MatchType::Exact, foldedQuery); + resultsTable.SearchOnField(PackageMatchField::ProductCode, MatchType::Exact, foldedQuery); // Then do the 1.0 search V1_0::Interface::PerformQuerySearch(resultsTable, query); diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/1_1/SearchResultsTable.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/1_1/SearchResultsTable.h @@ -9,7 +9,7 @@ namespace AppInstaller::Repository::Microsoft::Schema::V1_1 // Table for holding temporary search results. struct SearchResultsTable : public V1_0::SearchResultsTable { - SearchResultsTable(SQLite::Connection& connection) : V1_0::SearchResultsTable(connection) {} + SearchResultsTable(const SQLite::Connection& connection) : V1_0::SearchResultsTable(connection) {} SearchResultsTable(const SearchResultsTable&) = delete; SearchResultsTable& operator=(const SearchResultsTable&) = delete; @@ -20,7 +20,7 @@ namespace AppInstaller::Repository::Microsoft::Schema::V1_1 protected: std::optional<int> BuildSearchStatement( SQLite::Builder::StatementBuilder& builder, - ApplicationMatchField field, + PackageMatchField field, std::string_view manifestAlias, std::string_view valueAlias, bool useLike) const override; diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/1_1/SearchResultsTable_1_1.cpp b/src/AppInstallerRepositoryCore/Microsoft/Schema/1_1/SearchResultsTable_1_1.cpp @@ -13,16 +13,16 @@ namespace AppInstaller::Repository::Microsoft::Schema::V1_1 { std::optional<int> SearchResultsTable::BuildSearchStatement( SQLite::Builder::StatementBuilder& builder, - ApplicationMatchField field, + PackageMatchField field, std::string_view manifestAlias, std::string_view valueAlias, bool useLike) const { switch (field) { - case ApplicationMatchField::PackageFamilyName: + case PackageMatchField::PackageFamilyName: return V1_0::ManifestTable::BuildSearchStatement<PackageFamilyNameTable>(builder, manifestAlias, valueAlias, useLike); - case ApplicationMatchField::ProductCode: + case PackageMatchField::ProductCode: return V1_0::ManifestTable::BuildSearchStatement<ProductCodeTable>(builder, manifestAlias, valueAlias, useLike); default: return V1_0::SearchResultsTable::BuildSearchStatement(builder, field, manifestAlias, valueAlias, useLike); diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/ISQLiteIndex.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/ISQLiteIndex.h @@ -24,7 +24,7 @@ namespace AppInstaller::Repository::Microsoft::Schema // New fields must have initializers to their down-schema defaults. struct SearchResult { - std::vector<std::pair<SQLite::rowid_t, ApplicationMatchFilter>> Matches; + std::vector<std::pair<SQLite::rowid_t, PackageMatchFilter>> Matches; bool Truncated = false; }; @@ -51,19 +51,16 @@ namespace AppInstaller::Repository::Microsoft::Schema virtual void PrepareForPackaging(SQLite::Connection& connection) = 0; // Performs a search based on the given criteria. - virtual SearchResult Search(SQLite::Connection& connection, const SearchRequest& request) = 0; + virtual SearchResult Search(const SQLite::Connection& connection, const SearchRequest& request) const = 0; - // Gets the Id string for the given id, if present. - virtual std::optional<std::string> GetIdStringById(SQLite::Connection& connection, SQLite::rowid_t id) = 0; + // Gets the string for the given property and manifest id, if present. + virtual std::optional<std::string> GetPropertyByManifestId(const SQLite::Connection& connection, SQLite::rowid_t manifestId, PackageVersionProperty property) const = 0; - // Gets the Name string for the given id, if present. - virtual std::optional<std::string> GetNameStringById(SQLite::Connection& connection, SQLite::rowid_t id) = 0; - - // Gets the relative path string for the given { id, version, channel }, if present. + // Gets the manifest id for the given { id, version, channel }, if present. // If version is empty, gets the value for the 'latest' version. - virtual std::optional<std::string> GetPathStringByKey(SQLite::Connection& connection, SQLite::rowid_t id, std::string_view version, std::string_view channel) = 0; + virtual std::optional<SQLite::rowid_t> GetManifestIdByKey(const SQLite::Connection& connection, SQLite::rowid_t id, std::string_view version, std::string_view channel) const = 0; // Gets all versions and channels for the given id. - virtual std::vector<Utility::VersionAndChannel> GetVersionsById(SQLite::Connection& connection, SQLite::rowid_t id) = 0; + virtual std::vector<Utility::VersionAndChannel> GetVersionKeysById(const SQLite::Connection& connection, SQLite::rowid_t id) const = 0; }; } diff --git a/src/AppInstallerRepositoryCore/Public/AppInstallerRepositorySearch.h b/src/AppInstallerRepositoryCore/Public/AppInstallerRepositorySearch.h @@ -30,7 +30,7 @@ namespace AppInstaller::Repository // The field to match on. // The values must be declared in order of preference in search results. - enum class ApplicationMatchField + enum class PackageMatchField { Id = 0, Name, @@ -51,11 +51,11 @@ namespace AppInstaller::Repository }; // A match on a specific field to be performed during a search. - struct ApplicationMatchFilter : public RequestMatch + struct PackageMatchFilter : public RequestMatch { - ApplicationMatchField Field; + PackageMatchField Field; - ApplicationMatchFilter(ApplicationMatchField f, MatchType t, std::string_view v) : RequestMatch(t, v), Field(f) {} + PackageMatchFilter(PackageMatchField f, MatchType t, std::string_view v) : RequestMatch(t, v), Field(f) {} }; // Container for data used to filter the available manifests in a source. @@ -71,10 +71,10 @@ namespace AppInstaller::Repository // Specific fields used to include more data. // If Query is defined, this can add more rows afterward. // If Query is not defined, this is the only set of data included. - std::vector<ApplicationMatchFilter> Inclusions; + std::vector<PackageMatchFilter> Inclusions; // Specific fields used to filter the data further. - std::vector<ApplicationMatchFilter> Filters; + std::vector<PackageMatchFilter> Filters; // The maximum number of results to return. // The default of 0 will place no limit. @@ -84,41 +84,78 @@ namespace AppInstaller::Repository std::string ToString() const; }; - // A single application result from a search. - struct IApplication + // A property of a package. + enum class PackageVersionProperty { - virtual ~IApplication() = default; + Id, + Name, + SourceId, + Version, + Channel, + RelativePath, + }; + + // A single package version. + struct IPackageVersion + { + virtual ~IPackageVersion() = default; - // Gets the id of the application. - virtual Utility::LocIndString GetId() = 0; + // Gets a property of this package version. + virtual Utility::LocIndString GetProperty(PackageVersionProperty property) const = 0; + + // Gets the manifest of this package version. + virtual Manifest::Manifest GetManifest() const = 0; + }; + + // A key to identify a package version within a package. + struct PackageVersionKey + { + PackageVersionKey(Utility::NormalizedString sourceId, Utility::NormalizedString version, Utility::NormalizedString channel) : + SourceId(std::move(sourceId)), Version(std::move(version)), Channel(std::move(channel)) {} - // Gets the name of the application (the latest name). - virtual Utility::LocIndString GetName() = 0; + // The source id that this version came from. + Utility::NormalizedString SourceId; - // Gets a manifest for this application. - // An empty version implies 'latest'. - // An empty channel is the 'general audience'. - virtual std::optional<Manifest::Manifest> GetManifest(const Utility::NormalizedString& version, const Utility::NormalizedString& channel) = 0; + // The version. + Utility::NormalizedString Version; - // Gets all versions of this application. + // The channel. + Utility::NormalizedString Channel; + }; + + // A package, potentially containing information about it's local state and the available versions. + struct IPackage + { + virtual ~IPackage() = default; + + // Gets the installed package information. + virtual std::shared_ptr<IPackageVersion> GetInstalledVersion() const = 0; + + // Gets all available versions of this package. // The versions will be returned in sorted, descending order. // Ex. { 4, 3, 2, 1 } - virtual std::vector<Utility::VersionAndChannel> GetVersions() = 0; + virtual std::vector<PackageVersionKey> GetAvailableVersionKeys() const = 0; + + // Gets a specific version of this package. + virtual std::shared_ptr<IPackageVersion> GetLatestAvailableVersion() const = 0; + + // Gets a specific version of this package. + virtual std::shared_ptr<IPackageVersion> GetAvailableVersion(const PackageVersionKey& versionKey) const = 0; }; // A single result from the search. struct ResultMatch { - // The application found by the search request. - std::unique_ptr<IApplication> Application; + // The package found by the search request. + std::unique_ptr<IPackage> Package; - // The highest order field on which the application matched the search. - ApplicationMatchFilter MatchCriteria; + // The highest order field on which the package matched the search. + PackageMatchFilter MatchCriteria; // The name of the source where the result is from. Used in aggregated source scenario. std::string SourceName = {}; - ResultMatch(std::unique_ptr<IApplication>&& a, ApplicationMatchFilter f) : Application(std::move(a)), MatchCriteria(std::move(f)) {} + ResultMatch(std::unique_ptr<IPackage>&& p, PackageMatchFilter f) : Package(std::move(p)), MatchCriteria(std::move(f)) {} }; // Search result data. @@ -156,25 +193,25 @@ namespace AppInstaller::Repository return "UnknownMatchType"sv; } - inline std::string_view ApplicationMatchFieldToString(ApplicationMatchField matchField) + inline std::string_view PackageMatchFieldToString(PackageMatchField matchField) { using namespace std::string_view_literals; switch (matchField) { - case ApplicationMatchField::Command: + case PackageMatchField::Command: return "Command"sv; - case ApplicationMatchField::Id: + case PackageMatchField::Id: return "Id"sv; - case ApplicationMatchField::Moniker: + case PackageMatchField::Moniker: return "Moniker"sv; - case ApplicationMatchField::Name: + case PackageMatchField::Name: return "Name"sv; - case ApplicationMatchField::Tag: + case PackageMatchField::Tag: return "Tag"sv; - case ApplicationMatchField::PackageFamilyName: + case PackageMatchField::PackageFamilyName: return "PackageFamilyName"sv; - case ApplicationMatchField::ProductCode: + case PackageMatchField::ProductCode: return "ProductCode"sv; } diff --git a/src/AppInstallerRepositoryCore/Public/AppInstallerRepositorySource.h b/src/AppInstallerRepositoryCore/Public/AppInstallerRepositorySource.h @@ -56,8 +56,14 @@ namespace AppInstaller::Repository // Get the source's details. virtual const SourceDetails& GetDetails() const = 0; + // Gets the source's identifier; a unique identifier independent of the name + // that will not change between a remove/add or between additional adds. + // Must be suitable for filesystem names unless the source is internal to winget, + // in which case the identifier should begin with a '*' character. + virtual const std::string& GetIdentifier() const = 0; + // Execute a search on the source. - virtual SearchResult Search(const SearchRequest& request) = 0; + virtual SearchResult Search(const SearchRequest& request) const = 0; }; // Gets the details for all sources. diff --git a/src/AppInstallerRepositoryCore/RepositorySource.cpp b/src/AppInstallerRepositoryCore/RepositorySource.cpp @@ -528,7 +528,7 @@ namespace AppInstaller::Repository else { AICLI_LOG(Repo, Info, << "Default source requested, multiple sources available, creating aggregated source."); - auto aggregatedSource = std::make_shared<AggregatedSource>(); + auto aggregatedSource = std::make_shared<AggregatedSource>("*DefaultSource"); bool sourceUpdated = false; for (auto& source : currentSources) @@ -702,12 +702,12 @@ namespace AppInstaller::Repository for (const auto& include : Inclusions) { - result << " Inclusions:" << ApplicationMatchFieldToString(include.Field) << "='" << include.Value << "'[" << MatchTypeToString(include.Type) << "]"; + result << " Inclusions:" << PackageMatchFieldToString(include.Field) << "='" << include.Value << "'[" << MatchTypeToString(include.Type) << "]"; } for (const auto& filter : Filters) { - result << " Filter:" << ApplicationMatchFieldToString(filter.Field) << "='" << filter.Value << "'[" << MatchTypeToString(filter.Type) << "]"; + result << " Filter:" << PackageMatchFieldToString(filter.Field) << "='" << filter.Value << "'[" << MatchTypeToString(filter.Type) << "]"; } if (MaximumResults) diff --git a/src/AppInstallerRepositoryCore/SQLiteStatementBuilder.cpp b/src/AppInstallerRepositoryCore/SQLiteStatementBuilder.cpp @@ -713,7 +713,7 @@ namespace AppInstaller::Repository::SQLite::Builder return *this; } - Statement StatementBuilder::Prepare(Connection& connection) + Statement StatementBuilder::Prepare(const Connection& connection) { Statement result = Statement::Create(connection, m_stream.str()); for (const auto& f : m_binders) @@ -723,7 +723,7 @@ namespace AppInstaller::Repository::SQLite::Builder return result; } - void StatementBuilder::Execute(Connection& connection) + void StatementBuilder::Execute(const Connection& connection) { Prepare(connection).Execute(); } diff --git a/src/AppInstallerRepositoryCore/SQLiteStatementBuilder.h b/src/AppInstallerRepositoryCore/SQLiteStatementBuilder.h @@ -342,10 +342,10 @@ namespace AppInstaller::Repository::SQLite::Builder int GetLastBindIndex() const { return m_bindIndex - 1; } // Prepares and returns the statement, applying any bindings that were requested. - Statement Prepare(Connection& connection); + Statement Prepare(const Connection& connection); // A convenience function that prepares, binds, and then executes a statement that does not return rows. - void Execute(Connection& connection); + void Execute(const Connection& connection); private: enum class Op diff --git a/src/AppInstallerRepositoryCore/SQLiteTempTable.cpp b/src/AppInstallerRepositoryCore/SQLiteTempTable.cpp @@ -33,7 +33,7 @@ namespace AppInstaller::Repository::SQLite return Builder::QualifiedTable("temp"sv, m_name); } - void TempTable::InitDropStatement(Connection& connection) + void TempTable::InitDropStatement(const Connection& connection) { Builder::StatementBuilder builder; builder.DropTable(m_name); diff --git a/src/AppInstallerRepositoryCore/SQLiteTempTable.h b/src/AppInstallerRepositoryCore/SQLiteTempTable.h @@ -26,7 +26,7 @@ namespace AppInstaller::Repository::SQLite // Prepares the drop table statement for use in destructor. // It needs to be run by the derived class after the table is actually created. - void InitDropStatement(Connection& connection); + void InitDropStatement(const Connection& connection); private: std::string m_name; diff --git a/src/AppInstallerRepositoryCore/SQLiteWrapper.cpp b/src/AppInstallerRepositoryCore/SQLiteWrapper.cpp @@ -123,12 +123,12 @@ namespace AppInstaller::Repository::SQLite return sqlite3_last_insert_rowid(m_dbconn.get()); } - int Connection::GetChanges() + int Connection::GetChanges() const { return sqlite3_changes(m_dbconn.get()); } - Statement::Statement(Connection& connection, std::string_view sql) + Statement::Statement(const Connection& connection, std::string_view sql) { m_id = GetNextStatementId(); AICLI_LOG(SQL, Verbose, << "Preparing statement #" << m_id << ": " << sql); @@ -176,20 +176,20 @@ namespace AppInstaller::Repository::SQLite #define WINGET_SQLITE_EXPLAIN_QUERY_PLAN(_connection_,_sql_) #endif - Statement Statement::Create(Connection& connection, const std::string& sql) + Statement Statement::Create(const Connection& connection, const std::string& sql) { WINGET_SQLITE_EXPLAIN_QUERY_PLAN(connection, sql); return { connection, { sql.c_str(), sql.size() } }; } - Statement Statement::Create(Connection& connection, std::string_view sql) + Statement Statement::Create(const Connection& connection, std::string_view sql) { WINGET_SQLITE_EXPLAIN_QUERY_PLAN(connection, sql); // We need the statement to be null terminated, and the only way to guarantee that with a string_view is to construct a string copy. return Create(connection, std::string(sql)); } - Statement Statement::Create(Connection& connection, char const* const sql) + Statement Statement::Create(const Connection& connection, char const* const sql) { WINGET_SQLITE_EXPLAIN_QUERY_PLAN(connection, sql); return { connection, sql }; diff --git a/src/AppInstallerRepositoryCore/SQLiteWrapper.h b/src/AppInstallerRepositoryCore/SQLiteWrapper.h @@ -142,7 +142,7 @@ namespace AppInstaller::Repository::SQLite rowid_t GetLastInsertRowID(); // Gets the count of changed rows for the last executed statement. - int GetChanges(); + int GetChanges() const; operator sqlite3* () const { return m_dbconn.get(); } @@ -155,9 +155,9 @@ namespace AppInstaller::Repository::SQLite // A SQL statement. struct Statement { - static Statement Create(Connection& connection, const std::string& sql); - static Statement Create(Connection& connection, std::string_view sql); - static Statement Create(Connection& connection, char const* const sql); + static Statement Create(const Connection& connection, const std::string& sql); + static Statement Create(const Connection& connection, std::string_view sql); + static Statement Create(const Connection& connection, char const* const sql); Statement() = default; @@ -231,7 +231,7 @@ namespace AppInstaller::Repository::SQLite operator bool() const { return static_cast<bool>(m_stmt); } private: - Statement(Connection& connection, std::string_view sql); + Statement(const Connection& connection, std::string_view sql); // Helper to receive the integer sequence from the public function. // This is equivalent to calling: