MultiQueryFlow.cpp (6653B)
1 // Copyright (c) Microsoft Corporation. 2 // Licensed under the MIT License. 3 #include "pch.h" 4 #include "MultiQueryFlow.h" 5 #include "UpdateFlow.h" 6 7 using namespace AppInstaller::CLI; 8 using namespace AppInstaller::CLI::Execution; 9 using namespace AppInstaller::Repository; 10 using namespace AppInstaller::Utility::literals; 11 12 namespace AppInstaller::CLI::Workflow 13 { 14 namespace 15 { 16 Utility::LocIndString GetPackageStringFromSearchRequest(const SearchRequest& searchRequest) 17 { 18 if (searchRequest.Query) 19 { 20 return Utility::LocIndString{ searchRequest.Query->Value }; 21 } 22 23 if (!searchRequest.Inclusions.empty()) 24 { 25 return Utility::LocIndString{ searchRequest.Inclusions[0].Value }; 26 } 27 28 if (!searchRequest.Filters.empty()) 29 { 30 return Utility::LocIndString{ searchRequest.Filters[0].Value }; 31 } 32 33 return ""_lis; 34 } 35 } 36 37 void GetMultiSearchRequests(Execution::Context& context) 38 { 39 std::vector<std::unique_ptr<Execution::Context>> packageSubContexts; 40 auto& source = context.Get<Execution::Data::Source>(); 41 for (const auto& query : *context.Args.GetArgs(Execution::Args::Type::MultiQuery)) 42 { 43 auto searchContextPtr = context.CreateSubContext(); 44 Execution::Context& searchContext = *searchContextPtr; 45 auto previousThreadGlobals = searchContext.SetForCurrentThread(); 46 47 searchContext.Add<Execution::Data::Source>(source); 48 searchContext.Args.AddArg(Execution::Args::Type::Query, query); 49 50 AICLI_LOG(CLI, Info, << "Creating search query for package [" << query << "]"); 51 searchContext << GetSearchRequestForSingle; 52 53 packageSubContexts.emplace_back(std::move(searchContextPtr)); 54 } 55 56 context.Add<Execution::Data::PackageSubContexts>(std::move(packageSubContexts)); 57 } 58 59 void SearchSubContextsForSingle::operator()(Execution::Context& context) const 60 { 61 std::vector<std::unique_ptr<Execution::Context>> packageSubContexts; 62 bool foundAll = true; 63 64 for (auto& searchContextPtr : context.Get<Execution::Data::PackageSubContexts>()) 65 { 66 auto& searchContext = *searchContextPtr; 67 SearchRequest searchRequest = searchContext.Get<Execution::Data::SearchRequest>(); 68 searchContext.Add<Execution::Data::SearchResult>(searchContext.Get<Execution::Data::Source>().Search(searchRequest)); 69 70 switch (m_operationType) 71 { 72 case OperationType::Install: 73 case OperationType::Upgrade: 74 searchContext << Workflow::SelectSinglePackageVersionForInstallOrUpgrade(m_operationType); 75 break; 76 case OperationType::Uninstall: 77 searchContext << 78 Workflow::HandleSearchResultFailures << 79 Workflow::EnsureOneMatchFromSearchResult(m_operationType); 80 break; 81 default: 82 THROW_HR(E_UNEXPECTED); 83 } 84 85 if (searchContext.IsTerminated()) 86 { 87 if (context.IsTerminated() && context.GetTerminationHR() == E_ABORT) 88 { 89 // This means that the subcontext being terminated is due to an overall abort 90 context.Reporter.Info() << Resource::String::Cancelled << std::endl; 91 return; 92 } 93 else 94 { 95 // We already reported the error from the sub-context, but we repeat it here because 96 // for multi-queries we can a bit more verbose as the queries here are easier to report. 97 auto packageString = GetPackageStringFromSearchRequest(searchRequest); 98 auto searchTerminationHR = searchContext.GetTerminationHR(); 99 if (searchTerminationHR == APPINSTALLER_CLI_ERROR_UPDATE_NOT_APPLICABLE || 100 searchTerminationHR == APPINSTALLER_CLI_ERROR_PACKAGE_ALREADY_INSTALLED) 101 { 102 AICLI_LOG(CLI, Info, << "Package is already installed: [" << packageString << "]"); 103 context.Reporter.Info() << Resource::String::MultiQueryPackageAlreadyInstalled(packageString) << std::endl; 104 continue; 105 } 106 else 107 { 108 if (searchTerminationHR == APPINSTALLER_CLI_ERROR_NO_APPLICATIONS_FOUND) 109 { 110 AICLI_LOG(CLI, Info, << "Package not found for query: [" << packageString << "]"); 111 context.Reporter.Warn() << Resource::String::MultiQueryPackageNotFound(packageString) << std::endl; 112 } 113 else if (searchTerminationHR == APPINSTALLER_CLI_ERROR_MULTIPLE_APPLICATIONS_FOUND) 114 { 115 AICLI_LOG(CLI, Info, << "Multiple packages found for query: [" << packageString << "]"); 116 context.Reporter.Warn() << Resource::String::MultiQuerySearchFoundMultiple(packageString) << std::endl; 117 } 118 else 119 { 120 AICLI_LOG(CLI, Info, << "Search failed for query: [" << packageString << "]"); 121 context.Reporter.Info() << Resource::String::MultiQuerySearchFailed(packageString) << std::endl; 122 } 123 124 // Keep searching for the remaining packages and only fail at the end. 125 foundAll = false; 126 continue; 127 } 128 } 129 } 130 131 packageSubContexts.emplace_back(std::move(searchContextPtr)); 132 } 133 134 if (!foundAll) 135 { 136 AICLI_LOG(CLI, Info, << "Not all queries returned one result"); 137 if (context.Args.Contains(Execution::Args::Type::IgnoreUnavailable)) 138 { 139 AICLI_LOG(CLI, Info, << "Ignoring unavailable packages due to command line argument"); 140 } 141 else 142 { 143 AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_NOT_ALL_QUERIES_FOUND_SINGLE); 144 } 145 } 146 147 context.Add<Execution::Data::PackageSubContexts>(std::move(packageSubContexts)); 148 } 149 }