From 291ded4794f4151a4092cdd3a5303b02c83310a4 Mon Sep 17 00:00:00 2001 From: Ryan Fu Date: Fri, 14 Jul 2023 15:51:13 -0700 Subject: [PATCH 01/43] save work --- .../AppInstallerCLICore.vcxproj | 2 + .../AppInstallerCLICore.vcxproj.filters | 6 ++ src/AppInstallerCLICore/Argument.cpp | 4 +- src/AppInstallerCLICore/Command.cpp | 24 +++++ src/AppInstallerCLICore/Command.h | 11 +++ .../Commands/InstallCommand.cpp | 17 +++- .../Commands/InstallCommand.h | 1 + .../Commands/ResumeCommand.cpp | 39 ++++++++ .../Commands/ResumeCommand.h | 22 +++++ .../Commands/RootCommand.cpp | 2 + src/AppInstallerCLICore/ExecutionArgs.h | 3 + src/AppInstallerCLICore/Resources.h | 2 + .../Shared/Strings/en-us/winget.resw | 6 ++ .../ExperimentalFeature.cpp | 4 + .../Public/winget/ExperimentalFeature.h | 1 + .../Public/winget/UserSettings.h | 2 + src/AppInstallerCommonCore/UserSettings.cpp | 1 + .../AppInstallerRepositoryCore.vcxproj | 9 +- ...AppInstallerRepositoryCore.vcxproj.filters | 26 +++++- .../Microsoft/CheckpointIndex.cpp | 91 +++++++++++++++++++ .../Microsoft/CheckpointIndex.h | 47 ++++++++++ .../CheckpointIndexInterface_1_0.cpp | 25 +++++ .../CheckpointIndexInterface_1_0.h | 18 ++++ .../Schema/Checkpoint_1_0/CheckpointTable.cpp | 66 ++++++++++++++ .../Schema/Checkpoint_1_0/CheckpointTable.h | 31 +++++++ .../Microsoft/Schema/ICheckpointIndex.h | 23 +++++ 26 files changed, 479 insertions(+), 4 deletions(-) create mode 100644 src/AppInstallerCLICore/Commands/ResumeCommand.cpp create mode 100644 src/AppInstallerCLICore/Commands/ResumeCommand.h create mode 100644 src/AppInstallerRepositoryCore/Microsoft/CheckpointIndex.cpp create mode 100644 src/AppInstallerRepositoryCore/Microsoft/CheckpointIndex.h create mode 100644 src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointIndexInterface_1_0.cpp create mode 100644 src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointIndexInterface_1_0.h create mode 100644 src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointTable.cpp create mode 100644 src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointTable.h create mode 100644 src/AppInstallerRepositoryCore/Microsoft/Schema/ICheckpointIndex.h diff --git a/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj b/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj index d0a6e492c1..da1bbbc2ae 100644 --- a/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj +++ b/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj @@ -369,6 +369,7 @@ + @@ -447,6 +448,7 @@ + diff --git a/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj.filters b/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj.filters index 069879c1c3..fd4fc36c3b 100644 --- a/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj.filters +++ b/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj.filters @@ -224,6 +224,9 @@ Commands + + Commands + @@ -415,6 +418,9 @@ Commands + + Commands + diff --git a/src/AppInstallerCLICore/Argument.cpp b/src/AppInstallerCLICore/Argument.cpp index 3c5973d5c4..657969e17f 100644 --- a/src/AppInstallerCLICore/Argument.cpp +++ b/src/AppInstallerCLICore/Argument.cpp @@ -164,7 +164,6 @@ namespace AppInstaller::CLI case Execution::Args::Type::Upgrade: return { type, "upgrade-available"_liv}; - // Pin command case Execution::Args::Type::GatedVersion: return { type, "version"_liv, 'v', ArgTypeCategory::None, ArgTypeExclusiveSet::PinType }; @@ -173,6 +172,9 @@ namespace AppInstaller::CLI case Execution::Args::Type::PinInstalled: return { type, "installed"_liv, ArgTypeCategory::None }; + case Execution::Args::Type::ResumeGuid: + return { type, "resume-guid"_liv, 'g', ArgTypeCategory::None }; + // Configuration commands case Execution::Args::Type::ConfigurationFile: return { type, "file"_liv, 'f' }; diff --git a/src/AppInstallerCLICore/Command.cpp b/src/AppInstallerCLICore/Command.cpp index b4153d913b..9ca3fc94ab 100644 --- a/src/AppInstallerCLICore/Command.cpp +++ b/src/AppInstallerCLICore/Command.cpp @@ -867,6 +867,30 @@ namespace AppInstaller::CLI } } + // This function is responsible for initializing the savepoint index. + void Command::InitializeCheckpoints(Execution::Context& context) + { + UNREFERENCED_PARAMETER(context); + std::ignore = CoCreateGuid(&m_checkpointId); + AICLI_LOG(CLI, Info, << "Creating checkpoint index with the corresponding guid: " << m_checkpointId); + + //auto openDisposition = m_readOnly ? SQLiteStorageBase::OpenDisposition::Read : SQLiteStorageBase::OpenDisposition::ReadWrite; + auto openDisposition = AppInstaller::Repository::Microsoft::SQLiteStorageBase::OpenDisposition::ReadWrite; + auto checkpointIndex = AppInstaller::Repository::Microsoft::CheckpointIndex::OpenOrCreateDefault(m_checkpointId, openDisposition); + if (!checkpointIndex) + { + AICLI_LOG(CLI, Error, << "Unable to open savepoint index."); + } + m_checkpointIndex = std::move(checkpointIndex); + } + + void Command::Checkpoint(Execution::Context& context, CheckpointFlags checkpointFlag) const + { + UNREFERENCED_PARAMETER(checkpointFlag); + context.Reporter.Error() << Resource::String::PendingWorkError << std::endl; + THROW_HR(E_NOTIMPL); + } + void Command::SelectCurrentCommandIfUnrecognizedSubcommandFound(bool value) { m_selectCurrentCommandIfUnrecognizedSubcommandFound = value; diff --git a/src/AppInstallerCLICore/Command.h b/src/AppInstallerCLICore/Command.h index 0f9a7d14eb..70ad246b0c 100644 --- a/src/AppInstallerCLICore/Command.h +++ b/src/AppInstallerCLICore/Command.h @@ -8,6 +8,7 @@ #include #include #include +#include #include #include @@ -36,6 +37,11 @@ namespace AppInstaller::CLI IgnoreSettingsWarnings = 0x1, }; + enum class CheckpointFlags + { + ArgumentsProcessed, + }; + DEFINE_ENUM_FLAG_OPERATORS(CommandOutputFlags); struct Command @@ -113,8 +119,11 @@ namespace AppInstaller::CLI virtual void Execute(Execution::Context& context) const; protected: + void InitializeCheckpoints(Execution::Context& context); void SelectCurrentCommandIfUnrecognizedSubcommandFound(bool value); + virtual void Checkpoint(Execution::Context& context, CheckpointFlags checkpointFlag) const; + virtual void ValidateArgumentsInternal(Execution::Args& execArgs) const; virtual void ExecuteInternal(Execution::Context& context) const; @@ -127,6 +136,8 @@ namespace AppInstaller::CLI Settings::TogglePolicy::Policy m_groupPolicy; CommandOutputFlags m_outputFlags; bool m_selectCurrentCommandIfUnrecognizedSubcommandFound = false; + GUID m_checkpointId; + std::shared_ptr m_checkpointIndex; }; template diff --git a/src/AppInstallerCLICore/Commands/InstallCommand.cpp b/src/AppInstallerCLICore/Commands/InstallCommand.cpp index e639541222..07498ff148 100644 --- a/src/AppInstallerCLICore/Commands/InstallCommand.cpp +++ b/src/AppInstallerCLICore/Commands/InstallCommand.cpp @@ -9,7 +9,6 @@ #include "Workflows/WorkflowBase.h" #include "Resources.h" - namespace AppInstaller::CLI { using namespace AppInstaller::CLI::Execution; @@ -101,8 +100,24 @@ namespace AppInstaller::CLI Argument::ValidateCommonArguments(execArgs); } + void InstallCommand::Checkpoint(Context& context, CheckpointFlags checkpoint) const + { + if (!Settings::ExperimentalFeature::IsEnabled(Settings::ExperimentalFeature::Feature::Resume)) + { + return; + } + + if (checkpoint == CheckpointFlags::ArgumentsProcessed) + { + //m_checkpoint + context.Reporter.Info() << "hello" << std::endl; + } + } + void InstallCommand::ExecuteInternal(Context& context) const { + Checkpoint(context, CheckpointFlags::ArgumentsProcessed); + context.SetFlags(ContextFlag::ShowSearchResultsOnPartialFailure); if (context.Args.Contains(Execution::Args::Type::Manifest)) diff --git a/src/AppInstallerCLICore/Commands/InstallCommand.h b/src/AppInstallerCLICore/Commands/InstallCommand.h index ab41da9bff..8d676b72a4 100644 --- a/src/AppInstallerCLICore/Commands/InstallCommand.h +++ b/src/AppInstallerCLICore/Commands/InstallCommand.h @@ -19,6 +19,7 @@ namespace AppInstaller::CLI Utility::LocIndView HelpLink() const override; protected: + void Checkpoint(Execution::Context& context, CheckpointFlags checkpointFlag) const override; void ValidateArgumentsInternal(Execution::Args& execArgs) const override; void ExecuteInternal(Execution::Context& context) const override; }; diff --git a/src/AppInstallerCLICore/Commands/ResumeCommand.cpp b/src/AppInstallerCLICore/Commands/ResumeCommand.cpp new file mode 100644 index 0000000000..ab4b00ef87 --- /dev/null +++ b/src/AppInstallerCLICore/Commands/ResumeCommand.cpp @@ -0,0 +1,39 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#include "pch.h" +#include "ResumeCommand.h" +#include "RootCommand.h" +#include "Resources.h" + +namespace AppInstaller::CLI +{ + using namespace std::string_view_literals; + using namespace Execution; + + std::vector ResumeCommand::GetArguments() const + { + return { + Argument::ForType(Execution::Args::Type::ResumeGuid), + }; + } + + Resource::LocString ResumeCommand::ShortDescription() const + { + return { Resource::String::ResumeCommandShortDescription }; + } + + Resource::LocString ResumeCommand::LongDescription() const + { + return { Resource::String::ResumeCommandLongDescription }; + } + + Utility::LocIndView ResumeCommand::HelpLink() const + { + return "https://aka.ms/winget-command-resume"_liv; + } + + void ResumeCommand::ExecuteInternal(Execution::Context& context) const + { + UNREFERENCED_PARAMETER(context); + } +} diff --git a/src/AppInstallerCLICore/Commands/ResumeCommand.h b/src/AppInstallerCLICore/Commands/ResumeCommand.h new file mode 100644 index 0000000000..79c915df6d --- /dev/null +++ b/src/AppInstallerCLICore/Commands/ResumeCommand.h @@ -0,0 +1,22 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once +#include "Command.h" + +namespace AppInstaller::CLI +{ + struct ResumeCommand final : public Command + { + ResumeCommand(std::string_view parent) : Command("resume", {}, parent, Visibility::Hidden, Settings::ExperimentalFeature::Feature::Resume) {} + + std::vector GetArguments() const override; + + Resource::LocString ShortDescription() const override; + Resource::LocString LongDescription() const override; + + Utility::LocIndView HelpLink() const override; + + protected: + void ExecuteInternal(Execution::Context& context) const override; + }; +} diff --git a/src/AppInstallerCLICore/Commands/RootCommand.cpp b/src/AppInstallerCLICore/Commands/RootCommand.cpp index 5d8612b387..2b47345363 100644 --- a/src/AppInstallerCLICore/Commands/RootCommand.cpp +++ b/src/AppInstallerCLICore/Commands/RootCommand.cpp @@ -23,6 +23,7 @@ #include "ConfigureCommand.h" #include "DebugCommand.h" #include "TestCommand.h" +#include "ResumeCommand.h" #include "Resources.h" #include "TableOutput.h" @@ -177,6 +178,7 @@ namespace AppInstaller::CLI std::make_unique(FullName()), std::make_unique(FullName()), std::make_unique(FullName()), + std::make_unique(FullName()), #if _DEBUG std::make_unique(FullName()), #endif diff --git a/src/AppInstallerCLICore/ExecutionArgs.h b/src/AppInstallerCLICore/ExecutionArgs.h index 6275c125bc..becb8644e7 100644 --- a/src/AppInstallerCLICore/ExecutionArgs.h +++ b/src/AppInstallerCLICore/ExecutionArgs.h @@ -101,6 +101,9 @@ namespace AppInstaller::CLI::Execution BlockingPin, PinInstalled, + // Resume Command + ResumeGuid, + // Configuration ConfigurationFile, ConfigurationAcceptWarning, diff --git a/src/AppInstallerCLICore/Resources.h b/src/AppInstallerCLICore/Resources.h index 788201457c..a80ca24f5a 100644 --- a/src/AppInstallerCLICore/Resources.h +++ b/src/AppInstallerCLICore/Resources.h @@ -386,6 +386,8 @@ namespace AppInstaller::CLI::Resource WINGET_DEFINE_RESOURCE_STRINGID(ReportIdentityFound); WINGET_DEFINE_RESOURCE_STRINGID(RequiredArgError); WINGET_DEFINE_RESOURCE_STRINGID(ReservedFilenameError); + WINGET_DEFINE_RESOURCE_STRINGID(ResumeCommandLongDescription); + WINGET_DEFINE_RESOURCE_STRINGID(ResumeCommandShortDescription); WINGET_DEFINE_RESOURCE_STRINGID(RetroArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(SearchCommandLongDescription); WINGET_DEFINE_RESOURCE_STRINGID(SearchCommandShortDescription); diff --git a/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw b/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw index f89cffafd9..e38a67c65d 100644 --- a/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw +++ b/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw @@ -1999,4 +1999,10 @@ Please specify one of them using the --source option to proceed. Have you reviewed the configuration and would you like to proceed verifying it against the system? + + Resumes execution of a previously saved command by passing in the unique guid identifier of the saved command. This is used to resume an executed command that may have been terminated due to a reboot. + + + Resumes execution of a previously saved command. + \ No newline at end of file diff --git a/src/AppInstallerCommonCore/ExperimentalFeature.cpp b/src/AppInstallerCommonCore/ExperimentalFeature.cpp index 259c0d0114..dc42445b57 100644 --- a/src/AppInstallerCommonCore/ExperimentalFeature.cpp +++ b/src/AppInstallerCommonCore/ExperimentalFeature.cpp @@ -46,6 +46,8 @@ namespace AppInstaller::Settings return userSettings.Get(); case ExperimentalFeature::Feature::WindowsFeature: return userSettings.Get(); + case ExperimentalFeature::Feature::Resume: + return userSettings.Get(); default: THROW_HR(E_UNEXPECTED); } @@ -81,6 +83,8 @@ namespace AppInstaller::Settings return ExperimentalFeature{ "Configuration", "configuration", "https://aka.ms/winget-settings#configuration", Feature::Configuration }; case Feature::WindowsFeature: return ExperimentalFeature{ "Windows Feature Dependencies", "windowsFeature", "https://aka.ms/winget-settings", Feature::WindowsFeature }; + case Feature::Resume: + return ExperimentalFeature{ "Resume", "resume", "https://aka.ms/winget-settings", Feature::Resume }; default: THROW_HR(E_UNEXPECTED); } diff --git a/src/AppInstallerCommonCore/Public/winget/ExperimentalFeature.h b/src/AppInstallerCommonCore/Public/winget/ExperimentalFeature.h index 4f551984ae..8433f49feb 100644 --- a/src/AppInstallerCommonCore/Public/winget/ExperimentalFeature.h +++ b/src/AppInstallerCommonCore/Public/winget/ExperimentalFeature.h @@ -26,6 +26,7 @@ namespace AppInstaller::Settings DirectMSI = 0x2, Configuration = 0x4, WindowsFeature = 0x8, + Resume = 0x10, Max, // This MUST always be after all experimental features // Features listed after Max will not be shown with the features command diff --git a/src/AppInstallerCommonCore/Public/winget/UserSettings.h b/src/AppInstallerCommonCore/Public/winget/UserSettings.h index 22a98ed277..50ef482ce1 100644 --- a/src/AppInstallerCommonCore/Public/winget/UserSettings.h +++ b/src/AppInstallerCommonCore/Public/winget/UserSettings.h @@ -73,6 +73,7 @@ namespace AppInstaller::Settings EFDirectMSI, EFConfiguration, EFWindowsFeature, + EFResume, // Telemetry TelemetryDisable, // Install behavior @@ -145,6 +146,7 @@ namespace AppInstaller::Settings SETTINGMAPPING_SPECIALIZATION(Setting::EFDirectMSI, bool, bool, false, ".experimentalFeatures.directMSI"sv); SETTINGMAPPING_SPECIALIZATION(Setting::EFConfiguration, bool, bool, false, ".experimentalFeatures.configuration"sv); SETTINGMAPPING_SPECIALIZATION(Setting::EFWindowsFeature, bool, bool, false, ".experimentalFeatures.windowsFeature"sv); + SETTINGMAPPING_SPECIALIZATION(Setting::EFResume, bool, bool, false, ".experimentalFeatures.resume"sv); // Telemetry SETTINGMAPPING_SPECIALIZATION(Setting::TelemetryDisable, bool, bool, false, ".telemetry.disable"sv); // Install behavior diff --git a/src/AppInstallerCommonCore/UserSettings.cpp b/src/AppInstallerCommonCore/UserSettings.cpp index 218a2a67b0..cdb9b185fe 100644 --- a/src/AppInstallerCommonCore/UserSettings.cpp +++ b/src/AppInstallerCommonCore/UserSettings.cpp @@ -262,6 +262,7 @@ namespace AppInstaller::Settings WINGET_VALIDATE_PASS_THROUGH(EFDirectMSI) WINGET_VALIDATE_PASS_THROUGH(EFConfiguration) WINGET_VALIDATE_PASS_THROUGH(EFWindowsFeature) + WINGET_VALIDATE_PASS_THROUGH(EFResume) WINGET_VALIDATE_PASS_THROUGH(AnonymizePathForDisplay) WINGET_VALIDATE_PASS_THROUGH(TelemetryDisable) WINGET_VALIDATE_PASS_THROUGH(InteractivityDisable) diff --git a/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj b/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj index 94bd3ff17c..922c63af76 100644 --- a/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj +++ b/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj @@ -1,4 +1,4 @@ - + @@ -353,6 +353,7 @@ + @@ -387,12 +388,15 @@ + + + @@ -469,6 +473,7 @@ + @@ -491,6 +496,8 @@ + + diff --git a/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj.filters b/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj.filters index 8284f78c24..dd310e9db9 100644 --- a/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj.filters +++ b/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj.filters @@ -1,4 +1,4 @@ - + @@ -79,6 +79,9 @@ {f05e19bb-2161-4ab0-9d04-2dfa2d3eb3c6} + + {a3f9c7ed-f487-40d6-9ee7-e9a052e55c29} + @@ -363,6 +366,18 @@ Public\winget + + Microsoft\Schema + + + Microsoft + + + Microsoft\Schema\Checkpoint_1_0 + + + Microsoft\Schema\Checkpoint_1_0 + @@ -578,6 +593,15 @@ Source Files + + Source Files + + + Source Files + + + Microsoft\Schema\Checkpoint_1_0 + diff --git a/src/AppInstallerRepositoryCore/Microsoft/CheckpointIndex.cpp b/src/AppInstallerRepositoryCore/Microsoft/CheckpointIndex.cpp new file mode 100644 index 0000000000..88de860e07 --- /dev/null +++ b/src/AppInstallerRepositoryCore/Microsoft/CheckpointIndex.cpp @@ -0,0 +1,91 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#include "pch.h" +#include "CheckpointIndex.h" +#include "SQLiteStorageBase.h" +#include "Schema/Checkpoint_1_0/CheckpointIndexInterface_1_0.h" + +namespace AppInstaller::Repository::Microsoft +{ + CheckpointIndex CheckpointIndex::CreateNew(const std::string& filePath, Schema::Version version) + { + AICLI_LOG(Repo, Info, << "Creating new Savepoint Index with version [" << version << "] at '" << filePath << "'"); + CheckpointIndex result{ filePath, version }; + + SQLite::Savepoint savepoint = SQLite::Savepoint::Create(result.m_dbconn, "CheckpointIndex_createnew"); + + // Use calculated version, as incoming version could be 'latest' + result.m_version.SetSchemaVersion(result.m_dbconn); + + result.m_interface->CreateTable(result.m_dbconn); + + result.SetLastWriteTime(); + + savepoint.Commit(); + + return result; + } + + std::shared_ptr CheckpointIndex::OpenOrCreateDefault(GUID guid, OpenDisposition openDisposition) + { + wchar_t buffer[256]; + if (!StringFromGUID2(guid, buffer, ARRAYSIZE(buffer))) + { + THROW_HR(E_UNEXPECTED); + } + + auto indexPath = Runtime::GetPathTo(Runtime::PathName::LocalState) / buffer; + indexPath.replace_extension(".db"); + AICLI_LOG(Repo, Info, << "Opening savepoint index"); + + try + { + if (std::filesystem::exists(indexPath)) + { + if (std::filesystem::is_regular_file(indexPath)) + { + try + { + AICLI_LOG(Repo, Info, << "Opening existing savepoint index"); + return std::make_shared(CheckpointIndex::Open(indexPath.u8string(), openDisposition)); + } + CATCH_LOG(); + } + + AICLI_LOG(Repo, Info, << "Attempting to delete bad index file"); + std::filesystem::remove_all(indexPath); + } + + return std::make_shared(CheckpointIndex::CreateNew(indexPath.u8string())); + } + CATCH_LOG(); + + return {}; + } + + std::unique_ptr CheckpointIndex::CreateICheckpointIndex() const + { + if (m_version == Schema::Version{ 1, 0 } || + m_version.MajorVersion == 1 || + m_version.IsLatest()) + { + return std::make_unique(); + } + + THROW_HR(HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED)); + } + + CheckpointIndex::CheckpointIndex(const std::string& target, SQLiteStorageBase::OpenDisposition disposition, Utility::ManagedFile&& indexFile) : + SQLiteStorageBase(target, disposition, std::move(indexFile)) + { + AICLI_LOG(Repo, Info, << "Opened Checkpoint Index with version [" << m_version << "], last write [" << GetLastWriteTime() << "]"); + m_interface = CreateICheckpointIndex(); + THROW_HR_IF(APPINSTALLER_CLI_ERROR_CANNOT_WRITE_TO_UPLEVEL_INDEX, disposition == SQLiteStorageBase::OpenDisposition::ReadWrite && m_version != m_interface->GetVersion()); + } + + CheckpointIndex::CheckpointIndex(const std::string& target, Schema::Version version) : SQLiteStorageBase(target, version) + { + m_interface = CreateICheckpointIndex(); + m_version = m_interface->GetVersion(); + } +} \ No newline at end of file diff --git a/src/AppInstallerRepositoryCore/Microsoft/CheckpointIndex.h b/src/AppInstallerRepositoryCore/Microsoft/CheckpointIndex.h new file mode 100644 index 0000000000..b40a07ae0c --- /dev/null +++ b/src/AppInstallerRepositoryCore/Microsoft/CheckpointIndex.h @@ -0,0 +1,47 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once +#include "SQLiteWrapper.h" +#include "Microsoft/Schema/ICheckpointIndex.h" +#include "Microsoft/Schema/Checkpoint_1_0/CheckpointTable.h" +#include "Microsoft/SQLiteStorageBase.h" +#include + +namespace AppInstaller::Repository::Microsoft +{ + struct CheckpointIndex : SQLiteStorageBase + { + // An id that refers to a specific Checkpoint. + using IdType = SQLite::rowid_t; + + CheckpointIndex(const CheckpointIndex&) = delete; + CheckpointIndex& operator=(const CheckpointIndex&) = delete; + + CheckpointIndex(CheckpointIndex&&) = default; + CheckpointIndex& operator=(CheckpointIndex&&) = default; + + // Creates a new CheckpointIndex database of the given version. + static CheckpointIndex CreateNew(const std::string& filePath, Schema::Version version = Schema::Version::Latest()); + + // Opens an existing CheckpointIndex database. + static CheckpointIndex Open(const std::string& filePath, OpenDisposition disposition, Utility::ManagedFile&& indexFile = {}) + { + return { filePath, disposition, std::move(indexFile) }; + } + + // Opens or creates a CheckpointIndex database on the default path. + static std::shared_ptr OpenOrCreateDefault(GUID guid, OpenDisposition openDisposition = OpenDisposition::ReadWrite); + + private: + // Constructor used to open an existing index. + CheckpointIndex(const std::string& target, SQLiteStorageBase::OpenDisposition disposition, Utility::ManagedFile&& indexFile); + + // Constructor used to create a new index. + CheckpointIndex(const std::string& target, Schema::Version version); + + // Creates the ICheckpointIndex interface object for this version. + std::unique_ptr CreateICheckpointIndex() const; + + std::unique_ptr m_interface; + }; +} \ No newline at end of file diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointIndexInterface_1_0.cpp b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointIndexInterface_1_0.cpp new file mode 100644 index 0000000000..ef00882847 --- /dev/null +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointIndexInterface_1_0.cpp @@ -0,0 +1,25 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#include "pch.h" +#include "Microsoft/Schema/Checkpoint_1_0/CheckpointIndexInterface_1_0.h" +#include "Microsoft/Schema/Checkpoint_1_0/CheckpointTable.h" + +namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 +{ + Schema::Version CheckpointIndexInterface::GetVersion() const + { + return { 1, 0 }; + } + + void CheckpointIndexInterface::CreateTable(SQLite::Connection& connection) + { + SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "createCheckpointTable_v1_0"); + Checkpoint_V1_0::CheckpointTable::Create(connection); + savepoint.Commit(); + } + + bool CheckpointIndexInterface::IsEmpty(SQLite::Connection& connection) + { + return CheckpointTable::IsEmpty(connection); + } +} \ No newline at end of file diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointIndexInterface_1_0.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointIndexInterface_1_0.h new file mode 100644 index 0000000000..81c7db04bd --- /dev/null +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointIndexInterface_1_0.h @@ -0,0 +1,18 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once +#include "Microsoft/Schema/ICheckpointIndex.h" +#include "Microsoft/Schema/Checkpoint_1_0/CheckpointTable.h" + +namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 +{ + struct CheckpointIndexInterface : public ICheckpointIndex + { + // Version 1.0 + Schema::Version GetVersion() const override; + void CreateTable(SQLite::Connection& connection) override; + + private: + bool IsEmpty(SQLite::Connection& connection) override; + }; +} \ No newline at end of file diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointTable.cpp b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointTable.cpp new file mode 100644 index 0000000000..ff0937b537 --- /dev/null +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointTable.cpp @@ -0,0 +1,66 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#include "pch.h" +#include "CheckpointTable.h" +#include "SQLiteStatementBuilder.h" +#include "Microsoft/Schema/ICheckpointIndex.h" + +namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 +{ + using namespace std::string_view_literals; + static constexpr std::string_view s_CheckpointTable_Table_Name = "Checkpoint"sv; + + std::string_view CheckpointTable::TableName() + { + return s_CheckpointTable_Table_Name; + } + + void CheckpointTable::Create(SQLite::Connection& connection) + { + using namespace SQLite::Builder; + + SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "createCheckpointTable_v1_0"); + + StatementBuilder createTableBuilder; + createTableBuilder.CreateTable(s_CheckpointTable_Table_Name).BeginColumns(); + + // Add columns here. + + createTableBuilder.EndColumns(); + createTableBuilder.Execute(connection); + + savepoint.Commit(); + } + + bool CheckpointTable::ExistsById(const SQLite::Connection& connection, SQLite::rowid_t id) + { + SQLite::Builder::StatementBuilder builder; + builder.Select(SQLite::Builder::RowCount).From(s_CheckpointTable_Table_Name).Where(SQLite::RowIDName).Equals(id); + + SQLite::Statement countStatement = builder.Prepare(connection); + + THROW_HR_IF(E_UNEXPECTED, !countStatement.Step()); + + return (countStatement.GetColumn(0) != 0); + } + + void CheckpointTable::DeleteById(SQLite::Connection& connection, SQLite::rowid_t id) + { + SQLite::Builder::StatementBuilder builder; + builder.DeleteFrom(s_CheckpointTable_Table_Name).Where(SQLite::RowIDName).Equals(id); + + builder.Execute(connection); + } + + bool CheckpointTable::IsEmpty(SQLite::Connection& connection) + { + SQLite::Builder::StatementBuilder builder; + builder.Select(SQLite::Builder::RowCount).From(s_CheckpointTable_Table_Name); + + SQLite::Statement countStatement = builder.Prepare(connection); + + THROW_HR_IF(E_UNEXPECTED, !countStatement.Step()); + + return (countStatement.GetColumn(0) == 0); + } +} \ No newline at end of file diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointTable.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointTable.h new file mode 100644 index 0000000000..f4dac9d9df --- /dev/null +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointTable.h @@ -0,0 +1,31 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once +#include "SQLiteWrapper.h" +#include "SQLiteStatementBuilder.h" +#include "Microsoft/Schema/ICheckpointIndex.h" +#include + +namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 +{ + struct CheckpointTable + { + // Get the table name. + static std::string_view TableName(); + + // Creates the table with named indices. + static void Create(SQLite::Connection& connection); + + // Gets a value indicating whether the Checkpoint file with rowid id exists. + static bool ExistsById(const SQLite::Connection& connection, SQLite::rowid_t id); + + // Deletes the Checkpoint row with the given rowid + static void DeleteById(SQLite::Connection& connection, SQLite::rowid_t id); + + // Gets a value indicating whether the table is empty. + static bool IsEmpty(SQLite::Connection& connection); + + // Removes the Checkpoint from the table by id. + //static void RemoveCheckpointById(SQLite::Connection& connection, SQLite::rowid_t id); + }; +} \ No newline at end of file diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/ICheckpointIndex.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/ICheckpointIndex.h new file mode 100644 index 0000000000..20cd2ad843 --- /dev/null +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/ICheckpointIndex.h @@ -0,0 +1,23 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once +#include "SQLiteWrapper.h" +#include "Microsoft/Schema/Version.h" + +namespace AppInstaller::Repository::Microsoft::Schema +{ + struct ICheckpointIndex + { + virtual ~ICheckpointIndex() = default; + + // Gets the schema version that this index interface is built for. + virtual Schema::Version GetVersion() const = 0; + + // Creates all of the version dependent tables within the database. + virtual void CreateTable(SQLite::Connection& connection) = 0; + + // Version 1.0 + // Returns a bool value indicating whether the index is empty. + virtual bool IsEmpty(SQLite::Connection& connection) = 0; + }; +} \ No newline at end of file From c6adb1a69c4652803f687484d6caef86ae09599b Mon Sep 17 00:00:00 2001 From: Ryan Fu Date: Wed, 19 Jul 2023 10:55:40 -0700 Subject: [PATCH 02/43] it builds! --- src/AppInstallerCLICore/Command.cpp | 2 ++ src/AppInstallerCLICore/Command.h | 6 +++++- src/AppInstallerCLICore/Commands/InstallCommand.cpp | 5 ++++- .../AppInstallerRepositoryCore.vcxproj | 2 +- .../AppInstallerRepositoryCore.vcxproj.filters | 2 +- .../Microsoft/CheckpointIndex.cpp | 2 +- ...pointIndexInterface_1_0.h => CheckpointIndexInterface.h} | 1 - .../Schema/Checkpoint_1_0/CheckpointIndexInterface_1_0.cpp | 2 +- 8 files changed, 15 insertions(+), 7 deletions(-) rename src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/{CheckpointIndexInterface_1_0.h => CheckpointIndexInterface.h} (89%) diff --git a/src/AppInstallerCLICore/Command.cpp b/src/AppInstallerCLICore/Command.cpp index 9ca3fc94ab..bd79ca86d1 100644 --- a/src/AppInstallerCLICore/Command.cpp +++ b/src/AppInstallerCLICore/Command.cpp @@ -6,6 +6,8 @@ #include #include #include +#include "Microsoft/CheckpointIndex.h" +#include "Microsoft/SQLiteStorageBase.h" using namespace std::string_view_literals; using namespace AppInstaller::Utility::literals; diff --git a/src/AppInstallerCLICore/Command.h b/src/AppInstallerCLICore/Command.h index 70ad246b0c..3578f0eb48 100644 --- a/src/AppInstallerCLICore/Command.h +++ b/src/AppInstallerCLICore/Command.h @@ -8,7 +8,6 @@ #include #include #include -#include #include #include @@ -19,6 +18,11 @@ #include #include +namespace AppInstaller::Repository::Microsoft +{ + struct CheckpointIndex; +} + namespace AppInstaller::CLI { struct CommandException diff --git a/src/AppInstallerCLICore/Commands/InstallCommand.cpp b/src/AppInstallerCLICore/Commands/InstallCommand.cpp index 07498ff148..37b3557e0a 100644 --- a/src/AppInstallerCLICore/Commands/InstallCommand.cpp +++ b/src/AppInstallerCLICore/Commands/InstallCommand.cpp @@ -116,7 +116,10 @@ namespace AppInstaller::CLI void InstallCommand::ExecuteInternal(Context& context) const { - Checkpoint(context, CheckpointFlags::ArgumentsProcessed); + //if (Settings::ExperimentalFeature::IsEnabled(Settings::ExperimentalFeature::Feature::Resume)) + //{ + // Checkpoint(context, CheckpointFlags::ArgumentsProcessed); + //} context.SetFlags(ContextFlag::ShowSearchResultsOnPartialFailure); diff --git a/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj b/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj index 922c63af76..da7238ea05 100644 --- a/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj +++ b/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj @@ -395,7 +395,7 @@ - + diff --git a/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj.filters b/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj.filters index dd310e9db9..5be26d2ac2 100644 --- a/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj.filters +++ b/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj.filters @@ -372,7 +372,7 @@ Microsoft - + Microsoft\Schema\Checkpoint_1_0 diff --git a/src/AppInstallerRepositoryCore/Microsoft/CheckpointIndex.cpp b/src/AppInstallerRepositoryCore/Microsoft/CheckpointIndex.cpp index 88de860e07..16473eecf0 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/CheckpointIndex.cpp +++ b/src/AppInstallerRepositoryCore/Microsoft/CheckpointIndex.cpp @@ -3,7 +3,7 @@ #include "pch.h" #include "CheckpointIndex.h" #include "SQLiteStorageBase.h" -#include "Schema/Checkpoint_1_0/CheckpointIndexInterface_1_0.h" +#include "Schema/Checkpoint_1_0/CheckpointIndexInterface.h" namespace AppInstaller::Repository::Microsoft { diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointIndexInterface_1_0.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointIndexInterface.h similarity index 89% rename from src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointIndexInterface_1_0.h rename to src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointIndexInterface.h index 81c7db04bd..a9fd8a4ef7 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointIndexInterface_1_0.h +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointIndexInterface.h @@ -2,7 +2,6 @@ // Licensed under the MIT License. #pragma once #include "Microsoft/Schema/ICheckpointIndex.h" -#include "Microsoft/Schema/Checkpoint_1_0/CheckpointTable.h" namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 { diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointIndexInterface_1_0.cpp b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointIndexInterface_1_0.cpp index ef00882847..2d0fe5ec06 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointIndexInterface_1_0.cpp +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointIndexInterface_1_0.cpp @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" -#include "Microsoft/Schema/Checkpoint_1_0/CheckpointIndexInterface_1_0.h" +#include "Microsoft/Schema/Checkpoint_1_0/CheckpointIndexInterface.h" #include "Microsoft/Schema/Checkpoint_1_0/CheckpointTable.h" namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 From 952a799d5d1ef1f2ae065f293ecf6abb690a7c69 Mon Sep 17 00:00:00 2001 From: Ryan Fu Date: Fri, 21 Jul 2023 12:32:13 -0700 Subject: [PATCH 03/43] save work --- src/AppInstallerCLICore/Argument.cpp | 3 + src/AppInstallerCLICore/Command.cpp | 26 ------ src/AppInstallerCLICore/Command.h | 15 ---- .../Commands/InstallCommand.cpp | 21 ++--- .../Commands/InstallCommand.h | 1 - .../Commands/ResumeCommand.cpp | 29 ++++++- src/AppInstallerCLICore/ExecutionContext.cpp | 51 +++++++++++ src/AppInstallerCLICore/ExecutionContext.h | 20 +++++ src/AppInstallerCLICore/Resources.h | 1 + .../Shared/Strings/en-us/winget.resw | 3 + .../AppInstallerRepositoryCore.vcxproj | 2 + ...AppInstallerRepositoryCore.vcxproj.filters | 6 ++ .../Microsoft/CheckpointIndex.cpp | 60 ++++++++++++- .../Microsoft/CheckpointIndex.h | 14 +++ .../Checkpoint_1_0/CheckpointIndexInterface.h | 13 ++- .../CheckpointIndexInterface_1_0.cpp | 60 ++++++++++++- .../CheckpointMetadataTable.cpp | 85 +++++++++++++++++++ .../Checkpoint_1_0/CheckpointMetadataTable.h | 31 +++++++ .../Schema/Checkpoint_1_0/CheckpointTable.cpp | 39 ++++++++- .../Schema/Checkpoint_1_0/CheckpointTable.h | 7 +- .../Microsoft/Schema/ICheckpointIndex.h | 16 +++- 21 files changed, 435 insertions(+), 68 deletions(-) create mode 100644 src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointMetadataTable.cpp create mode 100644 src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointMetadataTable.h diff --git a/src/AppInstallerCLICore/Argument.cpp b/src/AppInstallerCLICore/Argument.cpp index 657969e17f..e96bec4e04 100644 --- a/src/AppInstallerCLICore/Argument.cpp +++ b/src/AppInstallerCLICore/Argument.cpp @@ -172,6 +172,7 @@ namespace AppInstaller::CLI case Execution::Args::Type::PinInstalled: return { type, "installed"_liv, ArgTypeCategory::None }; + // Resume command case Execution::Args::Type::ResumeGuid: return { type, "resume-guid"_liv, 'g', ArgTypeCategory::None }; @@ -338,6 +339,8 @@ namespace AppInstaller::CLI return Argument{ type, Resource::String::UninstallPreviousArgumentDescription, ArgumentType::Flag, Argument::Visibility::Help }; case Args::Type::Force: return Argument{ type, Resource::String::ForceArgumentDescription, ArgumentType::Flag, false }; + case Args::Type::ResumeGuid: + return Argument{ type, Resource::String::ResumeGuidArgumentDescription, ArgumentType::Standard, true }; default: THROW_HR(E_UNEXPECTED); } diff --git a/src/AppInstallerCLICore/Command.cpp b/src/AppInstallerCLICore/Command.cpp index bd79ca86d1..e92334d3cf 100644 --- a/src/AppInstallerCLICore/Command.cpp +++ b/src/AppInstallerCLICore/Command.cpp @@ -6,8 +6,6 @@ #include #include #include -#include "Microsoft/CheckpointIndex.h" -#include "Microsoft/SQLiteStorageBase.h" using namespace std::string_view_literals; using namespace AppInstaller::Utility::literals; @@ -868,31 +866,7 @@ namespace AppInstaller::CLI context.Reporter.PromptForEnter(); } } - - // This function is responsible for initializing the savepoint index. - void Command::InitializeCheckpoints(Execution::Context& context) - { - UNREFERENCED_PARAMETER(context); - std::ignore = CoCreateGuid(&m_checkpointId); - AICLI_LOG(CLI, Info, << "Creating checkpoint index with the corresponding guid: " << m_checkpointId); - - //auto openDisposition = m_readOnly ? SQLiteStorageBase::OpenDisposition::Read : SQLiteStorageBase::OpenDisposition::ReadWrite; - auto openDisposition = AppInstaller::Repository::Microsoft::SQLiteStorageBase::OpenDisposition::ReadWrite; - auto checkpointIndex = AppInstaller::Repository::Microsoft::CheckpointIndex::OpenOrCreateDefault(m_checkpointId, openDisposition); - if (!checkpointIndex) - { - AICLI_LOG(CLI, Error, << "Unable to open savepoint index."); - } - m_checkpointIndex = std::move(checkpointIndex); - } - void Command::Checkpoint(Execution::Context& context, CheckpointFlags checkpointFlag) const - { - UNREFERENCED_PARAMETER(checkpointFlag); - context.Reporter.Error() << Resource::String::PendingWorkError << std::endl; - THROW_HR(E_NOTIMPL); - } - void Command::SelectCurrentCommandIfUnrecognizedSubcommandFound(bool value) { m_selectCurrentCommandIfUnrecognizedSubcommandFound = value; diff --git a/src/AppInstallerCLICore/Command.h b/src/AppInstallerCLICore/Command.h index 3578f0eb48..0f9a7d14eb 100644 --- a/src/AppInstallerCLICore/Command.h +++ b/src/AppInstallerCLICore/Command.h @@ -18,11 +18,6 @@ #include #include -namespace AppInstaller::Repository::Microsoft -{ - struct CheckpointIndex; -} - namespace AppInstaller::CLI { struct CommandException @@ -41,11 +36,6 @@ namespace AppInstaller::CLI IgnoreSettingsWarnings = 0x1, }; - enum class CheckpointFlags - { - ArgumentsProcessed, - }; - DEFINE_ENUM_FLAG_OPERATORS(CommandOutputFlags); struct Command @@ -123,11 +113,8 @@ namespace AppInstaller::CLI virtual void Execute(Execution::Context& context) const; protected: - void InitializeCheckpoints(Execution::Context& context); void SelectCurrentCommandIfUnrecognizedSubcommandFound(bool value); - virtual void Checkpoint(Execution::Context& context, CheckpointFlags checkpointFlag) const; - virtual void ValidateArgumentsInternal(Execution::Args& execArgs) const; virtual void ExecuteInternal(Execution::Context& context) const; @@ -140,8 +127,6 @@ namespace AppInstaller::CLI Settings::TogglePolicy::Policy m_groupPolicy; CommandOutputFlags m_outputFlags; bool m_selectCurrentCommandIfUnrecognizedSubcommandFound = false; - GUID m_checkpointId; - std::shared_ptr m_checkpointIndex; }; template diff --git a/src/AppInstallerCLICore/Commands/InstallCommand.cpp b/src/AppInstallerCLICore/Commands/InstallCommand.cpp index 37b3557e0a..fb17228afa 100644 --- a/src/AppInstallerCLICore/Commands/InstallCommand.cpp +++ b/src/AppInstallerCLICore/Commands/InstallCommand.cpp @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" +#include "AppInstallerRuntime.h" #include "InstallCommand.h" #include "Workflows/CompletionFlow.h" #include "Workflows/InstallFlow.h" @@ -100,26 +101,16 @@ namespace AppInstaller::CLI Argument::ValidateCommonArguments(execArgs); } - void InstallCommand::Checkpoint(Context& context, CheckpointFlags checkpoint) const + void InstallCommand::ExecuteInternal(Context& context) const { - if (!Settings::ExperimentalFeature::IsEnabled(Settings::ExperimentalFeature::Feature::Resume)) + if (Settings::ExperimentalFeature::IsEnabled(Settings::ExperimentalFeature::Feature::Resume)) { - return; - } + context.InitializeCheckpoints(AppInstaller::Runtime::GetClientVersion(), this->Name()); - if (checkpoint == CheckpointFlags::ArgumentsProcessed) - { - //m_checkpoint - context.Reporter.Info() << "hello" << std::endl; + // Record the initial arguments. + context.Checkpoint(CheckpointFlags::ArgumentsProcessed); } - } - void InstallCommand::ExecuteInternal(Context& context) const - { - //if (Settings::ExperimentalFeature::IsEnabled(Settings::ExperimentalFeature::Feature::Resume)) - //{ - // Checkpoint(context, CheckpointFlags::ArgumentsProcessed); - //} context.SetFlags(ContextFlag::ShowSearchResultsOnPartialFailure); diff --git a/src/AppInstallerCLICore/Commands/InstallCommand.h b/src/AppInstallerCLICore/Commands/InstallCommand.h index 8d676b72a4..ab41da9bff 100644 --- a/src/AppInstallerCLICore/Commands/InstallCommand.h +++ b/src/AppInstallerCLICore/Commands/InstallCommand.h @@ -19,7 +19,6 @@ namespace AppInstaller::CLI Utility::LocIndView HelpLink() const override; protected: - void Checkpoint(Execution::Context& context, CheckpointFlags checkpointFlag) const override; void ValidateArgumentsInternal(Execution::Args& execArgs) const override; void ExecuteInternal(Execution::Context& context) const override; }; diff --git a/src/AppInstallerCLICore/Commands/ResumeCommand.cpp b/src/AppInstallerCLICore/Commands/ResumeCommand.cpp index ab4b00ef87..fa794cb327 100644 --- a/src/AppInstallerCLICore/Commands/ResumeCommand.cpp +++ b/src/AppInstallerCLICore/Commands/ResumeCommand.cpp @@ -34,6 +34,33 @@ namespace AppInstaller::CLI void ResumeCommand::ExecuteInternal(Execution::Context& context) const { - UNREFERENCED_PARAMETER(context); + // Convert guid argument and load checkpoint file. + const auto& resumeGuid = Utility::ConvertToUTF16(context.Args.GetArg(Execution::Args::Type::ResumeGuid)); + + GUID checkpointId; + THROW_IF_FAILED(CLSIDFromString(resumeGuid.c_str(), &checkpointId)); + context.LoadCheckpointIndex(checkpointId); + + std::string_view commandName = "install"sv; + std::unique_ptr commandToResume; + + // Use the root command to obtain all of the available commands. + std::unique_ptr rootCommand = std::make_unique(); + auto commands = rootCommand->GetCommands(); + + for (auto& command : commands) + { + if ( + Utility::CaseInsensitiveEquals(commandName, command->Name()) || + Utility::CaseInsensitiveContains(command->Aliases(), commandName) + ) + { + AICLI_LOG(CLI, Info, << "Resuming command: " << commandName); + commandToResume = std::move(command); + } + } + + context.SetExecutingCommand(commandToResume.get()); + commandToResume->Execute(context); } } diff --git a/src/AppInstallerCLICore/ExecutionContext.cpp b/src/AppInstallerCLICore/ExecutionContext.cpp index 2dbaf3cbf6..bd41c42750 100644 --- a/src/AppInstallerCLICore/ExecutionContext.cpp +++ b/src/AppInstallerCLICore/ExecutionContext.cpp @@ -5,6 +5,8 @@ #include "COMContext.h" #include "Argument.h" #include "winget/UserSettings.h" +#include "Microsoft/CheckpointIndex.h" +#include "Microsoft/SQLiteStorageBase.h" namespace AppInstaller::CLI::Execution { @@ -407,4 +409,53 @@ namespace AppInstaller::CLI::Execution return SignalTerminationHandler::Instance().WaitForAppShutdownEvent(); } #endif + + void Context::LoadCheckpointIndex(GUID guid) + { + m_checkpointId = guid; + auto openDisposition = AppInstaller::Repository::Microsoft::SQLiteStorageBase::OpenDisposition::ReadWrite; + auto checkpointIndex = AppInstaller::Repository::Microsoft::CheckpointIndex::OpenOrCreateDefault(m_checkpointId, openDisposition); + if (!checkpointIndex) + { + AICLI_LOG(CLI, Error, << "Unable to open checkpoint index."); + THROW_HR_MSG(HRESULT_FROM_WIN32(ERROR_NOT_FOUND), "The saved state could not be found."); + } + + m_checkpointIndex = std::move(checkpointIndex); + } + + void Context::InitializeCheckpoints(std::string_view clientVersion, std::string_view commandName) + { + std::ignore = CoCreateGuid(&m_checkpointId); + AICLI_LOG(CLI, Info, << "Creating checkpoint index with the corresponding guid: " << m_checkpointId); + + //auto openDisposition = m_readOnly ? SQLiteStorageBase::OpenDisposition::Read : SQLiteStorageBase::OpenDisposition::ReadWrite; + auto openDisposition = AppInstaller::Repository::Microsoft::SQLiteStorageBase::OpenDisposition::ReadWrite; + auto checkpointIndex = AppInstaller::Repository::Microsoft::CheckpointIndex::OpenOrCreateDefault(m_checkpointId, openDisposition); + if (!checkpointIndex) + { + AICLI_LOG(CLI, Error, << "Unable to open savepoint index."); + } + m_checkpointIndex = std::move(checkpointIndex); + + m_checkpointIndex->SetClientVersion(clientVersion); + m_checkpointIndex->SetCommandName(commandName); + } + + void Context::Checkpoint(CheckpointFlags flag) + { + if (flag == CheckpointFlags::ArgumentsProcessed) + { + // Get all args and write them to the index. + const std::vector& arguments = this->Args.GetTypes(); + + for (auto argument : arguments) + { + const auto& argumentValue = this->Args.GetArg(argument); + + // Write current arg to table. + m_checkpointIndex->AddCommandArgument(static_cast(argument), argumentValue); + } + } + } } diff --git a/src/AppInstallerCLICore/ExecutionContext.h b/src/AppInstallerCLICore/ExecutionContext.h index 404e83469a..a967cb79f8 100644 --- a/src/AppInstallerCLICore/ExecutionContext.h +++ b/src/AppInstallerCLICore/ExecutionContext.h @@ -50,6 +50,11 @@ namespace AppInstaller::CLI::Workflow enum class ExecutionStage : uint32_t; } +namespace AppInstaller::Repository::Microsoft +{ + struct CheckpointIndex; +} + namespace AppInstaller::CLI::Execution { // bit masks used as Context flags @@ -67,6 +72,11 @@ namespace AppInstaller::CLI::Execution BypassIsStoreClientBlockedPolicyCheck = 0x80, }; + enum class CheckpointFlags + { + ArgumentsProcessed, + }; + DEFINE_ENUM_FLAG_OPERATORS(ContextFlag); #ifndef AICLI_DISABLE_TEST_HOOKS @@ -159,6 +169,14 @@ namespace AppInstaller::CLI::Execution // Enable tests to override behavior bool ShouldExecuteWorkflowTask(const Workflow::WorkflowTask& task); #endif + // Loads the checkpoint index with the stored state of the context. + void LoadCheckpointIndex(GUID guid); + + // Creates the checkpoint index for saving the state of the context + void InitializeCheckpoints(std::string_view clientVersion, std::string_view commandName); + + // Sets the checkpoint flag and handles saving/reading the state to/from the checkpoint index. + void Checkpoint(CheckpointFlags flag); protected: // Copies the args that are also needed in a sub-context. E.g., silent @@ -178,5 +196,7 @@ namespace AppInstaller::CLI::Execution Workflow::ExecutionStage m_executionStage = Workflow::ExecutionStage::Initial; AppInstaller::ThreadLocalStorage::WingetThreadGlobals m_threadGlobals; AppInstaller::CLI::Command* m_executingCommand = nullptr; + GUID m_checkpointId = {}; + std::shared_ptr m_checkpointIndex = nullptr; }; } diff --git a/src/AppInstallerCLICore/Resources.h b/src/AppInstallerCLICore/Resources.h index a80ca24f5a..38cf1f9421 100644 --- a/src/AppInstallerCLICore/Resources.h +++ b/src/AppInstallerCLICore/Resources.h @@ -388,6 +388,7 @@ namespace AppInstaller::CLI::Resource WINGET_DEFINE_RESOURCE_STRINGID(ReservedFilenameError); WINGET_DEFINE_RESOURCE_STRINGID(ResumeCommandLongDescription); WINGET_DEFINE_RESOURCE_STRINGID(ResumeCommandShortDescription); + WINGET_DEFINE_RESOURCE_STRINGID(ResumeGuidArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(RetroArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(SearchCommandLongDescription); WINGET_DEFINE_RESOURCE_STRINGID(SearchCommandShortDescription); diff --git a/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw b/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw index e38a67c65d..8b0bf4daac 100644 --- a/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw +++ b/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw @@ -2005,4 +2005,7 @@ Please specify one of them using the --source option to proceed. Resumes execution of a previously saved command. + + The unique guid identifier of the saved state to resume + \ No newline at end of file diff --git a/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj b/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj index da7238ea05..b4043ed490 100644 --- a/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj +++ b/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj @@ -396,6 +396,7 @@ + @@ -497,6 +498,7 @@ + diff --git a/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj.filters b/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj.filters index 5be26d2ac2..e87ccda7b6 100644 --- a/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj.filters +++ b/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj.filters @@ -378,6 +378,9 @@ Microsoft\Schema\Checkpoint_1_0 + + Microsoft\Schema\Checkpoint_1_0 + @@ -602,6 +605,9 @@ Microsoft\Schema\Checkpoint_1_0 + + Microsoft\Schema\Checkpoint_1_0 + diff --git a/src/AppInstallerRepositoryCore/Microsoft/CheckpointIndex.cpp b/src/AppInstallerRepositoryCore/Microsoft/CheckpointIndex.cpp index 16473eecf0..2b8cc5309a 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/CheckpointIndex.cpp +++ b/src/AppInstallerRepositoryCore/Microsoft/CheckpointIndex.cpp @@ -17,7 +17,7 @@ namespace AppInstaller::Repository::Microsoft // Use calculated version, as incoming version could be 'latest' result.m_version.SetSchemaVersion(result.m_dbconn); - result.m_interface->CreateTable(result.m_dbconn); + result.m_interface->CreateTables(result.m_dbconn); result.SetLastWriteTime(); @@ -63,6 +63,64 @@ namespace AppInstaller::Repository::Microsoft return {}; } + CheckpointIndex::IdType CheckpointIndex::AddCommandArgument(int type, const std::string_view& argValue) + { + std::lock_guard lockInterface{ *m_interfaceLock }; + AICLI_LOG(Repo, Verbose, << "Adding argument for [" << type << "]"); + + SQLite::Savepoint savepoint = SQLite::Savepoint::Create(m_dbconn, "checkpointindex_addargument"); + + IdType result = m_interface->AddCommandArgument(m_dbconn, type, argValue); + + SetLastWriteTime(); + + savepoint.Commit(); + + return result; + } + + CheckpointIndex::IdType CheckpointIndex::SetClientVersion(std::string_view clientVersion) + { + std::lock_guard lockInterface{ *m_interfaceLock }; + AICLI_LOG(Repo, Verbose, << "Setting client version [" << clientVersion << "]"); + + SQLite::Savepoint savepoint = SQLite::Savepoint::Create(m_dbconn, "checkpointindex_setclientversion"); + + IdType result = m_interface->SetClientVersion(m_dbconn, clientVersion); + + SetLastWriteTime(); + + savepoint.Commit(); + + return result; + } + + std::string CheckpointIndex::GetClientVersion() + { + return m_interface->GetClientVersion(m_dbconn); + } + + CheckpointIndex::IdType CheckpointIndex::SetCommandName(std::string_view commandName) + { + std::lock_guard lockInterface{ *m_interfaceLock }; + AICLI_LOG(Repo, Verbose, << "Setting command name [" << commandName << "]"); + + SQLite::Savepoint savepoint = SQLite::Savepoint::Create(m_dbconn, "checkpointindex_setcommandname"); + + IdType result = m_interface->SetCommandName(m_dbconn, commandName); + + SetLastWriteTime(); + + savepoint.Commit(); + + return result; + } + + std::string CheckpointIndex::GetCommandName() + { + return m_interface->GetCommandName(m_dbconn); + } + std::unique_ptr CheckpointIndex::CreateICheckpointIndex() const { if (m_version == Schema::Version{ 1, 0 } || diff --git a/src/AppInstallerRepositoryCore/Microsoft/CheckpointIndex.h b/src/AppInstallerRepositoryCore/Microsoft/CheckpointIndex.h index b40a07ae0c..b632adb452 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/CheckpointIndex.h +++ b/src/AppInstallerRepositoryCore/Microsoft/CheckpointIndex.h @@ -32,6 +32,20 @@ namespace AppInstaller::Repository::Microsoft // Opens or creates a CheckpointIndex database on the default path. static std::shared_ptr OpenOrCreateDefault(GUID guid, OpenDisposition openDisposition = OpenDisposition::ReadWrite); + // Adds a command argument to the index. + IdType AddCommandArgument(int type, const std::string_view& argValue); + + // Removes a command argument from the index. + //IdType RemoveCommandArgument(const uint32_t& type, const std::string_view& argValue); + + IdType SetClientVersion(std::string_view clientVersion); + + std::string GetClientVersion(); + + IdType SetCommandName(std::string_view commandName); + + std::string GetCommandName(); + private: // Constructor used to open an existing index. CheckpointIndex(const std::string& target, SQLiteStorageBase::OpenDisposition disposition, Utility::ManagedFile&& indexFile); diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointIndexInterface.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointIndexInterface.h index a9fd8a4ef7..a2877cd4e4 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointIndexInterface.h +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointIndexInterface.h @@ -9,7 +9,18 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 { // Version 1.0 Schema::Version GetVersion() const override; - void CreateTable(SQLite::Connection& connection) override; + + void CreateTables(SQLite::Connection& connection) override; + + SQLite::rowid_t AddCommandArgument(SQLite::Connection& connection, int type, const std::string_view& argValue) override; + + SQLite::rowid_t SetClientVersion(SQLite::Connection& connection, std::string_view clientVersion) override; + + std::string GetClientVersion(SQLite::Connection& connection) override; + + SQLite::rowid_t SetCommandName(SQLite::Connection& connection, std::string_view clientVersion) override; + + std::string GetCommandName(SQLite::Connection& connection) override; private: bool IsEmpty(SQLite::Connection& connection) override; diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointIndexInterface_1_0.cpp b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointIndexInterface_1_0.cpp index 2d0fe5ec06..48463cb928 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointIndexInterface_1_0.cpp +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointIndexInterface_1_0.cpp @@ -3,19 +3,77 @@ #include "pch.h" #include "Microsoft/Schema/Checkpoint_1_0/CheckpointIndexInterface.h" #include "Microsoft/Schema/Checkpoint_1_0/CheckpointTable.h" +#include "Microsoft/Schema/Checkpoint_1_0/CheckpointMetadataTable.h" namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 { + namespace + { + std::optional GetExistingCommandArgument(const SQLite::Connection& connection, uint32_t type) + { + auto result = CheckpointTable::SelectByArgumentType(connection, type); + + if (!result) + { + AICLI_LOG(Repo, Verbose, << "Did not find an argument with the type { " << type << " }"); + } + + return result; + } + } + Schema::Version CheckpointIndexInterface::GetVersion() const { return { 1, 0 }; } - void CheckpointIndexInterface::CreateTable(SQLite::Connection& connection) + void CheckpointIndexInterface::CreateTables(SQLite::Connection& connection) { SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "createCheckpointTable_v1_0"); Checkpoint_V1_0::CheckpointTable::Create(connection); + Checkpoint_V1_0::CheckpointMetadataTable::Create(connection); + savepoint.Commit(); + } + + SQLite::rowid_t CheckpointIndexInterface::AddCommandArgument(SQLite::Connection& connection, int type, const std::string_view& argValue) + { + auto existingArgument = GetExistingCommandArgument(connection, type); + + THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS), existingArgument.has_value()); + + SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "addCommandArgument_v1_0"); + SQLite::rowid_t argumentId = CheckpointTable::AddCommandArgument(connection, type, argValue); + savepoint.Commit(); + return argumentId; + } + + SQLite::rowid_t CheckpointIndexInterface::SetClientVersion(SQLite::Connection& connection, std::string_view clientVersion) + { + SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "setClientVersion_v1_0"); + SQLite::rowid_t argumentId = CheckpointMetadataTable::SetClientVersion(connection, clientVersion); + + savepoint.Commit(); + return argumentId; + } + + std::string CheckpointIndexInterface::GetClientVersion(SQLite::Connection& connection) + { + return CheckpointMetadataTable::GetClientVersion(connection); + } + + SQLite::rowid_t CheckpointIndexInterface::SetCommandName(SQLite::Connection& connection, std::string_view commandName) + { + SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "setCommandName_v1_0"); + SQLite::rowid_t argumentId = CheckpointMetadataTable::SetCommandName(connection, commandName); + + savepoint.Commit(); + return argumentId; + } + + std::string CheckpointIndexInterface::GetCommandName(SQLite::Connection& connection) + { + return CheckpointMetadataTable::GetCommandName(connection); } bool CheckpointIndexInterface::IsEmpty(SQLite::Connection& connection) diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointMetadataTable.cpp b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointMetadataTable.cpp new file mode 100644 index 0000000000..08775f4ff1 --- /dev/null +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointMetadataTable.cpp @@ -0,0 +1,85 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#include "pch.h" +#include "CheckpointMetadataTable.h" +#include "SQLiteStatementBuilder.h" +#include "Microsoft/Schema/ICheckpointIndex.h" + +namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 +{ + using namespace std::string_view_literals; + static constexpr std::string_view s_CheckpointMetadataTable_Table_Name = "CheckpointMetadataTable"sv; + static constexpr std::string_view s_CheckpointMetadataTable_Name_Column = "Name"sv; + static constexpr std::string_view s_CheckpointMetadataTable_Value_Column = "Value"sv; + + static constexpr std::string_view s_CheckpointMetadataTable_ClientVersion = "ClientVersion"sv; + static constexpr std::string_view s_CheckpointMetadataTable_CommandName = "CommandName"sv; + + namespace + { + SQLite::rowid_t SetNamedValue(SQLite::Connection& connection, std::string_view name, std::string_view value) + { + SQLite::Builder::StatementBuilder builder; + builder.InsertInto(s_CheckpointMetadataTable_Table_Name) + .Columns({ s_CheckpointMetadataTable_Name_Column, + s_CheckpointMetadataTable_Value_Column }) + .Values(name, value); + + builder.Execute(connection); + return connection.GetLastInsertRowID(); + } + + std::string GetNamedValue(SQLite::Connection& connection, std::string_view name) + { + SQLite::Builder::StatementBuilder builder; + builder.Select({ s_CheckpointMetadataTable_Value_Column }) + .From(s_CheckpointMetadataTable_Table_Name).Where(s_CheckpointMetadataTable_Name_Column).Equals(name); + + SQLite::Statement statement = builder.Prepare(connection); + THROW_HR_IF(E_NOT_SET, !statement.Step()); + return statement.GetColumn(0); + } + } + + std::string_view CheckpointMetadataTable::TableName() + { + return s_CheckpointMetadataTable_Table_Name; + } + + void CheckpointMetadataTable::Create(SQLite::Connection& connection) + { + using namespace SQLite::Builder; + + SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "createCheckpointMetadataTable_v1_0"); + + StatementBuilder createTableBuilder; + createTableBuilder.CreateTable(s_CheckpointMetadataTable_Table_Name).Columns({ + ColumnBuilder(s_CheckpointMetadataTable_Name_Column, Type::Text).NotNull(), + ColumnBuilder(s_CheckpointMetadataTable_Value_Column, Type::Text).NotNull() + }); + + createTableBuilder.Execute(connection); + + savepoint.Commit(); + } + + SQLite::rowid_t CheckpointMetadataTable::SetClientVersion(SQLite::Connection& connection, std::string_view clientVersion) + { + return SetNamedValue(connection, s_CheckpointMetadataTable_ClientVersion, clientVersion); + } + + std::string CheckpointMetadataTable::GetClientVersion(SQLite::Connection& connection) + { + return GetNamedValue(connection, s_CheckpointMetadataTable_ClientVersion); + } + + SQLite::rowid_t CheckpointMetadataTable::SetCommandName(SQLite::Connection& connection, std::string_view commandName) + { + return SetNamedValue(connection, s_CheckpointMetadataTable_CommandName, commandName); + } + + std::string CheckpointMetadataTable::GetCommandName(SQLite::Connection& connection) + { + return GetNamedValue(connection, s_CheckpointMetadataTable_CommandName); + } +} \ No newline at end of file diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointMetadataTable.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointMetadataTable.h new file mode 100644 index 0000000000..9c1099c82e --- /dev/null +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointMetadataTable.h @@ -0,0 +1,31 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once +#include "SQLiteWrapper.h" +#include "SQLiteStatementBuilder.h" +#include "Microsoft/Schema/ICheckpointIndex.h" +#include + +namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 +{ + struct CheckpointMetadataTable + { + // Get the table name. + static std::string_view TableName(); + + // Creates the table with named indices. + static void Create(SQLite::Connection& connection); + + // Gets the client version of the saved state. + static std::string GetClientVersion(SQLite::Connection& connection); + + // Sets the client version of the saved state. + static SQLite::rowid_t SetClientVersion(SQLite::Connection& connection, std::string_view clientVersion); + + // Gets the command name of the saved state. + static std::string GetCommandName(SQLite::Connection& connection); + + // Sets the command name of the saved state. + static SQLite::rowid_t SetCommandName(SQLite::Connection& connection, std::string_view commandName); + }; +} \ No newline at end of file diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointTable.cpp b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointTable.cpp index ff0937b537..148035de7a 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointTable.cpp +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointTable.cpp @@ -9,6 +9,8 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 { using namespace std::string_view_literals; static constexpr std::string_view s_CheckpointTable_Table_Name = "Checkpoint"sv; + static constexpr std::string_view s_CheckpointTable_ArgumentType_Column = "argumentType"sv; + static constexpr std::string_view s_CheckpointTable_ArgumentValue_Column = "argumentValue"sv; std::string_view CheckpointTable::TableName() { @@ -22,11 +24,11 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "createCheckpointTable_v1_0"); StatementBuilder createTableBuilder; - createTableBuilder.CreateTable(s_CheckpointTable_Table_Name).BeginColumns(); + createTableBuilder.CreateTable(s_CheckpointTable_Table_Name).Columns({ + ColumnBuilder(s_CheckpointTable_ArgumentType_Column, Type::Int64).NotNull(), + ColumnBuilder(s_CheckpointTable_ArgumentValue_Column, Type::Text) + }); - // Add columns here. - - createTableBuilder.EndColumns(); createTableBuilder.Execute(connection); savepoint.Commit(); @@ -63,4 +65,33 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 return (countStatement.GetColumn(0) == 0); } + + std::optional CheckpointTable::SelectByArgumentType(const SQLite::Connection& connection, int type) + { + SQLite::Builder::StatementBuilder builder; + builder.Select(SQLite::RowIDName).From(s_CheckpointTable_Table_Name).Where(s_CheckpointTable_ArgumentType_Column); + builder.Equals(type); + + SQLite::Statement select = builder.Prepare(connection); + + if (select.Step()) + { + return select.GetColumn(0); + } + else + { + return {}; + } + } + + SQLite::rowid_t CheckpointTable::AddCommandArgument(SQLite::Connection& connection, int type, const std::string_view& value) + { + SQLite::Builder::StatementBuilder builder; + builder.InsertInto(s_CheckpointTable_Table_Name) + .Columns({ s_CheckpointTable_ArgumentType_Column, s_CheckpointTable_ArgumentValue_Column }) + .Values(type, value); + + builder.Execute(connection); + return connection.GetLastInsertRowID(); + } } \ No newline at end of file diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointTable.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointTable.h index f4dac9d9df..3d1f20afc7 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointTable.h +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointTable.h @@ -25,7 +25,10 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 // Gets a value indicating whether the table is empty. static bool IsEmpty(SQLite::Connection& connection); - // Removes the Checkpoint from the table by id. - //static void RemoveCheckpointById(SQLite::Connection& connection, SQLite::rowid_t id); + // Selects the command argument by type from the table, returning the rowid if it exists. + static std::optional SelectByArgumentType(const SQLite::Connection& connection, int type); + + // Adds the command argument and value to the table. + static SQLite::rowid_t AddCommandArgument(SQLite::Connection& connection, int type, const std::string_view& value); }; } \ No newline at end of file diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/ICheckpointIndex.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/ICheckpointIndex.h index 20cd2ad843..1b302e8190 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/Schema/ICheckpointIndex.h +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/ICheckpointIndex.h @@ -14,9 +14,23 @@ namespace AppInstaller::Repository::Microsoft::Schema virtual Schema::Version GetVersion() const = 0; // Creates all of the version dependent tables within the database. - virtual void CreateTable(SQLite::Connection& connection) = 0; + virtual void CreateTables(SQLite::Connection& connection) = 0; // Version 1.0 + // Adds an argument to the index. + virtual SQLite::rowid_t AddCommandArgument(SQLite::Connection& connection, int argumentType, const std::string_view& argumentValue) = 0; + + // Removes an argument from the index. + //virtual SQLite::rowid_t RemoveCommandArgument(SQLite::Connection& connection, int argumentType, const std::string_view& argumentValue); + + virtual SQLite::rowid_t SetClientVersion(SQLite::Connection& connection, std::string_view clientVersion) = 0; + + virtual std::string GetClientVersion(SQLite::Connection& connection) = 0; + + virtual SQLite::rowid_t SetCommandName(SQLite::Connection& connection, std::string_view commandName) = 0; + + virtual std::string GetCommandName(SQLite::Connection& connection) = 0; + // Returns a bool value indicating whether the index is empty. virtual bool IsEmpty(SQLite::Connection& connection) = 0; }; From 2ed8d2cd1b36298f85ca9241ad5e4c109bbb6600 Mon Sep 17 00:00:00 2001 From: Ryan Fu Date: Mon, 24 Jul 2023 13:43:29 -0700 Subject: [PATCH 04/43] add checkpoint manager --- .../AppInstallerCLICore.vcxproj | 2 + .../AppInstallerCLICore.vcxproj.filters | 6 ++ src/AppInstallerCLICore/CheckpointManager.cpp | 90 +++++++++++++++++++ src/AppInstallerCLICore/CheckpointManager.h | 34 +++++++ .../Commands/InstallCommand.cpp | 4 +- .../Commands/ResumeCommand.cpp | 17 +++- src/AppInstallerCLICore/ExecutionContext.cpp | 51 ----------- src/AppInstallerCLICore/ExecutionContext.h | 32 +++---- .../Workflows/WorkflowBase.cpp | 6 ++ .../Workflows/WorkflowBase.h | 1 - .../Microsoft/CheckpointIndex.cpp | 5 ++ .../Microsoft/CheckpointIndex.h | 2 + .../Checkpoint_1_0/CheckpointIndexInterface.h | 2 + .../CheckpointIndexInterface_1_0.cpp | 5 ++ .../Schema/Checkpoint_1_0/CheckpointTable.cpp | 17 ++++ .../Schema/Checkpoint_1_0/CheckpointTable.h | 2 + .../Microsoft/Schema/ICheckpointIndex.h | 2 + src/AppInstallerSharedLib/Errors.cpp | 2 + .../Public/AppInstallerErrors.h | 1 + 19 files changed, 205 insertions(+), 76 deletions(-) create mode 100644 src/AppInstallerCLICore/CheckpointManager.cpp create mode 100644 src/AppInstallerCLICore/CheckpointManager.h diff --git a/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj b/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj index da1bbbc2ae..2a7da7b683 100644 --- a/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj +++ b/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj @@ -350,6 +350,7 @@ + @@ -420,6 +421,7 @@ + diff --git a/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj.filters b/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj.filters index fd4fc36c3b..6cbe3a50e4 100644 --- a/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj.filters +++ b/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj.filters @@ -227,6 +227,9 @@ Commands + + Header Files + @@ -421,6 +424,9 @@ Commands + + Source Files + diff --git a/src/AppInstallerCLICore/CheckpointManager.cpp b/src/AppInstallerCLICore/CheckpointManager.cpp new file mode 100644 index 0000000000..a6cd5dc93b --- /dev/null +++ b/src/AppInstallerCLICore/CheckpointManager.cpp @@ -0,0 +1,90 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#include "pch.h" +#include "CheckpointManager.h" +#include "Microsoft/CheckpointIndex.h" +#include "Microsoft/SQLiteStorageBase.h" + +namespace AppInstaller::CLI::Checkpoint +{ + CheckpointManager::CheckpointManager(GUID checkpointId) : m_checkpointId(checkpointId) + { + auto openDisposition = AppInstaller::Repository::Microsoft::SQLiteStorageBase::OpenDisposition::ReadWrite; + auto checkpointIndex = AppInstaller::Repository::Microsoft::CheckpointIndex::OpenOrCreateDefault(m_checkpointId, openDisposition); + if (!checkpointIndex) + { + AICLI_LOG(CLI, Error, << "Unable to open checkpoint index."); + THROW_HR_MSG(HRESULT_FROM_WIN32(ERROR_NOT_FOUND), "The saved state could not be found."); + } + + m_checkpointIndex = std::move(checkpointIndex); + } + + std::unique_ptr CheckpointManager::CreateContextFromCheckpointIndex() + { + auto checkpointContext = std::make_unique(std::cout, std::cin); + auto previousthreadGlobals = checkpointContext->SetForCurrentThread(); + + checkpointContext->EnableSignalTerminationHandler(); + + // Change to loading in context from checkpoint. + PopulateContextArgsFromCheckpointIndex(*(checkpointContext.get())); + return checkpointContext; + } + + void CheckpointManager::PopulateContextArgsFromCheckpointIndex(Execution::Context& context) + { + std::vector> args = m_checkpointIndex->GetArguments(); + + for (auto arg : args) + { + context.Args.AddArg(static_cast(arg.first), arg.second); + } + } + + void CheckpointManager::InitializeCheckpoint(std::string_view clientVersion, std::string_view commandName) + { + std::ignore = CoCreateGuid(&m_checkpointId); + AICLI_LOG(CLI, Info, << "Creating checkpoint index with the corresponding guid: " << m_checkpointId); + + //auto openDisposition = m_readOnly ? SQLiteStorageBase::OpenDisposition::Read : SQLiteStorageBase::OpenDisposition::ReadWrite; + auto openDisposition = AppInstaller::Repository::Microsoft::SQLiteStorageBase::OpenDisposition::ReadWrite; + auto checkpointIndex = AppInstaller::Repository::Microsoft::CheckpointIndex::OpenOrCreateDefault(m_checkpointId, openDisposition); + if (!checkpointIndex) + { + AICLI_LOG(CLI, Error, << "Unable to open savepoint index."); + } + m_checkpointIndex = std::move(checkpointIndex); + + m_checkpointIndex->SetClientVersion(clientVersion); + m_checkpointIndex->SetCommandName(commandName); + } + + void CheckpointManager::SaveCheckpoint(Execution::Context& context, Execution::CheckpointFlags flag) + { + if (flag == Execution::CheckpointFlags::ArgumentsProcessed) + { + // Get all args and write them to the index. + const std::vector& arguments = context.Args.GetTypes(); + + for (auto argument : arguments) + { + const auto& argumentValue = context.Args.GetArg(argument); + + // Write current arg to table. + m_checkpointIndex->AddCommandArgument(static_cast(argument), argumentValue); + } + } + } + + std::string CheckpointManager::GetClientVersion() + { + // Need to throw exception if checkpoint index is null. + return m_checkpointIndex->GetClientVersion(); + } + + std::string CheckpointManager::GetCommandName() + { + return m_checkpointIndex->GetCommandName(); + } +} diff --git a/src/AppInstallerCLICore/CheckpointManager.h b/src/AppInstallerCLICore/CheckpointManager.h new file mode 100644 index 0000000000..e461978169 --- /dev/null +++ b/src/AppInstallerCLICore/CheckpointManager.h @@ -0,0 +1,34 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once +#include "ExecutionContext.h" + +#include + +namespace AppInstaller::Repository::Microsoft +{ + struct CheckpointIndex; +} + +namespace AppInstaller::CLI::Checkpoint +{ + struct CheckpointManager + { + CheckpointManager(GUID checkpointId); + + void InitializeCheckpoint(std::string_view clientVersion, std::string_view commandName); + + void SaveCheckpoint(Execution::Context& context, Execution::CheckpointFlags flag); + + std::unique_ptr CreateContextFromCheckpointIndex(); + + std::string GetClientVersion(); + + std::string GetCommandName(); + + private: + GUID m_checkpointId; + std::shared_ptr m_checkpointIndex = nullptr; + void PopulateContextArgsFromCheckpointIndex(Execution::Context& context); + }; +} \ No newline at end of file diff --git a/src/AppInstallerCLICore/Commands/InstallCommand.cpp b/src/AppInstallerCLICore/Commands/InstallCommand.cpp index fb17228afa..17b1cec1b7 100644 --- a/src/AppInstallerCLICore/Commands/InstallCommand.cpp +++ b/src/AppInstallerCLICore/Commands/InstallCommand.cpp @@ -105,10 +105,10 @@ namespace AppInstaller::CLI { if (Settings::ExperimentalFeature::IsEnabled(Settings::ExperimentalFeature::Feature::Resume)) { - context.InitializeCheckpoints(AppInstaller::Runtime::GetClientVersion(), this->Name()); + //context.InitializeCheckpoints(AppInstaller::Runtime::GetClientVersion(), this->Name()); // Record the initial arguments. - context.Checkpoint(CheckpointFlags::ArgumentsProcessed); + //context.Checkpoint(CheckpointFlags::ArgumentsProcessed); } diff --git a/src/AppInstallerCLICore/Commands/ResumeCommand.cpp b/src/AppInstallerCLICore/Commands/ResumeCommand.cpp index fa794cb327..861162b5c0 100644 --- a/src/AppInstallerCLICore/Commands/ResumeCommand.cpp +++ b/src/AppInstallerCLICore/Commands/ResumeCommand.cpp @@ -1,6 +1,8 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" +#include "AppInstallerRuntime.h" +#include "CheckpointManager.h" #include "ResumeCommand.h" #include "RootCommand.h" #include "Resources.h" @@ -9,6 +11,7 @@ namespace AppInstaller::CLI { using namespace std::string_view_literals; using namespace Execution; + using namespace Checkpoint; std::vector ResumeCommand::GetArguments() const { @@ -39,9 +42,14 @@ namespace AppInstaller::CLI GUID checkpointId; THROW_IF_FAILED(CLSIDFromString(resumeGuid.c_str(), &checkpointId)); - context.LoadCheckpointIndex(checkpointId); + CheckpointManager checkpointManager{ checkpointId }; - std::string_view commandName = "install"sv; + if (AppInstaller::Runtime::GetClientVersion().get() != checkpointManager.GetClientVersion()) + { + AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_CLIENTVERSION_MISMATCH); + } + + std::string commandName = checkpointManager.GetCommandName(); std::unique_ptr commandToResume; // Use the root command to obtain all of the available commands. @@ -60,7 +68,8 @@ namespace AppInstaller::CLI } } - context.SetExecutingCommand(commandToResume.get()); - commandToResume->Execute(context); + auto checkpointContext = checkpointManager.CreateContextFromCheckpointIndex(); + checkpointContext->SetExecutingCommand(commandToResume.get()); + commandToResume->Execute(*(checkpointContext.get())); } } diff --git a/src/AppInstallerCLICore/ExecutionContext.cpp b/src/AppInstallerCLICore/ExecutionContext.cpp index bd41c42750..2dbaf3cbf6 100644 --- a/src/AppInstallerCLICore/ExecutionContext.cpp +++ b/src/AppInstallerCLICore/ExecutionContext.cpp @@ -5,8 +5,6 @@ #include "COMContext.h" #include "Argument.h" #include "winget/UserSettings.h" -#include "Microsoft/CheckpointIndex.h" -#include "Microsoft/SQLiteStorageBase.h" namespace AppInstaller::CLI::Execution { @@ -409,53 +407,4 @@ namespace AppInstaller::CLI::Execution return SignalTerminationHandler::Instance().WaitForAppShutdownEvent(); } #endif - - void Context::LoadCheckpointIndex(GUID guid) - { - m_checkpointId = guid; - auto openDisposition = AppInstaller::Repository::Microsoft::SQLiteStorageBase::OpenDisposition::ReadWrite; - auto checkpointIndex = AppInstaller::Repository::Microsoft::CheckpointIndex::OpenOrCreateDefault(m_checkpointId, openDisposition); - if (!checkpointIndex) - { - AICLI_LOG(CLI, Error, << "Unable to open checkpoint index."); - THROW_HR_MSG(HRESULT_FROM_WIN32(ERROR_NOT_FOUND), "The saved state could not be found."); - } - - m_checkpointIndex = std::move(checkpointIndex); - } - - void Context::InitializeCheckpoints(std::string_view clientVersion, std::string_view commandName) - { - std::ignore = CoCreateGuid(&m_checkpointId); - AICLI_LOG(CLI, Info, << "Creating checkpoint index with the corresponding guid: " << m_checkpointId); - - //auto openDisposition = m_readOnly ? SQLiteStorageBase::OpenDisposition::Read : SQLiteStorageBase::OpenDisposition::ReadWrite; - auto openDisposition = AppInstaller::Repository::Microsoft::SQLiteStorageBase::OpenDisposition::ReadWrite; - auto checkpointIndex = AppInstaller::Repository::Microsoft::CheckpointIndex::OpenOrCreateDefault(m_checkpointId, openDisposition); - if (!checkpointIndex) - { - AICLI_LOG(CLI, Error, << "Unable to open savepoint index."); - } - m_checkpointIndex = std::move(checkpointIndex); - - m_checkpointIndex->SetClientVersion(clientVersion); - m_checkpointIndex->SetCommandName(commandName); - } - - void Context::Checkpoint(CheckpointFlags flag) - { - if (flag == CheckpointFlags::ArgumentsProcessed) - { - // Get all args and write them to the index. - const std::vector& arguments = this->Args.GetTypes(); - - for (auto argument : arguments) - { - const auto& argumentValue = this->Args.GetArg(argument); - - // Write current arg to table. - m_checkpointIndex->AddCommandArgument(static_cast(argument), argumentValue); - } - } - } } diff --git a/src/AppInstallerCLICore/ExecutionContext.h b/src/AppInstallerCLICore/ExecutionContext.h index a967cb79f8..0a355753f0 100644 --- a/src/AppInstallerCLICore/ExecutionContext.h +++ b/src/AppInstallerCLICore/ExecutionContext.h @@ -50,13 +50,14 @@ namespace AppInstaller::CLI::Workflow enum class ExecutionStage : uint32_t; } -namespace AppInstaller::Repository::Microsoft -{ - struct CheckpointIndex; -} - namespace AppInstaller::CLI::Execution { + enum class CheckpointFlags : uint32_t + { + None, + ArgumentsProcessed, + }; + // bit masks used as Context flags enum class ContextFlag : int { @@ -72,11 +73,6 @@ namespace AppInstaller::CLI::Execution BypassIsStoreClientBlockedPolicyCheck = 0x80, }; - enum class CheckpointFlags - { - ArgumentsProcessed, - }; - DEFINE_ENUM_FLAG_OPERATORS(ContextFlag); #ifndef AICLI_DISABLE_TEST_HOOKS @@ -169,14 +165,14 @@ namespace AppInstaller::CLI::Execution // Enable tests to override behavior bool ShouldExecuteWorkflowTask(const Workflow::WorkflowTask& task); #endif - // Loads the checkpoint index with the stored state of the context. - void LoadCheckpointIndex(GUID guid); + // Sets the target checkpoint of the context. + void SetTargetCheckpoint(CheckpointFlags flag) { m_targetCheckpoint = flag; }; - // Creates the checkpoint index for saving the state of the context - void InitializeCheckpoints(std::string_view clientVersion, std::string_view commandName); + // Gets the current checkpoint of the context. + CheckpointFlags GetCurrentCheckpoint() { return m_currentCheckpoint; }; - // Sets the checkpoint flag and handles saving/reading the state to/from the checkpoint index. - void Checkpoint(CheckpointFlags flag); + // Gets the target checkpoint of the context. + CheckpointFlags GetTargetCheckpoint() { return m_targetCheckpoint; }; protected: // Copies the args that are also needed in a sub-context. E.g., silent @@ -196,7 +192,7 @@ namespace AppInstaller::CLI::Execution Workflow::ExecutionStage m_executionStage = Workflow::ExecutionStage::Initial; AppInstaller::ThreadLocalStorage::WingetThreadGlobals m_threadGlobals; AppInstaller::CLI::Command* m_executingCommand = nullptr; - GUID m_checkpointId = {}; - std::shared_ptr m_checkpointIndex = nullptr; + CheckpointFlags m_currentCheckpoint = CheckpointFlags::None; + CheckpointFlags m_targetCheckpoint = CheckpointFlags::None; }; } diff --git a/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp b/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp index 2390bec7fd..13a29a1eba 100644 --- a/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp +++ b/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp @@ -1275,6 +1275,12 @@ AppInstaller::CLI::Execution::Context& operator<<(AppInstaller::CLI::Execution:: AppInstaller::CLI::Execution::Context& operator<<(AppInstaller::CLI::Execution::Context& context, const AppInstaller::CLI::Workflow::WorkflowTask& task) { + // Maybe add a should we execute workflow check using checkpoint. + if (context.GetCurrentCheckpoint() != context.GetTargetCheckpoint()) + { + return context; + } + if (!context.IsTerminated()) { #ifndef AICLI_DISABLE_TEST_HOOKS diff --git a/src/AppInstallerCLICore/Workflows/WorkflowBase.h b/src/AppInstallerCLICore/Workflows/WorkflowBase.h index 1d0dc1d433..7fb92dec37 100644 --- a/src/AppInstallerCLICore/Workflows/WorkflowBase.h +++ b/src/AppInstallerCLICore/Workflows/WorkflowBase.h @@ -10,7 +10,6 @@ #include #include - namespace AppInstaller::CLI::Execution { struct Context; diff --git a/src/AppInstallerRepositoryCore/Microsoft/CheckpointIndex.cpp b/src/AppInstallerRepositoryCore/Microsoft/CheckpointIndex.cpp index 2b8cc5309a..0cb7035a8c 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/CheckpointIndex.cpp +++ b/src/AppInstallerRepositoryCore/Microsoft/CheckpointIndex.cpp @@ -121,6 +121,11 @@ namespace AppInstaller::Repository::Microsoft return m_interface->GetCommandName(m_dbconn); } + std::vector> CheckpointIndex::GetArguments() + { + return m_interface->GetArguments(m_dbconn); + } + std::unique_ptr CheckpointIndex::CreateICheckpointIndex() const { if (m_version == Schema::Version{ 1, 0 } || diff --git a/src/AppInstallerRepositoryCore/Microsoft/CheckpointIndex.h b/src/AppInstallerRepositoryCore/Microsoft/CheckpointIndex.h index b632adb452..30424f554a 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/CheckpointIndex.h +++ b/src/AppInstallerRepositoryCore/Microsoft/CheckpointIndex.h @@ -46,6 +46,8 @@ namespace AppInstaller::Repository::Microsoft std::string GetCommandName(); + std::vector> GetArguments(); + private: // Constructor used to open an existing index. CheckpointIndex(const std::string& target, SQLiteStorageBase::OpenDisposition disposition, Utility::ManagedFile&& indexFile); diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointIndexInterface.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointIndexInterface.h index a2877cd4e4..9e1c82189f 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointIndexInterface.h +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointIndexInterface.h @@ -22,6 +22,8 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 std::string GetCommandName(SQLite::Connection& connection) override; + std::vector> GetArguments(SQLite::Connection& connection) override; + private: bool IsEmpty(SQLite::Connection& connection) override; }; diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointIndexInterface_1_0.cpp b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointIndexInterface_1_0.cpp index 48463cb928..c5d059f8b0 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointIndexInterface_1_0.cpp +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointIndexInterface_1_0.cpp @@ -80,4 +80,9 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 { return CheckpointTable::IsEmpty(connection); } + + std::vector> CheckpointIndexInterface::GetArguments(SQLite::Connection& connection) + { + return CheckpointTable::GetArguments(connection); + } } \ No newline at end of file diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointTable.cpp b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointTable.cpp index 148035de7a..6a9b782541 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointTable.cpp +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointTable.cpp @@ -94,4 +94,21 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 builder.Execute(connection); return connection.GetLastInsertRowID(); } + + std::vector> CheckpointTable::GetArguments(SQLite::Connection& connection) + { + SQLite::Builder::StatementBuilder builder; + builder.Select({ s_CheckpointTable_ArgumentType_Column, s_CheckpointTable_ArgumentValue_Column}) + .From(s_CheckpointTable_Table_Name); + + SQLite::Statement select = builder.Prepare(connection); + std::vector> result; + while (select.Step()) + { + auto [argumentType, argumentValue] = select.GetRow(); + result.emplace_back(std::pair{argumentType, argumentValue}); + } + + return result; + } } \ No newline at end of file diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointTable.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointTable.h index 3d1f20afc7..91308aa533 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointTable.h +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointTable.h @@ -30,5 +30,7 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 // Adds the command argument and value to the table. static SQLite::rowid_t AddCommandArgument(SQLite::Connection& connection, int type, const std::string_view& value); + + static std::vector> GetArguments(SQLite::Connection& connection); }; } \ No newline at end of file diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/ICheckpointIndex.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/ICheckpointIndex.h index 1b302e8190..f265c07bda 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/Schema/ICheckpointIndex.h +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/ICheckpointIndex.h @@ -31,6 +31,8 @@ namespace AppInstaller::Repository::Microsoft::Schema virtual std::string GetCommandName(SQLite::Connection& connection) = 0; + virtual std::vector> GetArguments(SQLite::Connection& connection) = 0; + // Returns a bool value indicating whether the index is empty. virtual bool IsEmpty(SQLite::Connection& connection) = 0; }; diff --git a/src/AppInstallerSharedLib/Errors.cpp b/src/AppInstallerSharedLib/Errors.cpp index 73b6f5eb85..b5567f82df 100644 --- a/src/AppInstallerSharedLib/Errors.cpp +++ b/src/AppInstallerSharedLib/Errors.cpp @@ -226,6 +226,8 @@ namespace AppInstaller return "The package currently installed is the stub package"; case APPINSTALLER_CLI_ERROR_APPTERMINATION_RECEIVED: return "Application shutdown signal received"; + case APPINSTALLER_CLI_ERROR_CLIENTVERSION_MISMATCH: + return "The current client version did not match the client version of the saved state."; // Install errors case APPINSTALLER_CLI_ERROR_INSTALL_PACKAGE_IN_USE: diff --git a/src/AppInstallerSharedLib/Public/AppInstallerErrors.h b/src/AppInstallerSharedLib/Public/AppInstallerErrors.h index ea88e64636..454dd28f91 100644 --- a/src/AppInstallerSharedLib/Public/AppInstallerErrors.h +++ b/src/AppInstallerSharedLib/Public/AppInstallerErrors.h @@ -119,6 +119,7 @@ #define APPINSTALLER_CLI_ERROR_PACKAGE_IS_PINNED ((HRESULT)0x8A150068) #define APPINSTALLER_CLI_ERROR_PACKAGE_IS_STUB ((HRESULT)0x8A150069) #define APPINSTALLER_CLI_ERROR_APPTERMINATION_RECEIVED ((HRESULT)0x8A15006A) +#define APPINSTALLER_CLI_ERROR_CLIENTVERSION_MISMATCH ((HRESULT)0x8A15006B) // Install errors. #define APPINSTALLER_CLI_ERROR_INSTALL_PACKAGE_IN_USE ((HRESULT)0x8A150101) From 166b843673f3e5093c91765ad68b599af9db64cf Mon Sep 17 00:00:00 2001 From: Ryan Fu Date: Fri, 28 Jul 2023 17:54:23 -0700 Subject: [PATCH 05/43] retrieve args from index --- src/AppInstallerCLICore/Argument.cpp | 47 ++++ src/AppInstallerCLICore/Argument.h | 3 + src/AppInstallerCLICore/CheckpointManager.cpp | 123 +++++++-- src/AppInstallerCLICore/CheckpointManager.h | 24 +- .../Commands/InstallCommand.cpp | 10 +- .../Commands/ResumeCommand.cpp | 13 +- src/AppInstallerCLICore/ExecutionContext.cpp | 2 + src/AppInstallerCLICore/ExecutionContext.h | 8 +- src/AppInstallerCLITests/Strings.cpp | 15 + .../AppInstallerRepositoryCore.vcxproj | 4 +- ...AppInstallerRepositoryCore.vcxproj.filters | 4 +- .../Microsoft/CheckpointIndex.cpp | 97 +++++-- .../Microsoft/CheckpointIndex.h | 24 +- .../CheckpointArgumentsTable.cpp | 261 ++++++++++++++++++ ...ointTable.h => CheckpointArgumentsTable.h} | 23 +- .../Checkpoint_1_0/CheckpointIndexInterface.h | 18 +- .../CheckpointIndexInterface_1_0.cpp | 71 +++-- .../CheckpointMetadataTable.cpp | 2 +- .../Schema/Checkpoint_1_0/CheckpointTable.cpp | 114 -------- .../Microsoft/Schema/ICheckpointIndex.h | 25 +- .../SQLiteWrapper.cpp | 7 + .../SQLiteWrapper.h | 3 + .../AppInstallerStrings.cpp | 19 ++ .../Public/AppInstallerStrings.h | 2 + 24 files changed, 692 insertions(+), 227 deletions(-) create mode 100644 src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointArgumentsTable.cpp rename src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/{CheckpointTable.h => CheckpointArgumentsTable.h} (50%) delete mode 100644 src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointTable.cpp diff --git a/src/AppInstallerCLICore/Argument.cpp b/src/AppInstallerCLICore/Argument.cpp index e96bec4e04..fc609a9e5f 100644 --- a/src/AppInstallerCLICore/Argument.cpp +++ b/src/AppInstallerCLICore/Argument.cpp @@ -346,6 +346,53 @@ namespace AppInstaller::CLI } } + std::string_view Argument::GetName(Execution::Args::Type type) + { + switch (type) + { + case Args::Type::Query: + return "Query"sv; + case Args::Type::MultiQuery: + return "MultiQuery"sv; + case Args::Type::Manifest: + return "Manifest"sv; + case Args::Type::Id: + return "Id"sv; + case Args::Type::Name: + return "Name"sv; + case Args::Type::Moniker: + return "Moniker"sv; + case Args::Type::Tag: + return "Tag"sv; + case Args::Type::Command: + return "Command"sv; + case Args::Type::Source: + return "Source"sv; + case Args::Type::DependencySource: + return "DependencySource"sv; + case Args::Type::Count: + return "Count"sv; + case Args::Type::Exact: + return "Exact"sv; + case Args::Type::Version: + return "Version"sv; + case Args::Type::Channel: + return "Channel"sv; + case Args::Type::Interactive: + return "Interactive"sv; + case Args::Type::Silent: + return "Silent"sv; + case Args::Type::Locale: + return "Locale"sv; + case Args::Type::InstallArchitecture: + return "InstallArchitecture"sv; + case Args::Type::InstallScope: + return "InstallScope"sv; + default: + THROW_HR(E_UNEXPECTED); + } + } + void Argument::GetCommon(std::vector& args) { args.push_back(ForType(Args::Type::Help)); diff --git a/src/AppInstallerCLICore/Argument.h b/src/AppInstallerCLICore/Argument.h index 202664788c..0e662c39fa 100644 --- a/src/AppInstallerCLICore/Argument.h +++ b/src/AppInstallerCLICore/Argument.h @@ -178,6 +178,9 @@ namespace AppInstaller::CLI // Gets the common arguments for all commands. static void GetCommon(std::vector& args); + // Gets the name for a given argument type. + static std::string_view GetName(Execution::Args::Type type); + // Static argument validation helpers; throw CommandException when validation fails. // Requires that at most one argument from the list is present. diff --git a/src/AppInstallerCLICore/CheckpointManager.cpp b/src/AppInstallerCLICore/CheckpointManager.cpp index a6cd5dc93b..42ba321fd4 100644 --- a/src/AppInstallerCLICore/CheckpointManager.cpp +++ b/src/AppInstallerCLICore/CheckpointManager.cpp @@ -1,14 +1,42 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" +#include "AppInstallerRuntime.h" #include "CheckpointManager.h" #include "Microsoft/CheckpointIndex.h" #include "Microsoft/SQLiteStorageBase.h" +#include +#include namespace AppInstaller::CLI::Checkpoint { - CheckpointManager::CheckpointManager(GUID checkpointId) : m_checkpointId(checkpointId) + void CheckpointManager::Initialize() { + // Initialize should not be called more than once. + THROW_HR_IF(E_UNEXPECTED, m_checkpointId != GUID_NULL); + + std::ignore = CoCreateGuid(&m_checkpointId); + AICLI_LOG(CLI, Info, << "Creating checkpoint index with the corresponding guid: " << m_checkpointId); + + //auto openDisposition = m_readOnly ? SQLiteStorageBase::OpenDisposition::Read : SQLiteStorageBase::OpenDisposition::ReadWrite; + auto openDisposition = AppInstaller::Repository::Microsoft::SQLiteStorageBase::OpenDisposition::ReadWrite; + auto checkpointIndex = AppInstaller::Repository::Microsoft::CheckpointIndex::OpenOrCreateDefault(m_checkpointId, openDisposition); + if (!checkpointIndex) + { + AICLI_LOG(CLI, Error, << "Unable to open checkpoint index."); + } + + m_checkpointIndex = std::move(checkpointIndex); + m_checkpointIndex->SetClientVersion(AppInstaller::Runtime::GetClientVersion()); + //m_checkpointIndex->SetCommandName(commandName); + } + + void CheckpointManager::InitializeFromGuid(GUID checkpointId) + { + // Initialize should not be called more than once. + THROW_HR_IF(E_UNEXPECTED, m_checkpointId != GUID_NULL); + + m_checkpointId = checkpointId; auto openDisposition = AppInstaller::Repository::Microsoft::SQLiteStorageBase::OpenDisposition::ReadWrite; auto checkpointIndex = AppInstaller::Repository::Microsoft::CheckpointIndex::OpenOrCreateDefault(m_checkpointId, openDisposition); if (!checkpointIndex) @@ -27,53 +55,90 @@ namespace AppInstaller::CLI::Checkpoint checkpointContext->EnableSignalTerminationHandler(); - // Change to loading in context from checkpoint. - PopulateContextArgsFromCheckpointIndex(*(checkpointContext.get())); + PopulateContextArgsFromIndex(*checkpointContext); return checkpointContext; } - void CheckpointManager::PopulateContextArgsFromCheckpointIndex(Execution::Context& context) + void CheckpointManager::PopulateContextArgsFromIndex(Execution::Context& context) { - std::vector> args = m_checkpointIndex->GetArguments(); + int contextId = context.GetContextId(); - for (auto arg : args) + const auto& executingCommand = context.GetExecutingCommand(); + if (executingCommand != nullptr) { - context.Args.AddArg(static_cast(arg.first), arg.second); + const auto& commandArguments = executingCommand->GetArguments(); + for (const auto& argument : commandArguments) + { + if (m_checkpointIndex->ContainsArgument(contextId, argument.Name())) + { + Execution::Args::Type executionArgsType = argument.ExecArgType(); + if (argument.Type() == ArgumentType::Flag) + { + if (m_checkpointIndex->GetBoolArgumentByContextId(contextId, argument.Name())) + { + context.Args.AddArg(executionArgsType); + } + } + else + { + context.Args.AddArg(executionArgsType, m_checkpointIndex->GetStringArgumentByContextId(contextId, argument.Name())); + } + } + } } } - void CheckpointManager::InitializeCheckpoint(std::string_view clientVersion, std::string_view commandName) + void CheckpointManager::RecordContextArgsToIndex(Execution::Context& context) { - std::ignore = CoCreateGuid(&m_checkpointId); - AICLI_LOG(CLI, Info, << "Creating checkpoint index with the corresponding guid: " << m_checkpointId); + int contextId = context.GetContextId(); - //auto openDisposition = m_readOnly ? SQLiteStorageBase::OpenDisposition::Read : SQLiteStorageBase::OpenDisposition::ReadWrite; - auto openDisposition = AppInstaller::Repository::Microsoft::SQLiteStorageBase::OpenDisposition::ReadWrite; - auto checkpointIndex = AppInstaller::Repository::Microsoft::CheckpointIndex::OpenOrCreateDefault(m_checkpointId, openDisposition); - if (!checkpointIndex) + // Figure out if there is a better place to add the context when it is first initialized. + m_checkpointIndex->AddContextToArgumentTable(contextId); + + const auto& executingCommand = context.GetExecutingCommand(); + if (executingCommand != nullptr) { - AICLI_LOG(CLI, Error, << "Unable to open savepoint index."); + const auto& commandArguments = executingCommand->GetArguments(); + for (const auto& argument : commandArguments) + { + Execution::Args::Type type = argument.ExecArgType(); + if (context.Args.Contains(type)) + { + if (argument.Type() == ArgumentType::Flag) + { + m_checkpointIndex->UpdateArgumentByContextId(contextId, argument.Name(), true); + } + else + { + const auto& argumentValue = context.Args.GetArg(type); + m_checkpointIndex->UpdateArgumentByContextId(contextId, argument.Name(), argumentValue); + } + } + } } - m_checkpointIndex = std::move(checkpointIndex); - - m_checkpointIndex->SetClientVersion(clientVersion); - m_checkpointIndex->SetCommandName(commandName); } void CheckpointManager::SaveCheckpoint(Execution::Context& context, Execution::CheckpointFlags flag) { - if (flag == Execution::CheckpointFlags::ArgumentsProcessed) + switch (flag) { - // Get all args and write them to the index. - const std::vector& arguments = context.Args.GetTypes(); - - for (auto argument : arguments) - { - const auto& argumentValue = context.Args.GetArg(argument); + case Execution::CheckpointFlags::ArgumentsProcessed: + RecordContextArgsToIndex(context); + break; + default: + THROW_HR(E_UNEXPECTED); + } + } - // Write current arg to table. - m_checkpointIndex->AddCommandArgument(static_cast(argument), argumentValue); - } + void CheckpointManager::LoadCheckpoint(Execution::Context& context, Execution::CheckpointFlags flag) + { + switch (flag) + { + case Execution::CheckpointFlags::ArgumentsProcessed: + PopulateContextArgsFromIndex(context); + break; + default: + THROW_HR(E_UNEXPECTED); } } diff --git a/src/AppInstallerCLICore/CheckpointManager.h b/src/AppInstallerCLICore/CheckpointManager.h index e461978169..1a0142698d 100644 --- a/src/AppInstallerCLICore/CheckpointManager.h +++ b/src/AppInstallerCLICore/CheckpointManager.h @@ -14,12 +14,22 @@ namespace AppInstaller::CLI::Checkpoint { struct CheckpointManager { - CheckpointManager(GUID checkpointId); + static CheckpointManager& Instance() + { + static CheckpointManager s_Instance; + return s_Instance; + } - void InitializeCheckpoint(std::string_view clientVersion, std::string_view commandName); + void Initialize(); // checks if guid is already defined, this means that it has already been loaded. + + void InitializeFromGuid(GUID checkpointId); + + void RemoveContext(Execution::Context& context); void SaveCheckpoint(Execution::Context& context, Execution::CheckpointFlags flag); + void LoadCheckpoint(Execution::Context& context, Execution::CheckpointFlags flag); + std::unique_ptr CreateContextFromCheckpointIndex(); std::string GetClientVersion(); @@ -27,8 +37,12 @@ namespace AppInstaller::CLI::Checkpoint std::string GetCommandName(); private: - GUID m_checkpointId; + CheckpointManager() = default; + GUID m_checkpointId = {}; std::shared_ptr m_checkpointIndex = nullptr; - void PopulateContextArgsFromCheckpointIndex(Execution::Context& context); + + void PopulateContextArgsFromIndex(Execution::Context& context); + void RecordContextArgsToIndex(Execution::Context& context); }; -} \ No newline at end of file +} +// \ No newline at end of file diff --git a/src/AppInstallerCLICore/Commands/InstallCommand.cpp b/src/AppInstallerCLICore/Commands/InstallCommand.cpp index 17b1cec1b7..618cf42f96 100644 --- a/src/AppInstallerCLICore/Commands/InstallCommand.cpp +++ b/src/AppInstallerCLICore/Commands/InstallCommand.cpp @@ -2,6 +2,7 @@ // Licensed under the MIT License. #include "pch.h" #include "AppInstallerRuntime.h" +#include "CheckpointManager.h" #include "InstallCommand.h" #include "Workflows/CompletionFlow.h" #include "Workflows/InstallFlow.h" @@ -12,6 +13,7 @@ namespace AppInstaller::CLI { + using namespace AppInstaller::CLI::Checkpoint; using namespace AppInstaller::CLI::Execution; using namespace AppInstaller::CLI::Workflow; using namespace AppInstaller::Manifest; @@ -105,13 +107,11 @@ namespace AppInstaller::CLI { if (Settings::ExperimentalFeature::IsEnabled(Settings::ExperimentalFeature::Feature::Resume)) { - //context.InitializeCheckpoints(AppInstaller::Runtime::GetClientVersion(), this->Name()); - - // Record the initial arguments. - //context.Checkpoint(CheckpointFlags::ArgumentsProcessed); + CheckpointManager checkpointManager = CheckpointManager::Instance(); + checkpointManager.Initialize(); + checkpointManager.SaveCheckpoint(context, Execution::CheckpointFlags::ArgumentsProcessed); } - context.SetFlags(ContextFlag::ShowSearchResultsOnPartialFailure); if (context.Args.Contains(Execution::Args::Type::Manifest)) diff --git a/src/AppInstallerCLICore/Commands/ResumeCommand.cpp b/src/AppInstallerCLICore/Commands/ResumeCommand.cpp index 861162b5c0..fd01933af5 100644 --- a/src/AppInstallerCLICore/Commands/ResumeCommand.cpp +++ b/src/AppInstallerCLICore/Commands/ResumeCommand.cpp @@ -37,12 +37,10 @@ namespace AppInstaller::CLI void ResumeCommand::ExecuteInternal(Execution::Context& context) const { - // Convert guid argument and load checkpoint file. - const auto& resumeGuid = Utility::ConvertToUTF16(context.Args.GetArg(Execution::Args::Type::ResumeGuid)); + GUID checkpointId = Utility::ConvertToGuid(std::string{ context.Args.GetArg(Execution::Args::Type::ResumeGuid) }); - GUID checkpointId; - THROW_IF_FAILED(CLSIDFromString(resumeGuid.c_str(), &checkpointId)); - CheckpointManager checkpointManager{ checkpointId }; + CheckpointManager checkpointManager = CheckpointManager::Instance(); + checkpointManager.InitializeFromGuid(checkpointId); if (AppInstaller::Runtime::GetClientVersion().get() != checkpointManager.GetClientVersion()) { @@ -65,11 +63,14 @@ namespace AppInstaller::CLI { AICLI_LOG(CLI, Info, << "Resuming command: " << commandName); commandToResume = std::move(command); + break; } } + // Create a new context and load from checkpoint auto checkpointContext = checkpointManager.CreateContextFromCheckpointIndex(); checkpointContext->SetExecutingCommand(commandToResume.get()); - commandToResume->Execute(*(checkpointContext.get())); + checkpointManager.LoadCheckpoint(*checkpointContext, Execution::CheckpointFlags::ArgumentsProcessed); + commandToResume->Execute(*checkpointContext); } } diff --git a/src/AppInstallerCLICore/ExecutionContext.cpp b/src/AppInstallerCLICore/ExecutionContext.cpp index 2dbaf3cbf6..b96c1304ce 100644 --- a/src/AppInstallerCLICore/ExecutionContext.cpp +++ b/src/AppInstallerCLICore/ExecutionContext.cpp @@ -248,6 +248,8 @@ namespace AppInstaller::CLI::Execution } } + int Context::s_contextCount = 0; + Context::~Context() { if (m_disableSignalTerminationHandlerOnExit) diff --git a/src/AppInstallerCLICore/ExecutionContext.h b/src/AppInstallerCLICore/ExecutionContext.h index 0a355753f0..6f01f28744 100644 --- a/src/AppInstallerCLICore/ExecutionContext.h +++ b/src/AppInstallerCLICore/ExecutionContext.h @@ -86,12 +86,12 @@ namespace AppInstaller::CLI::Execution // arguments via Execution::Args. struct Context : EnumBasedVariantMap { - Context(std::ostream& out, std::istream& in) : Reporter(out, in) {} + Context(std::ostream& out, std::istream& in) : Reporter(out, in) { s_contextCount++; } // Constructor for creating a sub-context. Context(Execution::Reporter& reporter, ThreadLocalStorage::WingetThreadGlobals& threadGlobals) : Reporter(reporter, Execution::Reporter::clone_t{}), - m_threadGlobals(threadGlobals, ThreadLocalStorage::WingetThreadGlobals::create_sub_thread_globals_t{}) {} + m_threadGlobals(threadGlobals, ThreadLocalStorage::WingetThreadGlobals::create_sub_thread_globals_t{}) { s_contextCount++; } virtual ~Context(); @@ -174,6 +174,8 @@ namespace AppInstaller::CLI::Execution // Gets the target checkpoint of the context. CheckpointFlags GetTargetCheckpoint() { return m_targetCheckpoint; }; + int GetContextId() { return m_contextId; }; + protected: // Copies the args that are also needed in a sub-context. E.g., silent void CopyArgsToSubContext(Context* subContext); @@ -194,5 +196,7 @@ namespace AppInstaller::CLI::Execution AppInstaller::CLI::Command* m_executingCommand = nullptr; CheckpointFlags m_currentCheckpoint = CheckpointFlags::None; CheckpointFlags m_targetCheckpoint = CheckpointFlags::None; + static int s_contextCount; + int m_contextId = s_contextCount; }; } diff --git a/src/AppInstallerCLITests/Strings.cpp b/src/AppInstallerCLITests/Strings.cpp index 95311cdab2..6bf06449ea 100644 --- a/src/AppInstallerCLITests/Strings.cpp +++ b/src/AppInstallerCLITests/Strings.cpp @@ -279,3 +279,18 @@ TEST_CASE("SplitWithSeparator", "[string]") REQUIRE(test3.size() == 1); REQUIRE(test3[0] == "test"); } + +TEST_CASE("ConvertToGuid", "[string]") +{ + std::string validGuidString1 = "{4d1e55b2-f16f-11cf-88cb-001111000030}"; + std::string validGuidString2 = "4d1e55b2-f16f-11cf-88cb-001111000030"; + std::string invalidGuidString1 = "4d1e55b2-f16f-11cf-001111000030"; + std::string invalidGuidString2 = "4d1e55b2f16f11cf88cb001111000030"; + + GUID guid = { 0x4d1e55b2, 0xf16f, 0x11cf, 0x88, 0xcb, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30 }; + + REQUIRE(ConvertToGuid(validGuidString1) == guid); + REQUIRE(ConvertToGuid(validGuidString2) == guid); + REQUIRE_THROWS(ConvertToGuid(invalidGuidString1)); + REQUIRE_THROWS(ConvertToGuid(invalidGuidString2)); +} diff --git a/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj b/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj index b4043ed490..86faf541e3 100644 --- a/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj +++ b/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj @@ -397,7 +397,7 @@ - + @@ -499,7 +499,7 @@ - + diff --git a/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj.filters b/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj.filters index e87ccda7b6..0dc765edbc 100644 --- a/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj.filters +++ b/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj.filters @@ -375,7 +375,7 @@ Microsoft\Schema\Checkpoint_1_0 - + Microsoft\Schema\Checkpoint_1_0 @@ -602,7 +602,7 @@ Source Files - + Microsoft\Schema\Checkpoint_1_0 diff --git a/src/AppInstallerRepositoryCore/Microsoft/CheckpointIndex.cpp b/src/AppInstallerRepositoryCore/Microsoft/CheckpointIndex.cpp index 0cb7035a8c..b4a689d3a6 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/CheckpointIndex.cpp +++ b/src/AppInstallerRepositoryCore/Microsoft/CheckpointIndex.cpp @@ -63,22 +63,6 @@ namespace AppInstaller::Repository::Microsoft return {}; } - CheckpointIndex::IdType CheckpointIndex::AddCommandArgument(int type, const std::string_view& argValue) - { - std::lock_guard lockInterface{ *m_interfaceLock }; - AICLI_LOG(Repo, Verbose, << "Adding argument for [" << type << "]"); - - SQLite::Savepoint savepoint = SQLite::Savepoint::Create(m_dbconn, "checkpointindex_addargument"); - - IdType result = m_interface->AddCommandArgument(m_dbconn, type, argValue); - - SetLastWriteTime(); - - savepoint.Commit(); - - return result; - } - CheckpointIndex::IdType CheckpointIndex::SetClientVersion(std::string_view clientVersion) { std::lock_guard lockInterface{ *m_interfaceLock }; @@ -121,9 +105,86 @@ namespace AppInstaller::Repository::Microsoft return m_interface->GetCommandName(m_dbconn); } - std::vector> CheckpointIndex::GetArguments() + CheckpointIndex::IdType CheckpointIndex::AddContextToArgumentTable(int contextId) + { + std::lock_guard lockInterface{ *m_interfaceLock }; + AICLI_LOG(Repo, Verbose, << "Adding context [" << contextId << "] to arguments table"); + + SQLite::Savepoint savepoint = SQLite::Savepoint::Create(m_dbconn, "checkpointindex_addcontexttoargumentstable"); + + IdType result = m_interface->AddContextToArgumentTable(m_dbconn, contextId); + + SetLastWriteTime(); + + savepoint.Commit(); + + return result; + } + + void CheckpointIndex::RemoveContextFromArgumentTable(int contextId) + { + std::lock_guard lockInterface{ *m_interfaceLock }; + AICLI_LOG(Repo, Verbose, << "Removing context [" << contextId << "] to arguments table"); + + SQLite::Savepoint savepoint = SQLite::Savepoint::Create(m_dbconn, "checkpointindex_removecontextfromargumentstable"); + + m_interface->RemoveContextFromArgumentTable(m_dbconn, contextId); + + SetLastWriteTime(); + + savepoint.Commit(); + } + + bool CheckpointIndex::UpdateArgumentByContextId(int contextId, std::string_view name, std::string_view value) + { + std::lock_guard lockInterface{ *m_interfaceLock }; + AICLI_LOG(Repo, Verbose, << "Updating argument column for [" << name << "] with value [" << value << "]"); + + SQLite::Savepoint savepoint = SQLite::Savepoint::Create(m_dbconn, "checkpointindex_updatestringargument"); + + IdType result = m_interface->UpdateArgumentByContextId(m_dbconn, contextId, name, value); + + SetLastWriteTime(); + + savepoint.Commit(); + + return result; + } + + bool CheckpointIndex::UpdateArgumentByContextId(int contextId, std::string_view name, bool value) + { + std::lock_guard lockInterface{ *m_interfaceLock }; + AICLI_LOG(Repo, Verbose, << "Updating argument column for [" << name << "] with value [" << value << "]"); + + SQLite::Savepoint savepoint = SQLite::Savepoint::Create(m_dbconn, "checkpointindex_updateboolargument"); + + IdType result = m_interface->UpdateArgumentByContextId(m_dbconn, contextId, name, value); + + SetLastWriteTime(); + + savepoint.Commit(); + + return result; + } + + std::vector CheckpointIndex::GetAvailableColumns(int contextId) + { + return m_interface->GetAvailableArguments(m_dbconn, contextId); + } + + bool CheckpointIndex::ContainsArgument(int contextId, std::string_view name) + { + return m_interface->ContainsArgument(m_dbconn, contextId, name); + } + + std::string CheckpointIndex::GetStringArgumentByContextId(int contextId, std::string_view name) + { + return m_interface->GetStringArgumentByContextId(m_dbconn, contextId, name); + } + + bool CheckpointIndex::GetBoolArgumentByContextId(int contextId, std::string_view name) { - return m_interface->GetArguments(m_dbconn); + return m_interface->GetBoolArgumentByContextId(m_dbconn, contextId, name); } std::unique_ptr CheckpointIndex::CreateICheckpointIndex() const diff --git a/src/AppInstallerRepositoryCore/Microsoft/CheckpointIndex.h b/src/AppInstallerRepositoryCore/Microsoft/CheckpointIndex.h index 30424f554a..8c0feeda38 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/CheckpointIndex.h +++ b/src/AppInstallerRepositoryCore/Microsoft/CheckpointIndex.h @@ -3,7 +3,7 @@ #pragma once #include "SQLiteWrapper.h" #include "Microsoft/Schema/ICheckpointIndex.h" -#include "Microsoft/Schema/Checkpoint_1_0/CheckpointTable.h" +#include "Microsoft/Schema/Checkpoint_1_0/CheckpointArgumentsTable.h" #include "Microsoft/SQLiteStorageBase.h" #include @@ -32,12 +32,6 @@ namespace AppInstaller::Repository::Microsoft // Opens or creates a CheckpointIndex database on the default path. static std::shared_ptr OpenOrCreateDefault(GUID guid, OpenDisposition openDisposition = OpenDisposition::ReadWrite); - // Adds a command argument to the index. - IdType AddCommandArgument(int type, const std::string_view& argValue); - - // Removes a command argument from the index. - //IdType RemoveCommandArgument(const uint32_t& type, const std::string_view& argValue); - IdType SetClientVersion(std::string_view clientVersion); std::string GetClientVersion(); @@ -46,7 +40,21 @@ namespace AppInstaller::Repository::Microsoft std::string GetCommandName(); - std::vector> GetArguments(); + IdType AddContextToArgumentTable(int contextId); + + void RemoveContextFromArgumentTable(int contextId); + + bool UpdateArgumentByContextId(int contextId, std::string_view name, std::string_view value); + + bool UpdateArgumentByContextId(int contextId, std::string_view name, bool value); + + std::vector GetAvailableColumns(int contextId); + + bool ContainsArgument(int contextId, std::string_view name); + + std::string GetStringArgumentByContextId(int contextId, std::string_view name); + + bool GetBoolArgumentByContextId(int contextId, std::string_view name); private: // Constructor used to open an existing index. diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointArgumentsTable.cpp b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointArgumentsTable.cpp new file mode 100644 index 0000000000..bf5fd99ab7 --- /dev/null +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointArgumentsTable.cpp @@ -0,0 +1,261 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#include "pch.h" +#include "CheckpointArgumentsTable.h" +#include "SQLiteStatementBuilder.h" +#include "Microsoft/Schema/ICheckpointIndex.h" + +namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 +{ + using namespace SQLite; + using namespace std::string_view_literals; + static constexpr std::string_view s_CheckpointArgumentsTable_Table_Name = "CheckpointArguments"sv; + static constexpr std::string_view s_CheckpointArgumentsTable_ContextId_Column = "ContextId"sv; + static constexpr std::string_view s_CheckpointArgumentsTable_Query_Column = "Query"sv; + static constexpr std::string_view s_CheckpointArgumentsTable_MultiQuery_Column = "MultiQuery"sv; + static constexpr std::string_view s_CheckpointArgumentsTable_Manifest_Column = "Manifest"sv; + static constexpr std::string_view s_CheckpointArgumentsTable_Id_Column = "Id"sv; + static constexpr std::string_view s_CheckpointArgumentsTable_Moniker_Column = "Moniker"sv; + static constexpr std::string_view s_CheckpointArgumentsTable_Version_Column = "Version"sv; + static constexpr std::string_view s_CheckpointArgumentsTable_Channel_Column = "Channel"sv; + static constexpr std::string_view s_CheckpointArgumentsTable_Source_Column = "Source"sv; + static constexpr std::string_view s_CheckpointArgumentsTable_InstallScope_Column = "InstallScope"sv; + static constexpr std::string_view s_CheckpointArgumentsTable_InstallArchitecture_Column = "InstallArchitecture"sv; + static constexpr std::string_view s_CheckpointArgumentsTable_Exact_Column = "Exact"sv; + static constexpr std::string_view s_CheckpointArgumentsTable_Interactive_Column = "Interactive"sv; + static constexpr std::string_view s_CheckpointArgumentsTable_Silent_Column = "Silent"sv; + static constexpr std::string_view s_CheckpointArgumentsTable_Locale_Column = "Locale"sv; + static constexpr std::string_view s_CheckpointArgumentsTable_Log_Column = "Log"sv; + static constexpr std::string_view s_CheckpointArgumentsTable_CustomSwitches_Column = "CustomSwitches"sv; + static constexpr std::string_view s_CheckpointArgumentsTable_Override_Column = "Override"sv; + static constexpr std::string_view s_CheckpointArgumentsTable_InstallLocation_Column = "InstallLocation"sv; + static constexpr std::string_view s_CheckpointArgumentsTable_HashOverride_Column = "HashOverride"sv; + static constexpr std::string_view s_CheckpointArgumentsTable_SkipDependencies_Column = "SkipDependencies"sv; + static constexpr std::string_view s_CheckpointArgumentsTable_IgnoreLocalArchiveMalwareScan_Column = "IgnoreLocalArchiveMalwareScan"sv; + static constexpr std::string_view s_CheckpointArgumentsTable_DependencySource_Column = "DependencySource"sv; + static constexpr std::string_view s_CheckpointArgumentsTable_AcceptPackageAgreements_Column = "AcceptPackageAgreements"sv; + static constexpr std::string_view s_CheckpointArgumentsTable_NoUpgrade_Column = "NoUpgrade"sv; + static constexpr std::string_view s_CheckpointArgumentsTable_CustomHeader_Column = "CustomHeader"sv; + static constexpr std::string_view s_CheckpointArgumentsTable_AcceptSourceAgreements_Column = "AcceptSourceAgreements"sv; + static constexpr std::string_view s_CheckpointArgumentsTable_Rename_Column = "Rename"sv; + static constexpr std::string_view s_CheckpointArgumentsTable_UninstallPrevious_Column = "UninstallPrevious"sv; + static constexpr std::string_view s_CheckpointArgumentsTable_Force_Column = "Force"sv; + + std::string_view CheckpointArgumentsTable::TableName() + { + return s_CheckpointArgumentsTable_Table_Name; + } + + void CheckpointArgumentsTable::Create(SQLite::Connection& connection) + { + using namespace SQLite::Builder; + + SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "createCheckpointArgumentsTable_v1_0"); + + StatementBuilder createTableBuilder; + + createTableBuilder.CreateTable(s_CheckpointArgumentsTable_Table_Name).BeginColumns(); + + createTableBuilder.Column(ColumnBuilder(s_CheckpointArgumentsTable_ContextId_Column, Type::Int).PrimaryKey().NotNull()); + createTableBuilder.Column(ColumnBuilder(s_CheckpointArgumentsTable_Query_Column, Type::Text)); + createTableBuilder.Column(ColumnBuilder(s_CheckpointArgumentsTable_MultiQuery_Column, Type::Text)); + createTableBuilder.Column(ColumnBuilder(s_CheckpointArgumentsTable_Manifest_Column, Type::Text)); + createTableBuilder.Column(ColumnBuilder(s_CheckpointArgumentsTable_Id_Column, Type::Text)); + createTableBuilder.Column(ColumnBuilder(s_CheckpointArgumentsTable_Moniker_Column, Type::Text)); + createTableBuilder.Column(ColumnBuilder(s_CheckpointArgumentsTable_Version_Column, Type::Text)); + createTableBuilder.Column(ColumnBuilder(s_CheckpointArgumentsTable_Channel_Column, Type::Text)); + createTableBuilder.Column(ColumnBuilder(s_CheckpointArgumentsTable_Source_Column, Type::Text)); + createTableBuilder.Column(ColumnBuilder(s_CheckpointArgumentsTable_InstallScope_Column, Type::Text)); + createTableBuilder.Column(ColumnBuilder(s_CheckpointArgumentsTable_InstallArchitecture_Column, Type::Text)); + createTableBuilder.Column(ColumnBuilder(s_CheckpointArgumentsTable_Exact_Column, Type::Bool)); + createTableBuilder.Column(ColumnBuilder(s_CheckpointArgumentsTable_Interactive_Column, Type::Bool)); + createTableBuilder.Column(ColumnBuilder(s_CheckpointArgumentsTable_Silent_Column, Type::Bool)); + createTableBuilder.Column(ColumnBuilder(s_CheckpointArgumentsTable_Locale_Column, Type::Text)); + createTableBuilder.Column(ColumnBuilder(s_CheckpointArgumentsTable_Log_Column, Type::Text)); + createTableBuilder.Column(ColumnBuilder(s_CheckpointArgumentsTable_CustomSwitches_Column, Type::Text)); + createTableBuilder.Column(ColumnBuilder(s_CheckpointArgumentsTable_Override_Column, Type::Text)); + createTableBuilder.Column(ColumnBuilder(s_CheckpointArgumentsTable_InstallLocation_Column, Type::Text)); + createTableBuilder.Column(ColumnBuilder(s_CheckpointArgumentsTable_SkipDependencies_Column, Type::Bool)); + createTableBuilder.Column(ColumnBuilder(s_CheckpointArgumentsTable_IgnoreLocalArchiveMalwareScan_Column, Type::Bool)); + createTableBuilder.Column(ColumnBuilder(s_CheckpointArgumentsTable_AcceptPackageAgreements_Column, Type::Bool)); + createTableBuilder.Column(ColumnBuilder(s_CheckpointArgumentsTable_NoUpgrade_Column, Type::Bool)); + createTableBuilder.Column(ColumnBuilder(s_CheckpointArgumentsTable_CustomHeader_Column, Type::Text)); + createTableBuilder.Column(ColumnBuilder(s_CheckpointArgumentsTable_AcceptSourceAgreements_Column, Type::Bool)); + createTableBuilder.Column(ColumnBuilder(s_CheckpointArgumentsTable_Rename_Column, Type::Text)); + createTableBuilder.Column(ColumnBuilder(s_CheckpointArgumentsTable_UninstallPrevious_Column, Type::Bool)); + createTableBuilder.Column(ColumnBuilder(s_CheckpointArgumentsTable_Force_Column, Type::Bool)); + + createTableBuilder.EndColumns(); + createTableBuilder.Execute(connection); + savepoint.Commit(); + } + + bool CheckpointArgumentsTable::ExistsById(const SQLite::Connection& connection, SQLite::rowid_t id) + { + SQLite::Builder::StatementBuilder builder; + builder.Select(SQLite::Builder::RowCount).From(s_CheckpointArgumentsTable_Table_Name).Where(SQLite::RowIDName).Equals(id); + + SQLite::Statement countStatement = builder.Prepare(connection); + + THROW_HR_IF(E_UNEXPECTED, !countStatement.Step()); + + return (countStatement.GetColumn(0) != 0); + } + + void CheckpointArgumentsTable::DeleteById(SQLite::Connection& connection, SQLite::rowid_t id) + { + SQLite::Builder::StatementBuilder builder; + builder.DeleteFrom(s_CheckpointArgumentsTable_Table_Name).Where(SQLite::RowIDName).Equals(id); + + builder.Execute(connection); + } + + bool CheckpointArgumentsTable::IsEmpty(SQLite::Connection& connection) + { + SQLite::Builder::StatementBuilder builder; + builder.Select(SQLite::Builder::RowCount).From(s_CheckpointArgumentsTable_Table_Name); + + SQLite::Statement countStatement = builder.Prepare(connection); + + THROW_HR_IF(E_UNEXPECTED, !countStatement.Step()); + + return (countStatement.GetColumn(0) == 0); + } + + std::optional CheckpointArgumentsTable::SelectByArgumentType(const SQLite::Connection& connection, int type) + { + SQLite::Builder::StatementBuilder builder; + builder.Select(SQLite::RowIDName).From(s_CheckpointArgumentsTable_Table_Name).Where("id"); + builder.Equals(type); + + SQLite::Statement select = builder.Prepare(connection); + + if (select.Step()) + { + return select.GetColumn(0); + } + else + { + return {}; + } + } + + bool CheckpointArgumentsTable::UpdateArgumentByContextId(SQLite::Connection& connection, int contextId, std::string_view name, std::string_view value) + { + SQLite::Builder::StatementBuilder builder; + builder.Update(s_CheckpointArgumentsTable_Table_Name).Set() + .Column(name).Equals(value) + .Where(s_CheckpointArgumentsTable_ContextId_Column).Equals(contextId); + + builder.Execute(connection); + return connection.GetChanges() != 0; + } + + bool CheckpointArgumentsTable::UpdateArgumentByContextId(SQLite::Connection& connection, int contextId, std::string_view name, bool value) + { + SQLite::Builder::StatementBuilder builder; + builder.Update(s_CheckpointArgumentsTable_Table_Name).Set() + .Column(name).Equals(value) + .Where(s_CheckpointArgumentsTable_ContextId_Column).Equals(contextId); + + builder.Execute(connection); + return connection.GetChanges() != 0; + } + + SQLite::rowid_t CheckpointArgumentsTable::AddContext(SQLite::Connection& connection, int contextId) + { + SQLite::Builder::StatementBuilder builder; + builder.InsertInto(s_CheckpointArgumentsTable_Table_Name) + .Columns({ s_CheckpointArgumentsTable_ContextId_Column }) + .Values(contextId); + + builder.Execute(connection); + return connection.GetLastInsertRowID(); + } + + void CheckpointArgumentsTable::RemoveContext(SQLite::Connection& connection, int contextId) + { + SQLite::Builder::StatementBuilder builder; + builder.DeleteFrom(s_CheckpointArgumentsTable_Table_Name).Where(s_CheckpointArgumentsTable_ContextId_Column).Equals(contextId); + builder.Execute(connection); + } + + bool CheckpointArgumentsTable::ContainsArgument(SQLite::Connection& connection, int contextId, std::string_view name) + { + SQLite::Builder::StatementBuilder builder; + builder.Select(SQLite::Builder::RowCount).From(s_CheckpointArgumentsTable_Table_Name) + .Where(s_CheckpointArgumentsTable_ContextId_Column).Equals(contextId).And(name).IsNotNull(); + + SQLite::Statement statement = builder.Prepare(connection); + THROW_HR_IF(E_UNEXPECTED, !statement.Step()); + return statement.GetColumn(0) != 0; + } + + std::string CheckpointArgumentsTable::GetStringArgumentByContextId(SQLite::Connection& connection, int contextId, std::string_view name) + { + using namespace Builder; + + StatementBuilder builder; + builder.Select(name).From(s_CheckpointArgumentsTable_Table_Name). + Where(s_CheckpointArgumentsTable_ContextId_Column).Equals(contextId); + + Statement statement = builder.Prepare(connection); + if (statement.Step()) + { + return statement.GetColumn(0); + } + else + { + return {}; + } + } + + bool CheckpointArgumentsTable::GetBoolArgumentByContextId(SQLite::Connection& connection, int contextId, std::string_view name) + { + using namespace Builder; + + StatementBuilder builder; + builder.Select(name).From(s_CheckpointArgumentsTable_Table_Name). + Where(s_CheckpointArgumentsTable_ContextId_Column).Equals(contextId); + + Statement statement = builder.Prepare(connection); + if (statement.Step()) + { + return statement.GetColumn(0); + } + else + { + return {}; + } + } + + // This probably doesn't work... + std::vector CheckpointArgumentsTable::GetAvailableArguments(SQLite::Connection& connection, int contextId) + { + SQLite::Builder::StatementBuilder builder; + builder.Select().From(s_CheckpointArgumentsTable_Table_Name).Where(s_CheckpointArgumentsTable_ContextId_Column).Equals(contextId); + + SQLite::Statement select = builder.Prepare(connection); + + std::vector availableColumnNames; + + if (select.Step()) + { + const std::tuple row = select.GetRow(); + int size = static_cast(std::tuple_size{}); + + // Start at column index 1 to skip 'rowid' + for (int i = 1; i < size; i++) + { + availableColumnNames.emplace_back(select.GetColumnName(i)); + } + + return availableColumnNames; + } + else + { + return {}; + } + + } +} \ No newline at end of file diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointTable.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointArgumentsTable.h similarity index 50% rename from src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointTable.h rename to src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointArgumentsTable.h index 91308aa533..d3dfe3e88a 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointTable.h +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointArgumentsTable.h @@ -8,7 +8,7 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 { - struct CheckpointTable + struct CheckpointArgumentsTable { // Get the table name. static std::string_view TableName(); @@ -28,9 +28,24 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 // Selects the command argument by type from the table, returning the rowid if it exists. static std::optional SelectByArgumentType(const SQLite::Connection& connection, int type); - // Adds the command argument and value to the table. - static SQLite::rowid_t AddCommandArgument(SQLite::Connection& connection, int type, const std::string_view& value); + // Adds a context row. + static SQLite::rowid_t AddContext(SQLite::Connection& connection, int contextId); - static std::vector> GetArguments(SQLite::Connection& connection); + // Removes a context row. + static void RemoveContext(SQLite::Connection& connection, int contextId); + + // Updates an argument for a given context id. + static bool UpdateArgumentByContextId(SQLite::Connection& connection, int contextId, std::string_view name, std::string_view value); + + static bool UpdateArgumentByContextId(SQLite::Connection& connection, int contextId, std::string_view name, bool value); + + static bool ContainsArgument(SQLite::Connection& connection, int contextId, std::string_view name); + + static std::string GetStringArgumentByContextId(SQLite::Connection& connection, int contextId, std::string_view name); + + static bool GetBoolArgumentByContextId(SQLite::Connection& connection, int contextId, std::string_view name); + + // Gets all arguments with an available value. + static std::vector GetAvailableArguments(SQLite::Connection& connection, int contextId); }; } \ No newline at end of file diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointIndexInterface.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointIndexInterface.h index 9e1c82189f..adb7394bb0 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointIndexInterface.h +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointIndexInterface.h @@ -12,8 +12,6 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 void CreateTables(SQLite::Connection& connection) override; - SQLite::rowid_t AddCommandArgument(SQLite::Connection& connection, int type, const std::string_view& argValue) override; - SQLite::rowid_t SetClientVersion(SQLite::Connection& connection, std::string_view clientVersion) override; std::string GetClientVersion(SQLite::Connection& connection) override; @@ -22,7 +20,21 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 std::string GetCommandName(SQLite::Connection& connection) override; - std::vector> GetArguments(SQLite::Connection& connection) override; + SQLite::rowid_t AddContextToArgumentTable(SQLite::Connection& connection, int contextId) override; + + void RemoveContextFromArgumentTable(SQLite::Connection& connection, int contextId) override; + + bool UpdateArgumentByContextId(SQLite::Connection& connection, int contextId, std::string_view name, std::string_view value) override; + + bool UpdateArgumentByContextId(SQLite::Connection& connection, int contextId, std::string_view name, bool value) override; + + std::vector GetAvailableArguments(SQLite::Connection& connection, int contextId) override; + + bool ContainsArgument(SQLite::Connection& connection, int contextId, std::string_view name) override; + + std::string GetStringArgumentByContextId(SQLite::Connection& connection, int contextId, std::string_view name) override; + + bool GetBoolArgumentByContextId(SQLite::Connection& connection, int contextId, std::string_view name) override; private: bool IsEmpty(SQLite::Connection& connection) override; diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointIndexInterface_1_0.cpp b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointIndexInterface_1_0.cpp index c5d059f8b0..5be628e76f 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointIndexInterface_1_0.cpp +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointIndexInterface_1_0.cpp @@ -2,7 +2,7 @@ // Licensed under the MIT License. #include "pch.h" #include "Microsoft/Schema/Checkpoint_1_0/CheckpointIndexInterface.h" -#include "Microsoft/Schema/Checkpoint_1_0/CheckpointTable.h" +#include "Microsoft/Schema/Checkpoint_1_0/CheckpointArgumentsTable.h" #include "Microsoft/Schema/Checkpoint_1_0/CheckpointMetadataTable.h" namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 @@ -11,7 +11,7 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 { std::optional GetExistingCommandArgument(const SQLite::Connection& connection, uint32_t type) { - auto result = CheckpointTable::SelectByArgumentType(connection, type); + auto result = CheckpointArgumentsTable::SelectByArgumentType(connection, type); if (!result) { @@ -30,24 +30,11 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 void CheckpointIndexInterface::CreateTables(SQLite::Connection& connection) { SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "createCheckpointTable_v1_0"); - Checkpoint_V1_0::CheckpointTable::Create(connection); + Checkpoint_V1_0::CheckpointArgumentsTable::Create(connection); Checkpoint_V1_0::CheckpointMetadataTable::Create(connection); savepoint.Commit(); } - SQLite::rowid_t CheckpointIndexInterface::AddCommandArgument(SQLite::Connection& connection, int type, const std::string_view& argValue) - { - auto existingArgument = GetExistingCommandArgument(connection, type); - - THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS), existingArgument.has_value()); - - SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "addCommandArgument_v1_0"); - SQLite::rowid_t argumentId = CheckpointTable::AddCommandArgument(connection, type, argValue); - - savepoint.Commit(); - return argumentId; - } - SQLite::rowid_t CheckpointIndexInterface::SetClientVersion(SQLite::Connection& connection, std::string_view clientVersion) { SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "setClientVersion_v1_0"); @@ -78,11 +65,57 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 bool CheckpointIndexInterface::IsEmpty(SQLite::Connection& connection) { - return CheckpointTable::IsEmpty(connection); + return CheckpointArgumentsTable::IsEmpty(connection); + } + + SQLite::rowid_t CheckpointIndexInterface::AddContextToArgumentTable(SQLite::Connection& connection, int contextId) + { + SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "addContextToArgTable_v1_0"); + SQLite::rowid_t rowId = CheckpointArgumentsTable::AddContext(connection, contextId); + savepoint.Commit(); + return rowId; + } + + void CheckpointIndexInterface::RemoveContextFromArgumentTable(SQLite::Connection& connection, int contextId) + { + SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "removeContextFromArgTable_v1_0"); + CheckpointArgumentsTable::RemoveContext(connection, contextId); + savepoint.Commit(); + } + + bool CheckpointIndexInterface::UpdateArgumentByContextId(SQLite::Connection& connection, int contextId, std::string_view name, std::string_view value) + { + SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "updateContextArgument_v1_0"); + bool status = CheckpointArgumentsTable::UpdateArgumentByContextId(connection, contextId, name, value); + savepoint.Commit(); + return status; + } + + bool CheckpointIndexInterface::UpdateArgumentByContextId(SQLite::Connection& connection, int contextId, std::string_view name, bool value) + { + SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "updateContextArgument_v1_0"); + bool status = CheckpointArgumentsTable::UpdateArgumentByContextId(connection, contextId, name, value); + savepoint.Commit(); + return status; + } + + std::vector CheckpointIndexInterface::GetAvailableArguments(SQLite::Connection& connection, int contextId) + { + return CheckpointArgumentsTable::GetAvailableArguments(connection, contextId); + } + + bool CheckpointIndexInterface::ContainsArgument(SQLite::Connection& connection, int contextId, std::string_view name) + { + return CheckpointArgumentsTable::ContainsArgument(connection, contextId, name); + } + + std::string CheckpointIndexInterface::GetStringArgumentByContextId(SQLite::Connection& connection, int contextId, std::string_view name) + { + return CheckpointArgumentsTable::GetStringArgumentByContextId(connection, contextId, name); } - std::vector> CheckpointIndexInterface::GetArguments(SQLite::Connection& connection) + bool CheckpointIndexInterface::GetBoolArgumentByContextId(SQLite::Connection& connection, int contextId, std::string_view name) { - return CheckpointTable::GetArguments(connection); + return CheckpointArgumentsTable::GetBoolArgumentByContextId(connection, contextId, name); } } \ No newline at end of file diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointMetadataTable.cpp b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointMetadataTable.cpp index 08775f4ff1..1e868124c3 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointMetadataTable.cpp +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointMetadataTable.cpp @@ -8,7 +8,7 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 { using namespace std::string_view_literals; - static constexpr std::string_view s_CheckpointMetadataTable_Table_Name = "CheckpointMetadataTable"sv; + static constexpr std::string_view s_CheckpointMetadataTable_Table_Name = "CheckpointMetadata"sv; static constexpr std::string_view s_CheckpointMetadataTable_Name_Column = "Name"sv; static constexpr std::string_view s_CheckpointMetadataTable_Value_Column = "Value"sv; diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointTable.cpp b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointTable.cpp deleted file mode 100644 index 6a9b782541..0000000000 --- a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointTable.cpp +++ /dev/null @@ -1,114 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. -#include "pch.h" -#include "CheckpointTable.h" -#include "SQLiteStatementBuilder.h" -#include "Microsoft/Schema/ICheckpointIndex.h" - -namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 -{ - using namespace std::string_view_literals; - static constexpr std::string_view s_CheckpointTable_Table_Name = "Checkpoint"sv; - static constexpr std::string_view s_CheckpointTable_ArgumentType_Column = "argumentType"sv; - static constexpr std::string_view s_CheckpointTable_ArgumentValue_Column = "argumentValue"sv; - - std::string_view CheckpointTable::TableName() - { - return s_CheckpointTable_Table_Name; - } - - void CheckpointTable::Create(SQLite::Connection& connection) - { - using namespace SQLite::Builder; - - SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "createCheckpointTable_v1_0"); - - StatementBuilder createTableBuilder; - createTableBuilder.CreateTable(s_CheckpointTable_Table_Name).Columns({ - ColumnBuilder(s_CheckpointTable_ArgumentType_Column, Type::Int64).NotNull(), - ColumnBuilder(s_CheckpointTable_ArgumentValue_Column, Type::Text) - }); - - createTableBuilder.Execute(connection); - - savepoint.Commit(); - } - - bool CheckpointTable::ExistsById(const SQLite::Connection& connection, SQLite::rowid_t id) - { - SQLite::Builder::StatementBuilder builder; - builder.Select(SQLite::Builder::RowCount).From(s_CheckpointTable_Table_Name).Where(SQLite::RowIDName).Equals(id); - - SQLite::Statement countStatement = builder.Prepare(connection); - - THROW_HR_IF(E_UNEXPECTED, !countStatement.Step()); - - return (countStatement.GetColumn(0) != 0); - } - - void CheckpointTable::DeleteById(SQLite::Connection& connection, SQLite::rowid_t id) - { - SQLite::Builder::StatementBuilder builder; - builder.DeleteFrom(s_CheckpointTable_Table_Name).Where(SQLite::RowIDName).Equals(id); - - builder.Execute(connection); - } - - bool CheckpointTable::IsEmpty(SQLite::Connection& connection) - { - SQLite::Builder::StatementBuilder builder; - builder.Select(SQLite::Builder::RowCount).From(s_CheckpointTable_Table_Name); - - SQLite::Statement countStatement = builder.Prepare(connection); - - THROW_HR_IF(E_UNEXPECTED, !countStatement.Step()); - - return (countStatement.GetColumn(0) == 0); - } - - std::optional CheckpointTable::SelectByArgumentType(const SQLite::Connection& connection, int type) - { - SQLite::Builder::StatementBuilder builder; - builder.Select(SQLite::RowIDName).From(s_CheckpointTable_Table_Name).Where(s_CheckpointTable_ArgumentType_Column); - builder.Equals(type); - - SQLite::Statement select = builder.Prepare(connection); - - if (select.Step()) - { - return select.GetColumn(0); - } - else - { - return {}; - } - } - - SQLite::rowid_t CheckpointTable::AddCommandArgument(SQLite::Connection& connection, int type, const std::string_view& value) - { - SQLite::Builder::StatementBuilder builder; - builder.InsertInto(s_CheckpointTable_Table_Name) - .Columns({ s_CheckpointTable_ArgumentType_Column, s_CheckpointTable_ArgumentValue_Column }) - .Values(type, value); - - builder.Execute(connection); - return connection.GetLastInsertRowID(); - } - - std::vector> CheckpointTable::GetArguments(SQLite::Connection& connection) - { - SQLite::Builder::StatementBuilder builder; - builder.Select({ s_CheckpointTable_ArgumentType_Column, s_CheckpointTable_ArgumentValue_Column}) - .From(s_CheckpointTable_Table_Name); - - SQLite::Statement select = builder.Prepare(connection); - std::vector> result; - while (select.Step()) - { - auto [argumentType, argumentValue] = select.GetRow(); - result.emplace_back(std::pair{argumentType, argumentValue}); - } - - return result; - } -} \ No newline at end of file diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/ICheckpointIndex.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/ICheckpointIndex.h index f265c07bda..27298a8ce6 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/Schema/ICheckpointIndex.h +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/ICheckpointIndex.h @@ -17,12 +17,6 @@ namespace AppInstaller::Repository::Microsoft::Schema virtual void CreateTables(SQLite::Connection& connection) = 0; // Version 1.0 - // Adds an argument to the index. - virtual SQLite::rowid_t AddCommandArgument(SQLite::Connection& connection, int argumentType, const std::string_view& argumentValue) = 0; - - // Removes an argument from the index. - //virtual SQLite::rowid_t RemoveCommandArgument(SQLite::Connection& connection, int argumentType, const std::string_view& argumentValue); - virtual SQLite::rowid_t SetClientVersion(SQLite::Connection& connection, std::string_view clientVersion) = 0; virtual std::string GetClientVersion(SQLite::Connection& connection) = 0; @@ -31,9 +25,22 @@ namespace AppInstaller::Repository::Microsoft::Schema virtual std::string GetCommandName(SQLite::Connection& connection) = 0; - virtual std::vector> GetArguments(SQLite::Connection& connection) = 0; - - // Returns a bool value indicating whether the index is empty. virtual bool IsEmpty(SQLite::Connection& connection) = 0; + + virtual SQLite::rowid_t AddContextToArgumentTable(SQLite::Connection& connection, int contextId) = 0; + + virtual void RemoveContextFromArgumentTable(SQLite::Connection& connection, int contextId) = 0; + + virtual bool UpdateArgumentByContextId(SQLite::Connection& connection, int contextId, std::string_view name, std::string_view value) = 0; + + virtual bool UpdateArgumentByContextId(SQLite::Connection& connection, int contextId, std::string_view name, bool value) = 0; + + virtual std::vector GetAvailableArguments(SQLite::Connection& connection, int contextId) = 0; + + virtual bool ContainsArgument(SQLite::Connection& connection, int contextId, std::string_view name) = 0; + + virtual std::string GetStringArgumentByContextId(SQLite::Connection& connection, int contextId, std::string_view name) = 0; + + virtual bool GetBoolArgumentByContextId(SQLite::Connection& connection, int contextId, std::string_view name) = 0; }; } \ No newline at end of file diff --git a/src/AppInstallerRepositoryCore/SQLiteWrapper.cpp b/src/AppInstallerRepositoryCore/SQLiteWrapper.cpp index 38b88d6982..a878fd329a 100644 --- a/src/AppInstallerRepositoryCore/SQLiteWrapper.cpp +++ b/src/AppInstallerRepositoryCore/SQLiteWrapper.cpp @@ -294,6 +294,13 @@ namespace AppInstaller::Repository::SQLite return type == SQLITE_NULL; } + std::string Statement::GetColumnName(int column) + { + THROW_HR_IF(E_BOUNDS, m_state != State::HasRow); + const char* columnName = sqlite3_column_name(m_stmt.get(), column); + return std::string{ columnName }; + } + void Statement::Reset() { AICLI_LOG(SQL, Verbose, << "Reset statement #" << m_connectionId << '-' << m_id); diff --git a/src/AppInstallerRepositoryCore/SQLiteWrapper.h b/src/AppInstallerRepositoryCore/SQLiteWrapper.h index 3a5913ce95..bec857604b 100644 --- a/src/AppInstallerRepositoryCore/SQLiteWrapper.h +++ b/src/AppInstallerRepositoryCore/SQLiteWrapper.h @@ -243,6 +243,9 @@ namespace AppInstaller::Repository::SQLite // The index is 0 based. bool GetColumnIsNull(int column); + // Gets the name of the specified column from the current row. + std::string GetColumnName(int column); + // Gets the value of the specified column from the current row. // The index is 0 based. template diff --git a/src/AppInstallerSharedLib/AppInstallerStrings.cpp b/src/AppInstallerSharedLib/AppInstallerStrings.cpp index c505da1fea..033a762086 100644 --- a/src/AppInstallerSharedLib/AppInstallerStrings.cpp +++ b/src/AppInstallerSharedLib/AppInstallerStrings.cpp @@ -855,4 +855,23 @@ namespace AppInstaller::Utility result.push_back(input.substr(startIndex)); return result; } + + GUID ConvertToGuid(const std::string& value) + { + std::string guidString = value; + + if (value.front() != '{') + { + guidString.insert(0, 1, '{'); + } + + if (value.back() != '}') + { + guidString.push_back('}'); + } + + GUID result; + THROW_IF_FAILED_MSG(CLSIDFromString(ConvertToUTF16(guidString).c_str(), &result), "ConvertToGuid: Invalid guid string"); + return result; + } } diff --git a/src/AppInstallerSharedLib/Public/AppInstallerStrings.h b/src/AppInstallerSharedLib/Public/AppInstallerStrings.h index 5ca4c72938..671bca37c0 100644 --- a/src/AppInstallerSharedLib/Public/AppInstallerStrings.h +++ b/src/AppInstallerSharedLib/Public/AppInstallerStrings.h @@ -262,4 +262,6 @@ namespace AppInstaller::Utility (FindAndReplace(inputStr, "{" + std::to_string(index++) + "}", (std::ostringstream() << args).str()),...); return inputStr; } + + GUID ConvertToGuid(const std::string& value); } From cbc34dbf8456a89cedf081f263546f18d55afa3c Mon Sep 17 00:00:00 2001 From: Ryan Fu Date: Mon, 31 Jul 2023 16:28:19 -0700 Subject: [PATCH 06/43] save work --- src/AppInstallerCLICore/Argument.cpp | 4 +- src/AppInstallerCLICore/CheckpointManager.cpp | 85 ++++++++++++--- src/AppInstallerCLICore/CheckpointManager.h | 27 +++-- .../Commands/InstallCommand.cpp | 16 ++- .../Commands/ResumeCommand.cpp | 17 +-- src/AppInstallerCLICore/ExecutionContext.cpp | 13 +++ src/AppInstallerCLICore/ExecutionContext.h | 15 ++- .../Workflows/WorkflowBase.cpp | 10 +- .../AppInstallerRepositoryCore.vcxproj | 6 +- ...AppInstallerRepositoryCore.vcxproj.filters | 16 ++- .../Microsoft/CheckpointIndex.cpp | 37 ++++--- .../Microsoft/CheckpointIndex.h | 12 ++- .../CheckpointArgumentsTable.cpp | 92 ++++++++++------ .../Checkpoint_1_0/CheckpointArgumentsTable.h | 6 ++ .../Checkpoint_1_0/CheckpointContextTable.cpp | 102 ++++++++++++++++++ .../Checkpoint_1_0/CheckpointContextTable.h | 37 +++++++ .../Checkpoint_1_0/CheckpointIndexInterface.h | 10 +- .../CheckpointIndexInterface_1_0.cpp | 30 +++++- .../CheckpointMetadataTable.cpp | 11 -- .../Checkpoint_1_0/CheckpointMetadataTable.h | 6 -- .../Microsoft/Schema/ICheckpointIndex.h | 10 +- 21 files changed, 439 insertions(+), 123 deletions(-) create mode 100644 src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointContextTable.cpp create mode 100644 src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointContextTable.h diff --git a/src/AppInstallerCLICore/Argument.cpp b/src/AppInstallerCLICore/Argument.cpp index fc609a9e5f..562fdb7c26 100644 --- a/src/AppInstallerCLICore/Argument.cpp +++ b/src/AppInstallerCLICore/Argument.cpp @@ -385,9 +385,9 @@ namespace AppInstaller::CLI case Args::Type::Locale: return "Locale"sv; case Args::Type::InstallArchitecture: - return "InstallArchitecture"sv; + return "Architecture"sv; case Args::Type::InstallScope: - return "InstallScope"sv; + return "Scope"sv; default: THROW_HR(E_UNEXPECTED); } diff --git a/src/AppInstallerCLICore/CheckpointManager.cpp b/src/AppInstallerCLICore/CheckpointManager.cpp index 42ba321fd4..a298b095f7 100644 --- a/src/AppInstallerCLICore/CheckpointManager.cpp +++ b/src/AppInstallerCLICore/CheckpointManager.cpp @@ -10,10 +10,29 @@ namespace AppInstaller::CLI::Checkpoint { + bool CheckpointManager::CleanUpIndex() + { + // Check if index is empty, if so, remove file and return status. + if (m_checkpointIndex->IsEmpty()) + { + // Remove file here if it is empty. + } + + return true; + } + + CheckpointManager::~CheckpointManager() + { + CleanUpIndex(); + } + void CheckpointManager::Initialize() { // Initialize should not be called more than once. - THROW_HR_IF(E_UNEXPECTED, m_checkpointId != GUID_NULL); + if (m_checkpointId != GUID_NULL) + { + return; + } std::ignore = CoCreateGuid(&m_checkpointId); AICLI_LOG(CLI, Info, << "Creating checkpoint index with the corresponding guid: " << m_checkpointId); @@ -28,13 +47,16 @@ namespace AppInstaller::CLI::Checkpoint m_checkpointIndex = std::move(checkpointIndex); m_checkpointIndex->SetClientVersion(AppInstaller::Runtime::GetClientVersion()); - //m_checkpointIndex->SetCommandName(commandName); } void CheckpointManager::InitializeFromGuid(GUID checkpointId) { // Initialize should not be called more than once. - THROW_HR_IF(E_UNEXPECTED, m_checkpointId != GUID_NULL); + if (m_checkpointId != GUID_NULL) + { + return; + } + // THROW_HR_IF(E_UNEXPECTED, m_checkpointId != GUID_NULL); m_checkpointId = checkpointId; auto openDisposition = AppInstaller::Repository::Microsoft::SQLiteStorageBase::OpenDisposition::ReadWrite; @@ -48,14 +70,21 @@ namespace AppInstaller::CLI::Checkpoint m_checkpointIndex = std::move(checkpointIndex); } + void CheckpointManager::AddContext(int contextId) + { + m_checkpointIndex->AddContext(contextId); + } + + void CheckpointManager::RemoveContext(int contextId) + { + m_checkpointIndex->RemoveContext(contextId); + } + std::unique_ptr CheckpointManager::CreateContextFromCheckpointIndex() { auto checkpointContext = std::make_unique(std::cout, std::cin); auto previousthreadGlobals = checkpointContext->SetForCurrentThread(); - checkpointContext->EnableSignalTerminationHandler(); - - PopulateContextArgsFromIndex(*checkpointContext); return checkpointContext; } @@ -92,12 +121,11 @@ namespace AppInstaller::CLI::Checkpoint { int contextId = context.GetContextId(); - // Figure out if there is a better place to add the context when it is first initialized. - m_checkpointIndex->AddContextToArgumentTable(contextId); - const auto& executingCommand = context.GetExecutingCommand(); if (executingCommand != nullptr) { + m_checkpointIndex->SetCommandName(contextId, executingCommand->Name()); + const auto& commandArguments = executingCommand->GetArguments(); for (const auto& argument : commandArguments) { @@ -118,28 +146,54 @@ namespace AppInstaller::CLI::Checkpoint } } + void CheckpointManager::Checkpoint(Execution::Context& context, Execution::CheckpointFlags targetCheckpointFlag) + { + Execution::CheckpointFlags currentCheckpointFlag = context.GetCurrentCheckpoint(); + + // If the current checkpoint is behind the target checkpoint, load the checkpoint state from the index. + // If the current checkpoint is ahead of the target checkpoint, save the checkpoint state to the index. + // If the states are equal, do nothing. + if (currentCheckpointFlag > targetCheckpointFlag) + { + // If the current checkpoint is ahead of the target, this means we have already previously passed this state, load. + LoadCheckpoint(context, targetCheckpointFlag); + } + else if (currentCheckpointFlag < targetCheckpointFlag) + { + // If the current checkpoint is behind the target checkpoint, we are still working our way up to the target, + // save the state. + SaveCheckpoint(context, targetCheckpointFlag); + } + + // If the current checkpoint is equal, do nothing as it has already been performed. + } + void CheckpointManager::SaveCheckpoint(Execution::Context& context, Execution::CheckpointFlags flag) { switch (flag) { - case Execution::CheckpointFlags::ArgumentsProcessed: + case Execution::CheckpointFlags::CommandArguments: RecordContextArgsToIndex(context); break; default: THROW_HR(E_UNEXPECTED); } + + context.SetCurrentCheckpoint(flag); } void CheckpointManager::LoadCheckpoint(Execution::Context& context, Execution::CheckpointFlags flag) { switch (flag) { - case Execution::CheckpointFlags::ArgumentsProcessed: + case Execution::CheckpointFlags::CommandArguments: PopulateContextArgsFromIndex(context); break; default: THROW_HR(E_UNEXPECTED); } + + context.SetCurrentCheckpoint(flag); } std::string CheckpointManager::GetClientVersion() @@ -148,8 +202,13 @@ namespace AppInstaller::CLI::Checkpoint return m_checkpointIndex->GetClientVersion(); } - std::string CheckpointManager::GetCommandName() + std::string CheckpointManager::GetCommandName(int contextId) + { + return m_checkpointIndex->GetCommandName(contextId); + } + + int CheckpointManager::GetFirstContextId() { - return m_checkpointIndex->GetCommandName(); + return m_checkpointIndex->GetFirstContextId(); } } diff --git a/src/AppInstallerCLICore/CheckpointManager.h b/src/AppInstallerCLICore/CheckpointManager.h index 1a0142698d..5d8d5ebf74 100644 --- a/src/AppInstallerCLICore/CheckpointManager.h +++ b/src/AppInstallerCLICore/CheckpointManager.h @@ -14,33 +14,46 @@ namespace AppInstaller::CLI::Checkpoint { struct CheckpointManager { + CheckpointManager(const CheckpointManager&) = delete; + CheckpointManager& operator=(const CheckpointManager&) = delete; + CheckpointManager(CheckpointManager&&) = delete; + CheckpointManager& operator=(CheckpointManager&&) = delete; + static CheckpointManager& Instance() { - static CheckpointManager s_Instance; - return s_Instance; + static CheckpointManager checkpointManager; + return checkpointManager; } - void Initialize(); // checks if guid is already defined, this means that it has already been loaded. + void Initialize(); void InitializeFromGuid(GUID checkpointId); - void RemoveContext(Execution::Context& context); + void AddContext(int contextId); - void SaveCheckpoint(Execution::Context& context, Execution::CheckpointFlags flag); + void RemoveContext(int contextId); - void LoadCheckpoint(Execution::Context& context, Execution::CheckpointFlags flag); + void Checkpoint(Execution::Context& context, Execution::CheckpointFlags flag); std::unique_ptr CreateContextFromCheckpointIndex(); std::string GetClientVersion(); - std::string GetCommandName(); + std::string GetCommandName(int contextId); + + int GetFirstContextId(); private: CheckpointManager() = default; + ~CheckpointManager(); GUID m_checkpointId = {}; std::shared_ptr m_checkpointIndex = nullptr; + bool CleanUpIndex(); + + void SaveCheckpoint(Execution::Context& context, Execution::CheckpointFlags flag); + void LoadCheckpoint(Execution::Context& context, Execution::CheckpointFlags flag); + void PopulateContextArgsFromIndex(Execution::Context& context); void RecordContextArgsToIndex(Execution::Context& context); }; diff --git a/src/AppInstallerCLICore/Commands/InstallCommand.cpp b/src/AppInstallerCLICore/Commands/InstallCommand.cpp index 618cf42f96..e477499f1f 100644 --- a/src/AppInstallerCLICore/Commands/InstallCommand.cpp +++ b/src/AppInstallerCLICore/Commands/InstallCommand.cpp @@ -107,11 +107,11 @@ namespace AppInstaller::CLI { if (Settings::ExperimentalFeature::IsEnabled(Settings::ExperimentalFeature::Feature::Resume)) { - CheckpointManager checkpointManager = CheckpointManager::Instance(); - checkpointManager.Initialize(); - checkpointManager.SaveCheckpoint(context, Execution::CheckpointFlags::ArgumentsProcessed); + CheckpointManager::Instance().Initialize(); + CheckpointManager::Instance().AddContext(context.GetContextId()); + CheckpointManager::Instance().Checkpoint(context, Execution::CheckpointFlags::CommandArguments); } - + context.SetFlags(ContextFlag::ShowSearchResultsOnPartialFailure); if (context.Args.Contains(Execution::Args::Type::Manifest)) @@ -150,5 +150,13 @@ namespace AppInstaller::CLI context << Workflow::InstallOrUpgradeSinglePackage(OperationType::Install); } } + + if (Settings::ExperimentalFeature::IsEnabled(Settings::ExperimentalFeature::Feature::Resume)) + { + if (context.IsTerminated()) + { + CheckpointManager::Instance().RemoveContext(context.GetContextId()); + } + } } } diff --git a/src/AppInstallerCLICore/Commands/ResumeCommand.cpp b/src/AppInstallerCLICore/Commands/ResumeCommand.cpp index fd01933af5..deb19bf25e 100644 --- a/src/AppInstallerCLICore/Commands/ResumeCommand.cpp +++ b/src/AppInstallerCLICore/Commands/ResumeCommand.cpp @@ -39,15 +39,15 @@ namespace AppInstaller::CLI { GUID checkpointId = Utility::ConvertToGuid(std::string{ context.Args.GetArg(Execution::Args::Type::ResumeGuid) }); - CheckpointManager checkpointManager = CheckpointManager::Instance(); - checkpointManager.InitializeFromGuid(checkpointId); + CheckpointManager::Instance().InitializeFromGuid(checkpointId); - if (AppInstaller::Runtime::GetClientVersion().get() != checkpointManager.GetClientVersion()) + if (AppInstaller::Runtime::GetClientVersion().get() != CheckpointManager::Instance().GetClientVersion()) { AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_CLIENTVERSION_MISMATCH); } - std::string commandName = checkpointManager.GetCommandName(); + int rootContextId = CheckpointManager::Instance().GetFirstContextId(); + std::string commandName = CheckpointManager::Instance().GetCommandName(rootContextId); std::unique_ptr commandToResume; // Use the root command to obtain all of the available commands. @@ -68,9 +68,10 @@ namespace AppInstaller::CLI } // Create a new context and load from checkpoint - auto checkpointContext = checkpointManager.CreateContextFromCheckpointIndex(); - checkpointContext->SetExecutingCommand(commandToResume.get()); - checkpointManager.LoadCheckpoint(*checkpointContext, Execution::CheckpointFlags::ArgumentsProcessed); - commandToResume->Execute(*checkpointContext); + auto resumeContext = context.CreateEmptyContext(rootContextId); + resumeContext->SetExecutingCommand(commandToResume.get()); + + CheckpointManager::Instance().Checkpoint(*resumeContext, Execution::CheckpointFlags::CommandArguments); + commandToResume->Execute(*resumeContext); } } diff --git a/src/AppInstallerCLICore/ExecutionContext.cpp b/src/AppInstallerCLICore/ExecutionContext.cpp index b96c1304ce..1aedfc4c1c 100644 --- a/src/AppInstallerCLICore/ExecutionContext.cpp +++ b/src/AppInstallerCLICore/ExecutionContext.cpp @@ -272,6 +272,19 @@ namespace AppInstaller::CLI::Execution return clone; } + std::unique_ptr Context::CreateEmptyContext(int contextId) + { + auto emptyContext = std::make_unique(Reporter, m_threadGlobals); + // If the parent is hooked up to the CTRL signal, have the clone be as well + if (m_disableSignalTerminationHandlerOnExit) + { + emptyContext->EnableSignalTerminationHandler(); + } + + emptyContext->SetContextId(contextId); + return emptyContext; + } + void Context::CopyArgsToSubContext(Context* subContext) { auto argProperties = ArgumentCommon::GetFromExecArgs(Args); diff --git a/src/AppInstallerCLICore/ExecutionContext.h b/src/AppInstallerCLICore/ExecutionContext.h index 6f01f28744..69b0fd2db4 100644 --- a/src/AppInstallerCLICore/ExecutionContext.h +++ b/src/AppInstallerCLICore/ExecutionContext.h @@ -55,7 +55,7 @@ namespace AppInstaller::CLI::Execution enum class CheckpointFlags : uint32_t { None, - ArgumentsProcessed, + CommandArguments, }; // bit masks used as Context flags @@ -104,6 +104,9 @@ namespace AppInstaller::CLI::Execution // Creates a child of this context. virtual std::unique_ptr CreateSubContext(); + // Creates an empty context. + virtual std::unique_ptr CreateEmptyContext(int contextId = 0); + // Enables reception of CTRL signals and window messages. void EnableSignalTerminationHandler(bool enabled = true); @@ -174,6 +177,13 @@ namespace AppInstaller::CLI::Execution // Gets the target checkpoint of the context. CheckpointFlags GetTargetCheckpoint() { return m_targetCheckpoint; }; + // Sets the current checkpoint flag of the context. + void SetCurrentCheckpoint(Execution::CheckpointFlags flag) + { + m_currentCheckpoint = flag; + } + + // Gets the id of the context. int GetContextId() { return m_contextId; }; protected: @@ -185,6 +195,9 @@ namespace AppInstaller::CLI::Execution // use this if AICLI_DISABLE_TEST_HOOKS is defined. std::function m_shouldExecuteWorkflowTask; + // Sets the id of the context. + void SetContextId(int contextId) { m_contextId = contextId; }; + private: DestructionToken m_disableSignalTerminationHandlerOnExit = false; bool m_isTerminated = false; diff --git a/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp b/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp index 13a29a1eba..cf7619e93c 100644 --- a/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp +++ b/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp @@ -1275,11 +1275,11 @@ AppInstaller::CLI::Execution::Context& operator<<(AppInstaller::CLI::Execution:: AppInstaller::CLI::Execution::Context& operator<<(AppInstaller::CLI::Execution::Context& context, const AppInstaller::CLI::Workflow::WorkflowTask& task) { - // Maybe add a should we execute workflow check using checkpoint. - if (context.GetCurrentCheckpoint() != context.GetTargetCheckpoint()) - { - return context; - } + //// Maybe add a should we execute workflow check using checkpoint. + //if (context.GetCurrentCheckpoint() != context.GetTargetCheckpoint()) + //{ + // return context; + //} if (!context.IsTerminated()) { diff --git a/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj b/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj index 86faf541e3..0b0eade299 100644 --- a/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj +++ b/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj @@ -395,9 +395,10 @@ + + - @@ -497,9 +498,10 @@ + + - diff --git a/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj.filters b/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj.filters index 0dc765edbc..7d33d0d32a 100644 --- a/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj.filters +++ b/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj.filters @@ -372,10 +372,13 @@ Microsoft - + Microsoft\Schema\Checkpoint_1_0 - + + Microsoft\Schema\Checkpoint_1_0 + + Microsoft\Schema\Checkpoint_1_0 @@ -599,12 +602,15 @@ Source Files - - Source Files - Microsoft\Schema\Checkpoint_1_0 + + Microsoft\Schema\Checkpoint_1_0 + + + Source Files + Microsoft\Schema\Checkpoint_1_0 diff --git a/src/AppInstallerRepositoryCore/Microsoft/CheckpointIndex.cpp b/src/AppInstallerRepositoryCore/Microsoft/CheckpointIndex.cpp index b4a689d3a6..2682d3fb98 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/CheckpointIndex.cpp +++ b/src/AppInstallerRepositoryCore/Microsoft/CheckpointIndex.cpp @@ -63,6 +63,11 @@ namespace AppInstaller::Repository::Microsoft return {}; } + bool CheckpointIndex::IsEmpty() + { + return m_interface->IsEmpty(m_dbconn); + } + CheckpointIndex::IdType CheckpointIndex::SetClientVersion(std::string_view clientVersion) { std::lock_guard lockInterface{ *m_interfaceLock }; @@ -84,14 +89,14 @@ namespace AppInstaller::Repository::Microsoft return m_interface->GetClientVersion(m_dbconn); } - CheckpointIndex::IdType CheckpointIndex::SetCommandName(std::string_view commandName) + CheckpointIndex::IdType CheckpointIndex::SetCommandName(int contextId, std::string_view commandName) { std::lock_guard lockInterface{ *m_interfaceLock }; AICLI_LOG(Repo, Verbose, << "Setting command name [" << commandName << "]"); SQLite::Savepoint savepoint = SQLite::Savepoint::Create(m_dbconn, "checkpointindex_setcommandname"); - IdType result = m_interface->SetCommandName(m_dbconn, commandName); + IdType result = m_interface->SetCommandName(m_dbconn, contextId, commandName); SetLastWriteTime(); @@ -100,35 +105,34 @@ namespace AppInstaller::Repository::Microsoft return result; } - std::string CheckpointIndex::GetCommandName() + std::string CheckpointIndex::GetCommandName(int contextId) { - return m_interface->GetCommandName(m_dbconn); + return m_interface->GetCommandName(m_dbconn, contextId); } - CheckpointIndex::IdType CheckpointIndex::AddContextToArgumentTable(int contextId) + void CheckpointIndex::AddContext(int contextId) { std::lock_guard lockInterface{ *m_interfaceLock }; - AICLI_LOG(Repo, Verbose, << "Adding context [" << contextId << "] to arguments table"); + AICLI_LOG(Repo, Verbose, << "Adding context [" << contextId << "] to checkpoint index"); - SQLite::Savepoint savepoint = SQLite::Savepoint::Create(m_dbconn, "checkpointindex_addcontexttoargumentstable"); + SQLite::Savepoint savepoint = SQLite::Savepoint::Create(m_dbconn, "checkpointindex_addcontext"); - IdType result = m_interface->AddContextToArgumentTable(m_dbconn, contextId); + m_interface->AddContextToArgumentTable(m_dbconn, contextId); + m_interface->AddContextToContextTable(m_dbconn, contextId); SetLastWriteTime(); - savepoint.Commit(); - - return result; } - void CheckpointIndex::RemoveContextFromArgumentTable(int contextId) + void CheckpointIndex::RemoveContext(int contextId) { std::lock_guard lockInterface{ *m_interfaceLock }; - AICLI_LOG(Repo, Verbose, << "Removing context [" << contextId << "] to arguments table"); + AICLI_LOG(Repo, Verbose, << "Removing context [" << contextId << "] from checkpoint index"); - SQLite::Savepoint savepoint = SQLite::Savepoint::Create(m_dbconn, "checkpointindex_removecontextfromargumentstable"); + SQLite::Savepoint savepoint = SQLite::Savepoint::Create(m_dbconn, "checkpointindex_removecontext"); m_interface->RemoveContextFromArgumentTable(m_dbconn, contextId); + m_interface->RemoveContextFromContextTable(m_dbconn, contextId); SetLastWriteTime(); @@ -187,6 +191,11 @@ namespace AppInstaller::Repository::Microsoft return m_interface->GetBoolArgumentByContextId(m_dbconn, contextId, name); } + int CheckpointIndex::GetFirstContextId() + { + return m_interface->GetFirstContextId(m_dbconn); + } + std::unique_ptr CheckpointIndex::CreateICheckpointIndex() const { if (m_version == Schema::Version{ 1, 0 } || diff --git a/src/AppInstallerRepositoryCore/Microsoft/CheckpointIndex.h b/src/AppInstallerRepositoryCore/Microsoft/CheckpointIndex.h index 8c0feeda38..a54373263b 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/CheckpointIndex.h +++ b/src/AppInstallerRepositoryCore/Microsoft/CheckpointIndex.h @@ -36,13 +36,13 @@ namespace AppInstaller::Repository::Microsoft std::string GetClientVersion(); - IdType SetCommandName(std::string_view commandName); + IdType SetCommandName(int contextId, std::string_view commandName); - std::string GetCommandName(); + std::string GetCommandName(int contextId); - IdType AddContextToArgumentTable(int contextId); + void AddContext(int contextId); - void RemoveContextFromArgumentTable(int contextId); + void RemoveContext(int contextId); bool UpdateArgumentByContextId(int contextId, std::string_view name, std::string_view value); @@ -56,6 +56,10 @@ namespace AppInstaller::Repository::Microsoft bool GetBoolArgumentByContextId(int contextId, std::string_view name); + int GetFirstContextId(); + + bool IsEmpty(); + private: // Constructor used to open an existing index. CheckpointIndex(const std::string& target, SQLiteStorageBase::OpenDisposition disposition, Utility::ManagedFile&& indexFile); diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointArgumentsTable.cpp b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointArgumentsTable.cpp index bf5fd99ab7..dfa960ef70 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointArgumentsTable.cpp +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointArgumentsTable.cpp @@ -10,36 +10,38 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 using namespace SQLite; using namespace std::string_view_literals; static constexpr std::string_view s_CheckpointArgumentsTable_Table_Name = "CheckpointArguments"sv; - static constexpr std::string_view s_CheckpointArgumentsTable_ContextId_Column = "ContextId"sv; - static constexpr std::string_view s_CheckpointArgumentsTable_Query_Column = "Query"sv; - static constexpr std::string_view s_CheckpointArgumentsTable_MultiQuery_Column = "MultiQuery"sv; - static constexpr std::string_view s_CheckpointArgumentsTable_Manifest_Column = "Manifest"sv; - static constexpr std::string_view s_CheckpointArgumentsTable_Id_Column = "Id"sv; - static constexpr std::string_view s_CheckpointArgumentsTable_Moniker_Column = "Moniker"sv; - static constexpr std::string_view s_CheckpointArgumentsTable_Version_Column = "Version"sv; - static constexpr std::string_view s_CheckpointArgumentsTable_Channel_Column = "Channel"sv; - static constexpr std::string_view s_CheckpointArgumentsTable_Source_Column = "Source"sv; - static constexpr std::string_view s_CheckpointArgumentsTable_InstallScope_Column = "InstallScope"sv; - static constexpr std::string_view s_CheckpointArgumentsTable_InstallArchitecture_Column = "InstallArchitecture"sv; - static constexpr std::string_view s_CheckpointArgumentsTable_Exact_Column = "Exact"sv; - static constexpr std::string_view s_CheckpointArgumentsTable_Interactive_Column = "Interactive"sv; - static constexpr std::string_view s_CheckpointArgumentsTable_Silent_Column = "Silent"sv; - static constexpr std::string_view s_CheckpointArgumentsTable_Locale_Column = "Locale"sv; - static constexpr std::string_view s_CheckpointArgumentsTable_Log_Column = "Log"sv; - static constexpr std::string_view s_CheckpointArgumentsTable_CustomSwitches_Column = "CustomSwitches"sv; - static constexpr std::string_view s_CheckpointArgumentsTable_Override_Column = "Override"sv; - static constexpr std::string_view s_CheckpointArgumentsTable_InstallLocation_Column = "InstallLocation"sv; - static constexpr std::string_view s_CheckpointArgumentsTable_HashOverride_Column = "HashOverride"sv; - static constexpr std::string_view s_CheckpointArgumentsTable_SkipDependencies_Column = "SkipDependencies"sv; - static constexpr std::string_view s_CheckpointArgumentsTable_IgnoreLocalArchiveMalwareScan_Column = "IgnoreLocalArchiveMalwareScan"sv; - static constexpr std::string_view s_CheckpointArgumentsTable_DependencySource_Column = "DependencySource"sv; - static constexpr std::string_view s_CheckpointArgumentsTable_AcceptPackageAgreements_Column = "AcceptPackageAgreements"sv; - static constexpr std::string_view s_CheckpointArgumentsTable_NoUpgrade_Column = "NoUpgrade"sv; - static constexpr std::string_view s_CheckpointArgumentsTable_CustomHeader_Column = "CustomHeader"sv; - static constexpr std::string_view s_CheckpointArgumentsTable_AcceptSourceAgreements_Column = "AcceptSourceAgreements"sv; - static constexpr std::string_view s_CheckpointArgumentsTable_Rename_Column = "Rename"sv; - static constexpr std::string_view s_CheckpointArgumentsTable_UninstallPrevious_Column = "UninstallPrevious"sv; - static constexpr std::string_view s_CheckpointArgumentsTable_Force_Column = "Force"sv; + static constexpr std::string_view s_CheckpointArgumentsTable_ContextId_Column = "contextId"sv; + static constexpr std::string_view s_CheckpointArgumentsTable_CommandName_Column = "commandName"sv; + static constexpr std::string_view s_CheckpointArgumentsTable_Query_Column = "query"sv; + static constexpr std::string_view s_CheckpointArgumentsTable_MultiQuery_Column = "multiQuery"sv; + static constexpr std::string_view s_CheckpointArgumentsTable_Manifest_Column = "manifest"sv; + static constexpr std::string_view s_CheckpointArgumentsTable_Id_Column = "id"sv; + static constexpr std::string_view s_CheckpointArgumentsTable_Name_Column = "name"sv; + static constexpr std::string_view s_CheckpointArgumentsTable_Moniker_Column = "moniker"sv; + static constexpr std::string_view s_CheckpointArgumentsTable_Version_Column = "version"sv; + static constexpr std::string_view s_CheckpointArgumentsTable_Channel_Column = "channel"sv; + static constexpr std::string_view s_CheckpointArgumentsTable_Source_Column = "source"sv; + static constexpr std::string_view s_CheckpointArgumentsTable_InstallScope_Column = "scope"sv; + static constexpr std::string_view s_CheckpointArgumentsTable_InstallArchitecture_Column = "architecture"sv; + static constexpr std::string_view s_CheckpointArgumentsTable_Exact_Column = "exact"sv; + static constexpr std::string_view s_CheckpointArgumentsTable_Interactive_Column = "interactive"sv; + static constexpr std::string_view s_CheckpointArgumentsTable_Silent_Column = "silent"sv; + static constexpr std::string_view s_CheckpointArgumentsTable_Locale_Column = "locale"sv; + static constexpr std::string_view s_CheckpointArgumentsTable_Log_Column = "log"sv; + static constexpr std::string_view s_CheckpointArgumentsTable_Custom_Column = "custom"sv; + static constexpr std::string_view s_CheckpointArgumentsTable_Override_Column = "override"sv; + static constexpr std::string_view s_CheckpointArgumentsTable_InstallLocation_Column = "location"sv; + static constexpr std::string_view s_CheckpointArgumentsTable_HashOverride_Column = "ignore-security-hash"sv; + static constexpr std::string_view s_CheckpointArgumentsTable_SkipDependencies_Column = "skip-dependencies"sv; + static constexpr std::string_view s_CheckpointArgumentsTable_IgnoreLocalArchiveMalwareScan_Column = "ignore-local-archive-malware-scan"sv; + static constexpr std::string_view s_CheckpointArgumentsTable_DependencySource_Column = "dependency-source"sv; + static constexpr std::string_view s_CheckpointArgumentsTable_AcceptPackageAgreements_Column = "accept-package-agreements"sv; + static constexpr std::string_view s_CheckpointArgumentsTable_NoUpgrade_Column = "no-upgrade"sv; + static constexpr std::string_view s_CheckpointArgumentsTable_CustomHeader_Column = "header"sv; + static constexpr std::string_view s_CheckpointArgumentsTable_AcceptSourceAgreements_Column = "accept-source-agreements"sv; + static constexpr std::string_view s_CheckpointArgumentsTable_Rename_Column = "rename"sv; + static constexpr std::string_view s_CheckpointArgumentsTable_UninstallPrevious_Column = "uninstall-previous"sv; + static constexpr std::string_view s_CheckpointArgumentsTable_Force_Column = "force"sv; std::string_view CheckpointArgumentsTable::TableName() { @@ -55,12 +57,13 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 StatementBuilder createTableBuilder; createTableBuilder.CreateTable(s_CheckpointArgumentsTable_Table_Name).BeginColumns(); - createTableBuilder.Column(ColumnBuilder(s_CheckpointArgumentsTable_ContextId_Column, Type::Int).PrimaryKey().NotNull()); + createTableBuilder.Column(ColumnBuilder(s_CheckpointArgumentsTable_CommandName_Column, Type::Text)); createTableBuilder.Column(ColumnBuilder(s_CheckpointArgumentsTable_Query_Column, Type::Text)); createTableBuilder.Column(ColumnBuilder(s_CheckpointArgumentsTable_MultiQuery_Column, Type::Text)); createTableBuilder.Column(ColumnBuilder(s_CheckpointArgumentsTable_Manifest_Column, Type::Text)); createTableBuilder.Column(ColumnBuilder(s_CheckpointArgumentsTable_Id_Column, Type::Text)); + createTableBuilder.Column(ColumnBuilder(s_CheckpointArgumentsTable_Name_Column, Type::Text)); createTableBuilder.Column(ColumnBuilder(s_CheckpointArgumentsTable_Moniker_Column, Type::Text)); createTableBuilder.Column(ColumnBuilder(s_CheckpointArgumentsTable_Version_Column, Type::Text)); createTableBuilder.Column(ColumnBuilder(s_CheckpointArgumentsTable_Channel_Column, Type::Text)); @@ -72,11 +75,13 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 createTableBuilder.Column(ColumnBuilder(s_CheckpointArgumentsTable_Silent_Column, Type::Bool)); createTableBuilder.Column(ColumnBuilder(s_CheckpointArgumentsTable_Locale_Column, Type::Text)); createTableBuilder.Column(ColumnBuilder(s_CheckpointArgumentsTable_Log_Column, Type::Text)); - createTableBuilder.Column(ColumnBuilder(s_CheckpointArgumentsTable_CustomSwitches_Column, Type::Text)); + createTableBuilder.Column(ColumnBuilder(s_CheckpointArgumentsTable_Custom_Column, Type::Text)); createTableBuilder.Column(ColumnBuilder(s_CheckpointArgumentsTable_Override_Column, Type::Text)); createTableBuilder.Column(ColumnBuilder(s_CheckpointArgumentsTable_InstallLocation_Column, Type::Text)); + createTableBuilder.Column(ColumnBuilder(s_CheckpointArgumentsTable_HashOverride_Column, Type::Bool)); createTableBuilder.Column(ColumnBuilder(s_CheckpointArgumentsTable_SkipDependencies_Column, Type::Bool)); createTableBuilder.Column(ColumnBuilder(s_CheckpointArgumentsTable_IgnoreLocalArchiveMalwareScan_Column, Type::Bool)); + createTableBuilder.Column(ColumnBuilder(s_CheckpointArgumentsTable_DependencySource_Column, Type::Text)); createTableBuilder.Column(ColumnBuilder(s_CheckpointArgumentsTable_AcceptPackageAgreements_Column, Type::Bool)); createTableBuilder.Column(ColumnBuilder(s_CheckpointArgumentsTable_NoUpgrade_Column, Type::Bool)); createTableBuilder.Column(ColumnBuilder(s_CheckpointArgumentsTable_CustomHeader_Column, Type::Text)); @@ -84,7 +89,6 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 createTableBuilder.Column(ColumnBuilder(s_CheckpointArgumentsTable_Rename_Column, Type::Text)); createTableBuilder.Column(ColumnBuilder(s_CheckpointArgumentsTable_UninstallPrevious_Column, Type::Bool)); createTableBuilder.Column(ColumnBuilder(s_CheckpointArgumentsTable_Force_Column, Type::Bool)); - createTableBuilder.EndColumns(); createTableBuilder.Execute(connection); savepoint.Commit(); @@ -229,6 +233,28 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 } } + int CheckpointArgumentsTable::GetFirstContextId(SQLite::Connection& connection) + { + using namespace Builder; + StatementBuilder builder; + builder.Select(s_CheckpointArgumentsTable_ContextId_Column).From(s_CheckpointArgumentsTable_Table_Name) + .OrderBy(s_CheckpointArgumentsTable_ContextId_Column); + + Statement statement = builder.Prepare(connection); + THROW_HR_IF(E_UNEXPECTED, !statement.Step()); + return statement.GetColumn(0); + } + + bool CheckpointArgumentsTable::SetCommandName(SQLite::Connection& connection, int contextId, std::string_view commandName) + { + return UpdateArgumentByContextId(connection, contextId, s_CheckpointArgumentsTable_CommandName_Column, commandName); + } + + std::string CheckpointArgumentsTable::GetCommandName(SQLite::Connection& connection, int contextId) + { + return GetStringArgumentByContextId(connection, contextId, s_CheckpointArgumentsTable_CommandName_Column); + } + // This probably doesn't work... std::vector CheckpointArgumentsTable::GetAvailableArguments(SQLite::Connection& connection, int contextId) { diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointArgumentsTable.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointArgumentsTable.h index d3dfe3e88a..f701915562 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointArgumentsTable.h +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointArgumentsTable.h @@ -45,6 +45,12 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 static bool GetBoolArgumentByContextId(SQLite::Connection& connection, int contextId, std::string_view name); + static bool SetCommandName(SQLite::Connection& connection, int contextId, std::string_view commandName); + + static std::string GetCommandName(SQLite::Connection& connection, int contextId); + + static int GetFirstContextId(SQLite::Connection& connection); + // Gets all arguments with an available value. static std::vector GetAvailableArguments(SQLite::Connection& connection, int contextId); }; diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointContextTable.cpp b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointContextTable.cpp new file mode 100644 index 0000000000..fbb9151cf7 --- /dev/null +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointContextTable.cpp @@ -0,0 +1,102 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#include "pch.h" +#include "CheckpointContextTable.h" +#include "SQLiteStatementBuilder.h" +#include "Microsoft/Schema/ICheckpointIndex.h" + +namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 +{ + using namespace SQLite; + using namespace std::string_view_literals; + static constexpr std::string_view s_CheckpointContextTable_Table_Name = "CheckpointContext"sv; + static constexpr std::string_view s_CheckpointContextTable_ContextId_Column = "ContextId"sv; + + std::string_view CheckpointContextTable::TableName() + { + return s_CheckpointContextTable_Table_Name; + } + + void CheckpointContextTable::Create(SQLite::Connection& connection) + { + using namespace SQLite::Builder; + + SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "createCheckpointArgumentsTable_v1_0"); + + StatementBuilder createTableBuilder; + + createTableBuilder.CreateTable(s_CheckpointContextTable_Table_Name).BeginColumns(); + createTableBuilder.Column(ColumnBuilder(s_CheckpointContextTable_ContextId_Column, Type::Int).PrimaryKey().NotNull()); + createTableBuilder.EndColumns(); + createTableBuilder.Execute(connection); + savepoint.Commit(); + } + + bool CheckpointContextTable::ExistsById(const SQLite::Connection& connection, SQLite::rowid_t id) + { + SQLite::Builder::StatementBuilder builder; + builder.Select(SQLite::Builder::RowCount).From(s_CheckpointContextTable_Table_Name).Where(SQLite::RowIDName).Equals(id); + + SQLite::Statement countStatement = builder.Prepare(connection); + + THROW_HR_IF(E_UNEXPECTED, !countStatement.Step()); + + return (countStatement.GetColumn(0) != 0); + } + + void CheckpointContextTable::DeleteById(SQLite::Connection& connection, SQLite::rowid_t id) + { + SQLite::Builder::StatementBuilder builder; + builder.DeleteFrom(s_CheckpointContextTable_Table_Name).Where(SQLite::RowIDName).Equals(id); + + builder.Execute(connection); + } + + bool CheckpointContextTable::IsEmpty(SQLite::Connection& connection) + { + SQLite::Builder::StatementBuilder builder; + builder.Select(SQLite::Builder::RowCount).From(s_CheckpointContextTable_Table_Name); + + SQLite::Statement countStatement = builder.Prepare(connection); + + THROW_HR_IF(E_UNEXPECTED, !countStatement.Step()); + + return (countStatement.GetColumn(0) == 0); + } + + std::optional CheckpointContextTable::SelectByArgumentType(const SQLite::Connection& connection, int type) + { + SQLite::Builder::StatementBuilder builder; + builder.Select(SQLite::RowIDName).From(s_CheckpointContextTable_Table_Name).Where("id"); + builder.Equals(type); + + SQLite::Statement select = builder.Prepare(connection); + + if (select.Step()) + { + return select.GetColumn(0); + } + else + { + return {}; + } + } + + SQLite::rowid_t CheckpointContextTable::AddContext(SQLite::Connection& connection, int contextId) + { + SQLite::Builder::StatementBuilder builder; + builder.InsertInto(s_CheckpointContextTable_Table_Name) + .Columns({ s_CheckpointContextTable_ContextId_Column }) + .Values(contextId); + + builder.Execute(connection); + return connection.GetLastInsertRowID(); + } + + void CheckpointContextTable::RemoveContext(SQLite::Connection& connection, int contextId) + { + SQLite::Builder::StatementBuilder builder; + builder.DeleteFrom(s_CheckpointContextTable_Table_Name).Where(s_CheckpointContextTable_ContextId_Column).Equals(contextId); + builder.Execute(connection); + } +} \ No newline at end of file diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointContextTable.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointContextTable.h new file mode 100644 index 0000000000..65f4de30b2 --- /dev/null +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointContextTable.h @@ -0,0 +1,37 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once +#include "SQLiteWrapper.h" +#include "SQLiteStatementBuilder.h" +#include "Microsoft/Schema/ICheckpointIndex.h" +#include + +namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 +{ + struct CheckpointContextTable + { + // Get the table name. + static std::string_view TableName(); + + // Creates the table with named indices. + static void Create(SQLite::Connection& connection); + + // Gets a value indicating whether the Checkpoint file with rowid id exists. + static bool ExistsById(const SQLite::Connection& connection, SQLite::rowid_t id); + + // Deletes the Checkpoint row with the given rowid + static void DeleteById(SQLite::Connection& connection, SQLite::rowid_t id); + + // Gets a value indicating whether the table is empty. + static bool IsEmpty(SQLite::Connection& connection); + + // Selects the command argument by type from the table, returning the rowid if it exists. + static std::optional SelectByArgumentType(const SQLite::Connection& connection, int type); + + // Adds a context row. + static SQLite::rowid_t AddContext(SQLite::Connection& connection, int contextId); + + // Removes a context row. + static void RemoveContext(SQLite::Connection& connection, int contextId); + }; +} \ No newline at end of file diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointIndexInterface.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointIndexInterface.h index adb7394bb0..1075886034 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointIndexInterface.h +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointIndexInterface.h @@ -16,14 +16,18 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 std::string GetClientVersion(SQLite::Connection& connection) override; - SQLite::rowid_t SetCommandName(SQLite::Connection& connection, std::string_view clientVersion) override; + SQLite::rowid_t SetCommandName(SQLite::Connection& connection, int contextId, std::string_view clientVersion) override; - std::string GetCommandName(SQLite::Connection& connection) override; + std::string GetCommandName(SQLite::Connection& connection, int contextId) override; SQLite::rowid_t AddContextToArgumentTable(SQLite::Connection& connection, int contextId) override; void RemoveContextFromArgumentTable(SQLite::Connection& connection, int contextId) override; + SQLite::rowid_t AddContextToContextTable(SQLite::Connection& connection, int contextId) override; + + void RemoveContextFromContextTable(SQLite::Connection& connection, int contextId) override; + bool UpdateArgumentByContextId(SQLite::Connection& connection, int contextId, std::string_view name, std::string_view value) override; bool UpdateArgumentByContextId(SQLite::Connection& connection, int contextId, std::string_view name, bool value) override; @@ -36,6 +40,8 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 bool GetBoolArgumentByContextId(SQLite::Connection& connection, int contextId, std::string_view name) override; + int GetFirstContextId(SQLite::Connection& connection) override; + private: bool IsEmpty(SQLite::Connection& connection) override; }; diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointIndexInterface_1_0.cpp b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointIndexInterface_1_0.cpp index 5be628e76f..c557182682 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointIndexInterface_1_0.cpp +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointIndexInterface_1_0.cpp @@ -3,6 +3,7 @@ #include "pch.h" #include "Microsoft/Schema/Checkpoint_1_0/CheckpointIndexInterface.h" #include "Microsoft/Schema/Checkpoint_1_0/CheckpointArgumentsTable.h" +#include "Microsoft/Schema/Checkpoint_1_0/CheckpointContextTable.h" #include "Microsoft/Schema/Checkpoint_1_0/CheckpointMetadataTable.h" namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 @@ -31,6 +32,7 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 { SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "createCheckpointTable_v1_0"); Checkpoint_V1_0::CheckpointArgumentsTable::Create(connection); + Checkpoint_V1_0::CheckpointContextTable::Create(connection); Checkpoint_V1_0::CheckpointMetadataTable::Create(connection); savepoint.Commit(); } @@ -49,18 +51,18 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 return CheckpointMetadataTable::GetClientVersion(connection); } - SQLite::rowid_t CheckpointIndexInterface::SetCommandName(SQLite::Connection& connection, std::string_view commandName) + SQLite::rowid_t CheckpointIndexInterface::SetCommandName(SQLite::Connection& connection, int contextId, std::string_view commandName) { SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "setCommandName_v1_0"); - SQLite::rowid_t argumentId = CheckpointMetadataTable::SetCommandName(connection, commandName); + SQLite::rowid_t argumentId = CheckpointArgumentsTable::SetCommandName(connection, contextId, commandName); savepoint.Commit(); return argumentId; } - std::string CheckpointIndexInterface::GetCommandName(SQLite::Connection& connection) + std::string CheckpointIndexInterface::GetCommandName(SQLite::Connection& connection, int contextId) { - return CheckpointMetadataTable::GetCommandName(connection); + return CheckpointArgumentsTable::GetCommandName(connection, contextId); } bool CheckpointIndexInterface::IsEmpty(SQLite::Connection& connection) @@ -83,6 +85,21 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 savepoint.Commit(); } + SQLite::rowid_t CheckpointIndexInterface::AddContextToContextTable(SQLite::Connection& connection, int contextId) + { + SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "addContextToContextTable_v1_0"); + SQLite::rowid_t rowId = CheckpointContextTable::AddContext(connection, contextId); + savepoint.Commit(); + return rowId; + } + + void CheckpointIndexInterface::RemoveContextFromContextTable(SQLite::Connection& connection, int contextId) + { + SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "removeContextFromContextTable_v1_0"); + CheckpointContextTable::RemoveContext(connection, contextId); + savepoint.Commit(); + } + bool CheckpointIndexInterface::UpdateArgumentByContextId(SQLite::Connection& connection, int contextId, std::string_view name, std::string_view value) { SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "updateContextArgument_v1_0"); @@ -118,4 +135,9 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 { return CheckpointArgumentsTable::GetBoolArgumentByContextId(connection, contextId, name); } + + int CheckpointIndexInterface::GetFirstContextId(SQLite::Connection& connection) + { + return CheckpointArgumentsTable::GetFirstContextId(connection); + } } \ No newline at end of file diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointMetadataTable.cpp b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointMetadataTable.cpp index 1e868124c3..8b25ee2194 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointMetadataTable.cpp +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointMetadataTable.cpp @@ -13,7 +13,6 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 static constexpr std::string_view s_CheckpointMetadataTable_Value_Column = "Value"sv; static constexpr std::string_view s_CheckpointMetadataTable_ClientVersion = "ClientVersion"sv; - static constexpr std::string_view s_CheckpointMetadataTable_CommandName = "CommandName"sv; namespace { @@ -72,14 +71,4 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 { return GetNamedValue(connection, s_CheckpointMetadataTable_ClientVersion); } - - SQLite::rowid_t CheckpointMetadataTable::SetCommandName(SQLite::Connection& connection, std::string_view commandName) - { - return SetNamedValue(connection, s_CheckpointMetadataTable_CommandName, commandName); - } - - std::string CheckpointMetadataTable::GetCommandName(SQLite::Connection& connection) - { - return GetNamedValue(connection, s_CheckpointMetadataTable_CommandName); - } } \ No newline at end of file diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointMetadataTable.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointMetadataTable.h index 9c1099c82e..7617bf333c 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointMetadataTable.h +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointMetadataTable.h @@ -21,11 +21,5 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 // Sets the client version of the saved state. static SQLite::rowid_t SetClientVersion(SQLite::Connection& connection, std::string_view clientVersion); - - // Gets the command name of the saved state. - static std::string GetCommandName(SQLite::Connection& connection); - - // Sets the command name of the saved state. - static SQLite::rowid_t SetCommandName(SQLite::Connection& connection, std::string_view commandName); }; } \ No newline at end of file diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/ICheckpointIndex.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/ICheckpointIndex.h index 27298a8ce6..ec61769814 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/Schema/ICheckpointIndex.h +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/ICheckpointIndex.h @@ -21,14 +21,18 @@ namespace AppInstaller::Repository::Microsoft::Schema virtual std::string GetClientVersion(SQLite::Connection& connection) = 0; - virtual SQLite::rowid_t SetCommandName(SQLite::Connection& connection, std::string_view commandName) = 0; + virtual SQLite::rowid_t SetCommandName(SQLite::Connection& connection, int contextId, std::string_view commandName) = 0; - virtual std::string GetCommandName(SQLite::Connection& connection) = 0; + virtual std::string GetCommandName(SQLite::Connection& connection, int contextId) = 0; virtual bool IsEmpty(SQLite::Connection& connection) = 0; virtual SQLite::rowid_t AddContextToArgumentTable(SQLite::Connection& connection, int contextId) = 0; + virtual SQLite::rowid_t AddContextToContextTable(SQLite::Connection& connection, int contextId) = 0; + + virtual void RemoveContextFromContextTable(SQLite::Connection& connection, int contextId) = 0; + virtual void RemoveContextFromArgumentTable(SQLite::Connection& connection, int contextId) = 0; virtual bool UpdateArgumentByContextId(SQLite::Connection& connection, int contextId, std::string_view name, std::string_view value) = 0; @@ -42,5 +46,7 @@ namespace AppInstaller::Repository::Microsoft::Schema virtual std::string GetStringArgumentByContextId(SQLite::Connection& connection, int contextId, std::string_view name) = 0; virtual bool GetBoolArgumentByContextId(SQLite::Connection& connection, int contextId, std::string_view name) = 0; + + virtual int GetFirstContextId(SQLite::Connection& connection) = 0; }; } \ No newline at end of file From 47861949f5473611e95cf1fb47d56859199ae825 Mon Sep 17 00:00:00 2001 From: Ryan Fu Date: Tue, 1 Aug 2023 12:52:31 -0700 Subject: [PATCH 07/43] clean up index functions --- src/AppInstallerCLICore/CheckpointManager.cpp | 45 +++--- src/AppInstallerCLICore/CheckpointManager.h | 8 +- .../Commands/InstallCommand.cpp | 2 +- .../Commands/ResumeCommand.cpp | 17 ++- src/AppInstallerCLICore/ExecutionContext.h | 14 +- .../Microsoft/CheckpointIndex.cpp | 81 ++++++----- .../Microsoft/CheckpointIndex.h | 24 ++-- .../CheckpointArgumentsTable.cpp | 135 +++++++----------- .../Checkpoint_1_0/CheckpointArgumentsTable.h | 42 +++--- .../Checkpoint_1_0/CheckpointContextTable.cpp | 73 +++++++--- .../Checkpoint_1_0/CheckpointContextTable.h | 15 +- .../Checkpoint_1_0/CheckpointIndexInterface.h | 25 +--- .../CheckpointIndexInterface_1_0.cpp | 93 +++++++++--- .../Microsoft/Schema/ICheckpointIndex.h | 33 ++++- 14 files changed, 344 insertions(+), 263 deletions(-) diff --git a/src/AppInstallerCLICore/CheckpointManager.cpp b/src/AppInstallerCLICore/CheckpointManager.cpp index a298b095f7..af387d7b4e 100644 --- a/src/AppInstallerCLICore/CheckpointManager.cpp +++ b/src/AppInstallerCLICore/CheckpointManager.cpp @@ -103,14 +103,14 @@ namespace AppInstaller::CLI::Checkpoint Execution::Args::Type executionArgsType = argument.ExecArgType(); if (argument.Type() == ArgumentType::Flag) { - if (m_checkpointIndex->GetBoolArgumentByContextId(contextId, argument.Name())) + if (m_checkpointIndex->GetBoolArgument(contextId, argument.Name())) { context.Args.AddArg(executionArgsType); } } else { - context.Args.AddArg(executionArgsType, m_checkpointIndex->GetStringArgumentByContextId(contextId, argument.Name())); + context.Args.AddArg(executionArgsType, m_checkpointIndex->GetStringArgument(contextId, argument.Name())); } } } @@ -134,59 +134,55 @@ namespace AppInstaller::CLI::Checkpoint { if (argument.Type() == ArgumentType::Flag) { - m_checkpointIndex->UpdateArgumentByContextId(contextId, argument.Name(), true); + m_checkpointIndex->UpdateArgument(contextId, argument.Name(), true); } else { const auto& argumentValue = context.Args.GetArg(type); - m_checkpointIndex->UpdateArgumentByContextId(contextId, argument.Name(), argumentValue); + m_checkpointIndex->UpdateArgument(contextId, argument.Name(), argumentValue); } } } } } - void CheckpointManager::Checkpoint(Execution::Context& context, Execution::CheckpointFlags targetCheckpointFlag) + void CheckpointManager::Checkpoint(Execution::Context& context, Execution::CheckpointFlag checkpointFlag) { - Execution::CheckpointFlags currentCheckpointFlag = context.GetCurrentCheckpoint(); + Execution::CheckpointFlag contextCheckpointFlag = context.GetCurrentCheckpoint(); - // If the current checkpoint is behind the target checkpoint, load the checkpoint state from the index. - // If the current checkpoint is ahead of the target checkpoint, save the checkpoint state to the index. - // If the states are equal, do nothing. - if (currentCheckpointFlag > targetCheckpointFlag) + // If the context is ahead of the current checkpoint, we have previously executed this state, load checkpoint state from index. + // If the context is behind the current checkpoint, we have not yet processed this state, save checkpoint state to index. + // If the checkpoints are equal, do nothing. + if (contextCheckpointFlag > checkpointFlag) { - // If the current checkpoint is ahead of the target, this means we have already previously passed this state, load. - LoadCheckpoint(context, targetCheckpointFlag); + LoadCheckpoint(context, checkpointFlag); } - else if (currentCheckpointFlag < targetCheckpointFlag) + else if (contextCheckpointFlag < checkpointFlag) { - // If the current checkpoint is behind the target checkpoint, we are still working our way up to the target, - // save the state. - SaveCheckpoint(context, targetCheckpointFlag); + SaveCheckpoint(context, checkpointFlag); } - - // If the current checkpoint is equal, do nothing as it has already been performed. } - void CheckpointManager::SaveCheckpoint(Execution::Context& context, Execution::CheckpointFlags flag) + void CheckpointManager::SaveCheckpoint(Execution::Context& context, Execution::CheckpointFlag flag) { switch (flag) { - case Execution::CheckpointFlags::CommandArguments: + case Execution::CheckpointFlag::CommandArguments: RecordContextArgsToIndex(context); break; default: THROW_HR(E_UNEXPECTED); } + m_checkpointIndex->SetLastCheckpoint(context.GetContextId(), static_cast(flag)); context.SetCurrentCheckpoint(flag); } - void CheckpointManager::LoadCheckpoint(Execution::Context& context, Execution::CheckpointFlags flag) + void CheckpointManager::LoadCheckpoint(Execution::Context& context, Execution::CheckpointFlag flag) { switch (flag) { - case Execution::CheckpointFlags::CommandArguments: + case Execution::CheckpointFlag::CommandArguments: PopulateContextArgsFromIndex(context); break; default: @@ -211,4 +207,9 @@ namespace AppInstaller::CLI::Checkpoint { return m_checkpointIndex->GetFirstContextId(); } + + Execution::CheckpointFlag CheckpointManager::GetLastCheckpoint(int contextId) + { + return static_cast(m_checkpointIndex->GetLastCheckpoint(contextId)); + } } diff --git a/src/AppInstallerCLICore/CheckpointManager.h b/src/AppInstallerCLICore/CheckpointManager.h index 5d8d5ebf74..5d06f29cd5 100644 --- a/src/AppInstallerCLICore/CheckpointManager.h +++ b/src/AppInstallerCLICore/CheckpointManager.h @@ -33,7 +33,7 @@ namespace AppInstaller::CLI::Checkpoint void RemoveContext(int contextId); - void Checkpoint(Execution::Context& context, Execution::CheckpointFlags flag); + void Checkpoint(Execution::Context& context, Execution::CheckpointFlag flag); std::unique_ptr CreateContextFromCheckpointIndex(); @@ -43,6 +43,8 @@ namespace AppInstaller::CLI::Checkpoint int GetFirstContextId(); + Execution::CheckpointFlag GetLastCheckpoint(int contextId); + private: CheckpointManager() = default; ~CheckpointManager(); @@ -51,8 +53,8 @@ namespace AppInstaller::CLI::Checkpoint bool CleanUpIndex(); - void SaveCheckpoint(Execution::Context& context, Execution::CheckpointFlags flag); - void LoadCheckpoint(Execution::Context& context, Execution::CheckpointFlags flag); + void SaveCheckpoint(Execution::Context& context, Execution::CheckpointFlag flag); + void LoadCheckpoint(Execution::Context& context, Execution::CheckpointFlag flag); void PopulateContextArgsFromIndex(Execution::Context& context); void RecordContextArgsToIndex(Execution::Context& context); diff --git a/src/AppInstallerCLICore/Commands/InstallCommand.cpp b/src/AppInstallerCLICore/Commands/InstallCommand.cpp index e477499f1f..21edcfba3c 100644 --- a/src/AppInstallerCLICore/Commands/InstallCommand.cpp +++ b/src/AppInstallerCLICore/Commands/InstallCommand.cpp @@ -109,7 +109,7 @@ namespace AppInstaller::CLI { CheckpointManager::Instance().Initialize(); CheckpointManager::Instance().AddContext(context.GetContextId()); - CheckpointManager::Instance().Checkpoint(context, Execution::CheckpointFlags::CommandArguments); + CheckpointManager::Instance().Checkpoint(context, Execution::CheckpointFlag::CommandArguments); } context.SetFlags(ContextFlag::ShowSearchResultsOnPartialFailure); diff --git a/src/AppInstallerCLICore/Commands/ResumeCommand.cpp b/src/AppInstallerCLICore/Commands/ResumeCommand.cpp index deb19bf25e..3dd0993f15 100644 --- a/src/AppInstallerCLICore/Commands/ResumeCommand.cpp +++ b/src/AppInstallerCLICore/Commands/ResumeCommand.cpp @@ -46,15 +46,14 @@ namespace AppInstaller::CLI AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_CLIENTVERSION_MISMATCH); } + // Get the root context id from the index. int rootContextId = CheckpointManager::Instance().GetFirstContextId(); + + // Determine the command to execute. std::string commandName = CheckpointManager::Instance().GetCommandName(rootContextId); std::unique_ptr commandToResume; - // Use the root command to obtain all of the available commands. - std::unique_ptr rootCommand = std::make_unique(); - auto commands = rootCommand->GetCommands(); - - for (auto& command : commands) + for (auto& command : std::make_unique()->GetCommands()) { if ( Utility::CaseInsensitiveEquals(commandName, command->Name()) || @@ -67,11 +66,15 @@ namespace AppInstaller::CLI } } - // Create a new context and load from checkpoint + // Create a new context, set the executing command and current checkpoint. auto resumeContext = context.CreateEmptyContext(rootContextId); resumeContext->SetExecutingCommand(commandToResume.get()); - CheckpointManager::Instance().Checkpoint(*resumeContext, Execution::CheckpointFlags::CommandArguments); + // Set the current checkpoint of the root context. + resumeContext->SetCurrentCheckpoint(CheckpointManager::Instance().GetLastCheckpoint(rootContextId)); + + // Load the arguments from the checkpoint index prior to executing the command. + CheckpointManager::Instance().Checkpoint(*resumeContext, Execution::CheckpointFlag::CommandArguments); commandToResume->Execute(*resumeContext); } } diff --git a/src/AppInstallerCLICore/ExecutionContext.h b/src/AppInstallerCLICore/ExecutionContext.h index 69b0fd2db4..0f4cbc1258 100644 --- a/src/AppInstallerCLICore/ExecutionContext.h +++ b/src/AppInstallerCLICore/ExecutionContext.h @@ -52,7 +52,7 @@ namespace AppInstaller::CLI::Workflow namespace AppInstaller::CLI::Execution { - enum class CheckpointFlags : uint32_t + enum class CheckpointFlag : uint32_t { None, CommandArguments, @@ -169,16 +169,16 @@ namespace AppInstaller::CLI::Execution bool ShouldExecuteWorkflowTask(const Workflow::WorkflowTask& task); #endif // Sets the target checkpoint of the context. - void SetTargetCheckpoint(CheckpointFlags flag) { m_targetCheckpoint = flag; }; + void SetTargetCheckpoint(CheckpointFlag flag) { m_targetCheckpoint = flag; }; // Gets the current checkpoint of the context. - CheckpointFlags GetCurrentCheckpoint() { return m_currentCheckpoint; }; + CheckpointFlag GetCurrentCheckpoint() { return m_currentCheckpoint; }; // Gets the target checkpoint of the context. - CheckpointFlags GetTargetCheckpoint() { return m_targetCheckpoint; }; + CheckpointFlag GetTargetCheckpoint() { return m_targetCheckpoint; }; // Sets the current checkpoint flag of the context. - void SetCurrentCheckpoint(Execution::CheckpointFlags flag) + void SetCurrentCheckpoint(Execution::CheckpointFlag flag) { m_currentCheckpoint = flag; } @@ -207,8 +207,8 @@ namespace AppInstaller::CLI::Execution Workflow::ExecutionStage m_executionStage = Workflow::ExecutionStage::Initial; AppInstaller::ThreadLocalStorage::WingetThreadGlobals m_threadGlobals; AppInstaller::CLI::Command* m_executingCommand = nullptr; - CheckpointFlags m_currentCheckpoint = CheckpointFlags::None; - CheckpointFlags m_targetCheckpoint = CheckpointFlags::None; + CheckpointFlag m_currentCheckpoint = CheckpointFlag::None; + CheckpointFlag m_targetCheckpoint = CheckpointFlag::None; static int s_contextCount; int m_contextId = s_contextCount; }; diff --git a/src/AppInstallerRepositoryCore/Microsoft/CheckpointIndex.cpp b/src/AppInstallerRepositoryCore/Microsoft/CheckpointIndex.cpp index 2682d3fb98..37a14296fc 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/CheckpointIndex.cpp +++ b/src/AppInstallerRepositoryCore/Microsoft/CheckpointIndex.cpp @@ -9,7 +9,7 @@ namespace AppInstaller::Repository::Microsoft { CheckpointIndex CheckpointIndex::CreateNew(const std::string& filePath, Schema::Version version) { - AICLI_LOG(Repo, Info, << "Creating new Savepoint Index with version [" << version << "] at '" << filePath << "'"); + AICLI_LOG(Repo, Info, << "Creating new Checkpoint Index with version [" << version << "] at '" << filePath << "'"); CheckpointIndex result{ filePath, version }; SQLite::Savepoint savepoint = SQLite::Savepoint::Create(result.m_dbconn, "CheckpointIndex_createnew"); @@ -78,9 +78,7 @@ namespace AppInstaller::Repository::Microsoft IdType result = m_interface->SetClientVersion(m_dbconn, clientVersion); SetLastWriteTime(); - savepoint.Commit(); - return result; } @@ -89,27 +87,6 @@ namespace AppInstaller::Repository::Microsoft return m_interface->GetClientVersion(m_dbconn); } - CheckpointIndex::IdType CheckpointIndex::SetCommandName(int contextId, std::string_view commandName) - { - std::lock_guard lockInterface{ *m_interfaceLock }; - AICLI_LOG(Repo, Verbose, << "Setting command name [" << commandName << "]"); - - SQLite::Savepoint savepoint = SQLite::Savepoint::Create(m_dbconn, "checkpointindex_setcommandname"); - - IdType result = m_interface->SetCommandName(m_dbconn, contextId, commandName); - - SetLastWriteTime(); - - savepoint.Commit(); - - return result; - } - - std::string CheckpointIndex::GetCommandName(int contextId) - { - return m_interface->GetCommandName(m_dbconn, contextId); - } - void CheckpointIndex::AddContext(int contextId) { std::lock_guard lockInterface{ *m_interfaceLock }; @@ -134,19 +111,39 @@ namespace AppInstaller::Repository::Microsoft m_interface->RemoveContextFromArgumentTable(m_dbconn, contextId); m_interface->RemoveContextFromContextTable(m_dbconn, contextId); + SetLastWriteTime(); + savepoint.Commit(); + } + + CheckpointIndex::IdType CheckpointIndex::SetCommandName(int contextId, std::string_view commandName) + { + std::lock_guard lockInterface{ *m_interfaceLock }; + AICLI_LOG(Repo, Verbose, << "Setting command name [" << commandName << "]"); + + SQLite::Savepoint savepoint = SQLite::Savepoint::Create(m_dbconn, "checkpointindex_setcommandname"); + + IdType result = m_interface->SetCommandName(m_dbconn, contextId, commandName); + SetLastWriteTime(); savepoint.Commit(); + + return result; } - bool CheckpointIndex::UpdateArgumentByContextId(int contextId, std::string_view name, std::string_view value) + std::string CheckpointIndex::GetCommandName(int contextId) + { + return m_interface->GetCommandName(m_dbconn, contextId); + } + + bool CheckpointIndex::UpdateArgument(int contextId, std::string_view name, std::string_view value) { std::lock_guard lockInterface{ *m_interfaceLock }; AICLI_LOG(Repo, Verbose, << "Updating argument column for [" << name << "] with value [" << value << "]"); SQLite::Savepoint savepoint = SQLite::Savepoint::Create(m_dbconn, "checkpointindex_updatestringargument"); - IdType result = m_interface->UpdateArgumentByContextId(m_dbconn, contextId, name, value); + bool result = m_interface->UpdateArgumentByContextId(m_dbconn, contextId, name, value); SetLastWriteTime(); @@ -155,14 +152,14 @@ namespace AppInstaller::Repository::Microsoft return result; } - bool CheckpointIndex::UpdateArgumentByContextId(int contextId, std::string_view name, bool value) + bool CheckpointIndex::UpdateArgument(int contextId, std::string_view name, bool value) { std::lock_guard lockInterface{ *m_interfaceLock }; AICLI_LOG(Repo, Verbose, << "Updating argument column for [" << name << "] with value [" << value << "]"); SQLite::Savepoint savepoint = SQLite::Savepoint::Create(m_dbconn, "checkpointindex_updateboolargument"); - IdType result = m_interface->UpdateArgumentByContextId(m_dbconn, contextId, name, value); + bool result = m_interface->UpdateArgumentByContextId(m_dbconn, contextId, name, value); SetLastWriteTime(); @@ -171,22 +168,17 @@ namespace AppInstaller::Repository::Microsoft return result; } - std::vector CheckpointIndex::GetAvailableColumns(int contextId) - { - return m_interface->GetAvailableArguments(m_dbconn, contextId); - } - bool CheckpointIndex::ContainsArgument(int contextId, std::string_view name) { return m_interface->ContainsArgument(m_dbconn, contextId, name); } - std::string CheckpointIndex::GetStringArgumentByContextId(int contextId, std::string_view name) + std::string CheckpointIndex::GetStringArgument(int contextId, std::string_view name) { return m_interface->GetStringArgumentByContextId(m_dbconn, contextId, name); } - bool CheckpointIndex::GetBoolArgumentByContextId(int contextId, std::string_view name) + bool CheckpointIndex::GetBoolArgument(int contextId, std::string_view name) { return m_interface->GetBoolArgumentByContextId(m_dbconn, contextId, name); } @@ -196,6 +188,25 @@ namespace AppInstaller::Repository::Microsoft return m_interface->GetFirstContextId(m_dbconn); } + bool CheckpointIndex::SetLastCheckpoint(int contextId, int checkpointFlag) + { + std::lock_guard lockInterface{ *m_interfaceLock }; + AICLI_LOG(Repo, Verbose, << "Updating last checkpoint flag for [" << contextId << "] with value [" << checkpointFlag << "]"); + + SQLite::Savepoint savepoint = SQLite::Savepoint::Create(m_dbconn, "checkpointindex_updatelastcheckpointflag"); + + bool result = m_interface->SetLastCheckpointByContextId(m_dbconn, contextId, checkpointFlag); + + SetLastWriteTime(); + savepoint.Commit(); + return result; + } + + int CheckpointIndex::GetLastCheckpoint(int contextId) + { + return m_interface->GetLastCheckpointByContextId(m_dbconn, contextId); + } + std::unique_ptr CheckpointIndex::CreateICheckpointIndex() const { if (m_version == Schema::Version{ 1, 0 } || diff --git a/src/AppInstallerRepositoryCore/Microsoft/CheckpointIndex.h b/src/AppInstallerRepositoryCore/Microsoft/CheckpointIndex.h index a54373263b..53e7482714 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/CheckpointIndex.h +++ b/src/AppInstallerRepositoryCore/Microsoft/CheckpointIndex.h @@ -32,33 +32,35 @@ namespace AppInstaller::Repository::Microsoft // Opens or creates a CheckpointIndex database on the default path. static std::shared_ptr OpenOrCreateDefault(GUID guid, OpenDisposition openDisposition = OpenDisposition::ReadWrite); + bool IsEmpty(); + IdType SetClientVersion(std::string_view clientVersion); std::string GetClientVersion(); - IdType SetCommandName(int contextId, std::string_view commandName); - - std::string GetCommandName(int contextId); - void AddContext(int contextId); void RemoveContext(int contextId); - bool UpdateArgumentByContextId(int contextId, std::string_view name, std::string_view value); - - bool UpdateArgumentByContextId(int contextId, std::string_view name, bool value); + IdType SetCommandName(int contextId, std::string_view commandName); - std::vector GetAvailableColumns(int contextId); + std::string GetCommandName(int contextId); bool ContainsArgument(int contextId, std::string_view name); - std::string GetStringArgumentByContextId(int contextId, std::string_view name); + bool UpdateArgument(int contextId, std::string_view name, std::string_view value); + + bool UpdateArgument(int contextId, std::string_view name, bool value); - bool GetBoolArgumentByContextId(int contextId, std::string_view name); + std::string GetStringArgument(int contextId, std::string_view name); + + bool GetBoolArgument(int contextId, std::string_view name); int GetFirstContextId(); - bool IsEmpty(); + bool SetLastCheckpoint(int contextId, int checkpointFlag); + + int GetLastCheckpoint(int contextId); private: // Constructor used to open an existing index. diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointArgumentsTable.cpp b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointArgumentsTable.cpp index dfa960ef70..b517c54b33 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointArgumentsTable.cpp +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointArgumentsTable.cpp @@ -55,9 +55,8 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "createCheckpointArgumentsTable_v1_0"); StatementBuilder createTableBuilder; - createTableBuilder.CreateTable(s_CheckpointArgumentsTable_Table_Name).BeginColumns(); - createTableBuilder.Column(ColumnBuilder(s_CheckpointArgumentsTable_ContextId_Column, Type::Int).PrimaryKey().NotNull()); + createTableBuilder.Column(ColumnBuilder(s_CheckpointArgumentsTable_ContextId_Column, Type::Int).Unique().NotNull()); createTableBuilder.Column(ColumnBuilder(s_CheckpointArgumentsTable_CommandName_Column, Type::Text)); createTableBuilder.Column(ColumnBuilder(s_CheckpointArgumentsTable_Query_Column, Type::Text)); createTableBuilder.Column(ColumnBuilder(s_CheckpointArgumentsTable_MultiQuery_Column, Type::Text)); @@ -94,6 +93,24 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 savepoint.Commit(); } + std::optional CheckpointArgumentsTable::SelectByContextId(const SQLite::Connection& connection, int contextId) + { + SQLite::Builder::StatementBuilder builder; + builder.Select(SQLite::RowIDName).From(s_CheckpointArgumentsTable_Table_Name).Where(s_CheckpointArgumentsTable_ContextId_Column); + builder.Equals(contextId); + + SQLite::Statement select = builder.Prepare(connection); + + if (select.Step()) + { + return select.GetColumn(0); + } + else + { + return {}; + } + } + bool CheckpointArgumentsTable::ExistsById(const SQLite::Connection& connection, SQLite::rowid_t id) { SQLite::Builder::StatementBuilder builder; @@ -126,84 +143,65 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 return (countStatement.GetColumn(0) == 0); } - std::optional CheckpointArgumentsTable::SelectByArgumentType(const SQLite::Connection& connection, int type) + SQLite::rowid_t CheckpointArgumentsTable::AddContext(SQLite::Connection& connection, int contextId) { SQLite::Builder::StatementBuilder builder; - builder.Select(SQLite::RowIDName).From(s_CheckpointArgumentsTable_Table_Name).Where("id"); - builder.Equals(type); - - SQLite::Statement select = builder.Prepare(connection); + builder.InsertInto(s_CheckpointArgumentsTable_Table_Name) + .Columns({ s_CheckpointArgumentsTable_ContextId_Column }) + .Values(contextId); - if (select.Step()) - { - return select.GetColumn(0); - } - else - { - return {}; - } + builder.Execute(connection); + return connection.GetLastInsertRowID(); } - bool CheckpointArgumentsTable::UpdateArgumentByContextId(SQLite::Connection& connection, int contextId, std::string_view name, std::string_view value) + void CheckpointArgumentsTable::RemoveContextById(SQLite::Connection& connection, SQLite::rowid_t id) { SQLite::Builder::StatementBuilder builder; - builder.Update(s_CheckpointArgumentsTable_Table_Name).Set() - .Column(name).Equals(value) - .Where(s_CheckpointArgumentsTable_ContextId_Column).Equals(contextId); - + builder.DeleteFrom(s_CheckpointArgumentsTable_Table_Name).Where(SQLite::RowIDName).Equals(id); builder.Execute(connection); - return connection.GetChanges() != 0; } - bool CheckpointArgumentsTable::UpdateArgumentByContextId(SQLite::Connection& connection, int contextId, std::string_view name, bool value) + bool CheckpointArgumentsTable::UpdateArgumentById(SQLite::Connection& connection, SQLite::rowid_t id, std::string_view name, std::string_view value) { SQLite::Builder::StatementBuilder builder; builder.Update(s_CheckpointArgumentsTable_Table_Name).Set() .Column(name).Equals(value) - .Where(s_CheckpointArgumentsTable_ContextId_Column).Equals(contextId); + .Where(SQLite::RowIDName).Equals(id); builder.Execute(connection); return connection.GetChanges() != 0; } - SQLite::rowid_t CheckpointArgumentsTable::AddContext(SQLite::Connection& connection, int contextId) + bool CheckpointArgumentsTable::UpdateArgumentById(SQLite::Connection& connection, SQLite::rowid_t id, std::string_view name, bool value) { SQLite::Builder::StatementBuilder builder; - builder.InsertInto(s_CheckpointArgumentsTable_Table_Name) - .Columns({ s_CheckpointArgumentsTable_ContextId_Column }) - .Values(contextId); + builder.Update(s_CheckpointArgumentsTable_Table_Name).Set() + .Column(name).Equals(value) + .Where(SQLite::RowIDName).Equals(id); builder.Execute(connection); - return connection.GetLastInsertRowID(); + return connection.GetChanges() != 0; } - void CheckpointArgumentsTable::RemoveContext(SQLite::Connection& connection, int contextId) - { - SQLite::Builder::StatementBuilder builder; - builder.DeleteFrom(s_CheckpointArgumentsTable_Table_Name).Where(s_CheckpointArgumentsTable_ContextId_Column).Equals(contextId); - builder.Execute(connection); - } - bool CheckpointArgumentsTable::ContainsArgument(SQLite::Connection& connection, int contextId, std::string_view name) + bool CheckpointArgumentsTable::ContainsArgument(SQLite::Connection& connection, SQLite::rowid_t id, std::string_view name) { SQLite::Builder::StatementBuilder builder; builder.Select(SQLite::Builder::RowCount).From(s_CheckpointArgumentsTable_Table_Name) - .Where(s_CheckpointArgumentsTable_ContextId_Column).Equals(contextId).And(name).IsNotNull(); + .Where(SQLite::RowIDName).Equals(id).And(name).IsNotNull(); SQLite::Statement statement = builder.Prepare(connection); THROW_HR_IF(E_UNEXPECTED, !statement.Step()); return statement.GetColumn(0) != 0; } - std::string CheckpointArgumentsTable::GetStringArgumentByContextId(SQLite::Connection& connection, int contextId, std::string_view name) + std::string CheckpointArgumentsTable::GetStringArgumentById(SQLite::Connection& connection, SQLite::rowid_t id, std::string_view name) { - using namespace Builder; - - StatementBuilder builder; + SQLite::Builder::StatementBuilder builder; builder.Select(name).From(s_CheckpointArgumentsTable_Table_Name). - Where(s_CheckpointArgumentsTable_ContextId_Column).Equals(contextId); + Where(SQLite::RowIDName).Equals(id); - Statement statement = builder.Prepare(connection); + SQLite::Statement statement = builder.Prepare(connection); if (statement.Step()) { return statement.GetColumn(0); @@ -214,15 +212,13 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 } } - bool CheckpointArgumentsTable::GetBoolArgumentByContextId(SQLite::Connection& connection, int contextId, std::string_view name) + bool CheckpointArgumentsTable::GetBoolArgumentById(SQLite::Connection& connection, SQLite::rowid_t id, std::string_view name) { - using namespace Builder; - - StatementBuilder builder; + SQLite::Builder::StatementBuilder builder; builder.Select(name).From(s_CheckpointArgumentsTable_Table_Name). - Where(s_CheckpointArgumentsTable_ContextId_Column).Equals(contextId); + Where(SQLite::RowIDName).Equals(id); - Statement statement = builder.Prepare(connection); + SQLite::Statement statement = builder.Prepare(connection); if (statement.Step()) { return statement.GetColumn(0); @@ -235,53 +231,22 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 int CheckpointArgumentsTable::GetFirstContextId(SQLite::Connection& connection) { - using namespace Builder; - StatementBuilder builder; + SQLite::Builder::StatementBuilder builder; builder.Select(s_CheckpointArgumentsTable_ContextId_Column).From(s_CheckpointArgumentsTable_Table_Name) .OrderBy(s_CheckpointArgumentsTable_ContextId_Column); - Statement statement = builder.Prepare(connection); + SQLite::Statement statement = builder.Prepare(connection); THROW_HR_IF(E_UNEXPECTED, !statement.Step()); return statement.GetColumn(0); } - bool CheckpointArgumentsTable::SetCommandName(SQLite::Connection& connection, int contextId, std::string_view commandName) + bool CheckpointArgumentsTable::SetCommandNameById(SQLite::Connection& connection, SQLite::rowid_t id, std::string_view commandName) { - return UpdateArgumentByContextId(connection, contextId, s_CheckpointArgumentsTable_CommandName_Column, commandName); + return UpdateArgumentById(connection, id, s_CheckpointArgumentsTable_CommandName_Column, commandName); } - std::string CheckpointArgumentsTable::GetCommandName(SQLite::Connection& connection, int contextId) + std::string CheckpointArgumentsTable::GetCommandNameById(SQLite::Connection& connection, SQLite::rowid_t id) { - return GetStringArgumentByContextId(connection, contextId, s_CheckpointArgumentsTable_CommandName_Column); - } - - // This probably doesn't work... - std::vector CheckpointArgumentsTable::GetAvailableArguments(SQLite::Connection& connection, int contextId) - { - SQLite::Builder::StatementBuilder builder; - builder.Select().From(s_CheckpointArgumentsTable_Table_Name).Where(s_CheckpointArgumentsTable_ContextId_Column).Equals(contextId); - - SQLite::Statement select = builder.Prepare(connection); - - std::vector availableColumnNames; - - if (select.Step()) - { - const std::tuple row = select.GetRow(); - int size = static_cast(std::tuple_size{}); - - // Start at column index 1 to skip 'rowid' - for (int i = 1; i < size; i++) - { - availableColumnNames.emplace_back(select.GetColumnName(i)); - } - - return availableColumnNames; - } - else - { - return {}; - } - + return GetStringArgumentById(connection, id, s_CheckpointArgumentsTable_CommandName_Column); } } \ No newline at end of file diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointArgumentsTable.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointArgumentsTable.h index f701915562..cc770732aa 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointArgumentsTable.h +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointArgumentsTable.h @@ -16,42 +16,46 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 // Creates the table with named indices. static void Create(SQLite::Connection& connection); - // Gets a value indicating whether the Checkpoint file with rowid id exists. + // Gets a value indicating whether a context with the given id exists. static bool ExistsById(const SQLite::Connection& connection, SQLite::rowid_t id); - // Deletes the Checkpoint row with the given rowid + // Deletes the context with the given id static void DeleteById(SQLite::Connection& connection, SQLite::rowid_t id); // Gets a value indicating whether the table is empty. static bool IsEmpty(SQLite::Connection& connection); - // Selects the command argument by type from the table, returning the rowid if it exists. - static std::optional SelectByArgumentType(const SQLite::Connection& connection, int type); + // Selects the context arguments by context id from the table, returning the rowid if it exists. + static std::optional SelectByContextId(const SQLite::Connection& connection, int contextId); - // Adds a context row. + // Adds a context with the given context id to the table. static SQLite::rowid_t AddContext(SQLite::Connection& connection, int contextId); - // Removes a context row. - static void RemoveContext(SQLite::Connection& connection, int contextId); + // Removes a context from the table by id. + static void RemoveContextById(SQLite::Connection& connection, SQLite::rowid_t id); - // Updates an argument for a given context id. - static bool UpdateArgumentByContextId(SQLite::Connection& connection, int contextId, std::string_view name, std::string_view value); + // Gets a value indicating whether a context argument exists. + static bool ContainsArgument(SQLite::Connection& connection, SQLite::rowid_t id, std::string_view name); - static bool UpdateArgumentByContextId(SQLite::Connection& connection, int contextId, std::string_view name, bool value); + // Updates a string argument by id. + static bool UpdateArgumentById(SQLite::Connection& connection, SQLite::rowid_t id, std::string_view name, std::string_view value); + + // Gets a string value for an argument by id. + static std::string GetStringArgumentById(SQLite::Connection& connection, SQLite::rowid_t id, std::string_view name); - static bool ContainsArgument(SQLite::Connection& connection, int contextId, std::string_view name); + // Updates a bool argument by id. + static bool UpdateArgumentById(SQLite::Connection& connection, SQLite::rowid_t id, std::string_view name, bool value); - static std::string GetStringArgumentByContextId(SQLite::Connection& connection, int contextId, std::string_view name); + // Gets a boolean value for an argument by id. + static bool GetBoolArgumentById(SQLite::Connection& connection, SQLite::rowid_t id, std::string_view name); - static bool GetBoolArgumentByContextId(SQLite::Connection& connection, int contextId, std::string_view name); + // Sets the command name by id. + static bool SetCommandNameById(SQLite::Connection& connection, SQLite::rowid_t id, std::string_view commandName); - static bool SetCommandName(SQLite::Connection& connection, int contextId, std::string_view commandName); - - static std::string GetCommandName(SQLite::Connection& connection, int contextId); + // Gets the command name by id. + static std::string GetCommandNameById(SQLite::Connection& connection, SQLite::rowid_t id); + // Gets the first context id from the table. static int GetFirstContextId(SQLite::Connection& connection); - - // Gets all arguments with an available value. - static std::vector GetAvailableArguments(SQLite::Connection& connection, int contextId); }; } \ No newline at end of file diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointContextTable.cpp b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointContextTable.cpp index fbb9151cf7..098b6c3f1d 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointContextTable.cpp +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointContextTable.cpp @@ -11,6 +11,7 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 using namespace std::string_view_literals; static constexpr std::string_view s_CheckpointContextTable_Table_Name = "CheckpointContext"sv; static constexpr std::string_view s_CheckpointContextTable_ContextId_Column = "ContextId"sv; + static constexpr std::string_view s_CheckpointContextTable_LastCheckpoint_Column = "LastCheckpoint"sv; std::string_view CheckpointContextTable::TableName() { @@ -24,14 +25,32 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "createCheckpointArgumentsTable_v1_0"); StatementBuilder createTableBuilder; - createTableBuilder.CreateTable(s_CheckpointContextTable_Table_Name).BeginColumns(); - createTableBuilder.Column(ColumnBuilder(s_CheckpointContextTable_ContextId_Column, Type::Int).PrimaryKey().NotNull()); + createTableBuilder.Column(ColumnBuilder(s_CheckpointContextTable_ContextId_Column, Type::Int).Unique().NotNull()); + createTableBuilder.Column(ColumnBuilder(s_CheckpointContextTable_LastCheckpoint_Column, Type::Int)); createTableBuilder.EndColumns(); createTableBuilder.Execute(connection); savepoint.Commit(); } + std::optional CheckpointContextTable::SelectByContextId(const SQLite::Connection& connection, int contextId) + { + SQLite::Builder::StatementBuilder builder; + builder.Select(SQLite::RowIDName).From(s_CheckpointContextTable_Table_Name).Where(s_CheckpointContextTable_ContextId_Column); + builder.Equals(contextId); + + SQLite::Statement select = builder.Prepare(connection); + + if (select.Step()) + { + return select.GetColumn(0); + } + else + { + return {}; + } + } + bool CheckpointContextTable::ExistsById(const SQLite::Connection& connection, SQLite::rowid_t id) { SQLite::Builder::StatementBuilder builder; @@ -64,24 +83,6 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 return (countStatement.GetColumn(0) == 0); } - std::optional CheckpointContextTable::SelectByArgumentType(const SQLite::Connection& connection, int type) - { - SQLite::Builder::StatementBuilder builder; - builder.Select(SQLite::RowIDName).From(s_CheckpointContextTable_Table_Name).Where("id"); - builder.Equals(type); - - SQLite::Statement select = builder.Prepare(connection); - - if (select.Step()) - { - return select.GetColumn(0); - } - else - { - return {}; - } - } - SQLite::rowid_t CheckpointContextTable::AddContext(SQLite::Connection& connection, int contextId) { SQLite::Builder::StatementBuilder builder; @@ -93,10 +94,38 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 return connection.GetLastInsertRowID(); } - void CheckpointContextTable::RemoveContext(SQLite::Connection& connection, int contextId) + void CheckpointContextTable::RemoveContextById(SQLite::Connection& connection, SQLite::rowid_t id) + { + SQLite::Builder::StatementBuilder builder; + builder.DeleteFrom(s_CheckpointContextTable_Table_Name).Where(SQLite::RowIDName).Equals(id); + builder.Execute(connection); + } + + bool CheckpointContextTable::SetLastCheckpointById(SQLite::Connection& connection, SQLite::rowid_t id, int checkpointFlag) { SQLite::Builder::StatementBuilder builder; - builder.DeleteFrom(s_CheckpointContextTable_Table_Name).Where(s_CheckpointContextTable_ContextId_Column).Equals(contextId); + builder.Update(s_CheckpointContextTable_Table_Name).Set() + .Column(s_CheckpointContextTable_LastCheckpoint_Column).Equals(checkpointFlag) + .Where(SQLite::RowIDName).Equals(id); + builder.Execute(connection); + return connection.GetChanges() != 0; + } + + int CheckpointContextTable::GetLastCheckpointById(SQLite::Connection& connection, SQLite::rowid_t id) + { + SQLite::Builder::StatementBuilder builder; + builder.Select(s_CheckpointContextTable_LastCheckpoint_Column).From(s_CheckpointContextTable_Table_Name). + Where(SQLite::RowIDName).Equals(id); + + Statement statement = builder.Prepare(connection); + if (statement.Step()) + { + return statement.GetColumn(0); + } + else + { + return {}; + } } } \ No newline at end of file diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointContextTable.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointContextTable.h index 65f4de30b2..36d5b88730 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointContextTable.h +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointContextTable.h @@ -16,7 +16,7 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 // Creates the table with named indices. static void Create(SQLite::Connection& connection); - // Gets a value indicating whether the Checkpoint file with rowid id exists. + // Gets a value indicating whether a context with the given rowid exists. static bool ExistsById(const SQLite::Connection& connection, SQLite::rowid_t id); // Deletes the Checkpoint row with the given rowid @@ -25,13 +25,20 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 // Gets a value indicating whether the table is empty. static bool IsEmpty(SQLite::Connection& connection); - // Selects the command argument by type from the table, returning the rowid if it exists. - static std::optional SelectByArgumentType(const SQLite::Connection& connection, int type); + // Selects the context data by context id from the table, returning the rowid if it exists. + static std::optional SelectByContextId(const SQLite::Connection& connection, int contextId); // Adds a context row. static SQLite::rowid_t AddContext(SQLite::Connection& connection, int contextId); // Removes a context row. - static void RemoveContext(SQLite::Connection& connection, int contextId); + static void RemoveContextById(SQLite::Connection& connection, SQLite::rowid_t id); + + // Set the last checkpoint for a given context id. + static bool SetLastCheckpointById(SQLite::Connection& connection, SQLite::rowid_t id, int checkpointFlag); + + // Get the last checkpoint for a given context id. + static int GetLastCheckpointById(SQLite::Connection& connection, SQLite::rowid_t id); + }; } \ No newline at end of file diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointIndexInterface.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointIndexInterface.h index 1075886034..e9a03bc60c 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointIndexInterface.h +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointIndexInterface.h @@ -9,40 +9,25 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 { // Version 1.0 Schema::Version GetVersion() const override; - void CreateTables(SQLite::Connection& connection) override; + private: + bool IsEmpty(SQLite::Connection& connection) override; SQLite::rowid_t SetClientVersion(SQLite::Connection& connection, std::string_view clientVersion) override; - std::string GetClientVersion(SQLite::Connection& connection) override; - SQLite::rowid_t SetCommandName(SQLite::Connection& connection, int contextId, std::string_view clientVersion) override; - std::string GetCommandName(SQLite::Connection& connection, int contextId) override; - SQLite::rowid_t AddContextToArgumentTable(SQLite::Connection& connection, int contextId) override; - void RemoveContextFromArgumentTable(SQLite::Connection& connection, int contextId) override; - SQLite::rowid_t AddContextToContextTable(SQLite::Connection& connection, int contextId) override; - void RemoveContextFromContextTable(SQLite::Connection& connection, int contextId) override; - + bool ContainsArgument(SQLite::Connection& connection, int contextId, std::string_view name) override; bool UpdateArgumentByContextId(SQLite::Connection& connection, int contextId, std::string_view name, std::string_view value) override; - bool UpdateArgumentByContextId(SQLite::Connection& connection, int contextId, std::string_view name, bool value) override; - - std::vector GetAvailableArguments(SQLite::Connection& connection, int contextId) override; - - bool ContainsArgument(SQLite::Connection& connection, int contextId, std::string_view name) override; - std::string GetStringArgumentByContextId(SQLite::Connection& connection, int contextId, std::string_view name) override; - bool GetBoolArgumentByContextId(SQLite::Connection& connection, int contextId, std::string_view name) override; - int GetFirstContextId(SQLite::Connection& connection) override; - - private: - bool IsEmpty(SQLite::Connection& connection) override; + bool SetLastCheckpointByContextId(SQLite::Connection& connection, int contextId, int checkpointFlag) override; + int GetLastCheckpointByContextId(SQLite::Connection& connection, int contextId) override; }; } \ No newline at end of file diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointIndexInterface_1_0.cpp b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointIndexInterface_1_0.cpp index c557182682..d6c5503793 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointIndexInterface_1_0.cpp +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointIndexInterface_1_0.cpp @@ -10,13 +10,25 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 { namespace { - std::optional GetExistingCommandArgument(const SQLite::Connection& connection, uint32_t type) + std::optional GetExistingContextRowIdFromArgumentTable(const SQLite::Connection& connection, int contextId) { - auto result = CheckpointArgumentsTable::SelectByArgumentType(connection, type); + auto result = CheckpointArgumentsTable::SelectByContextId(connection, contextId); if (!result) { - AICLI_LOG(Repo, Verbose, << "Did not find an argument with the type { " << type << " }"); + AICLI_LOG(Repo, Verbose, << "Did not find a context with the contextId { " << contextId << " }"); + } + + return result; + } + + std::optional GetExistingContextRowIdFromContextTable(const SQLite::Connection& connection, int contextId) + { + auto result = CheckpointContextTable::SelectByContextId(connection, contextId); + + if (!result) + { + AICLI_LOG(Repo, Verbose, << "Did not find a context with the contextId { " << contextId << " }"); } return result; @@ -30,7 +42,7 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 void CheckpointIndexInterface::CreateTables(SQLite::Connection& connection) { - SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "createCheckpointTable_v1_0"); + SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "createCheckpointTables_v1_0"); Checkpoint_V1_0::CheckpointArgumentsTable::Create(connection); Checkpoint_V1_0::CheckpointContextTable::Create(connection); Checkpoint_V1_0::CheckpointMetadataTable::Create(connection); @@ -41,7 +53,6 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 { SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "setClientVersion_v1_0"); SQLite::rowid_t argumentId = CheckpointMetadataTable::SetClientVersion(connection, clientVersion); - savepoint.Commit(); return argumentId; } @@ -53,25 +64,32 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 SQLite::rowid_t CheckpointIndexInterface::SetCommandName(SQLite::Connection& connection, int contextId, std::string_view commandName) { - SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "setCommandName_v1_0"); - SQLite::rowid_t argumentId = CheckpointArgumentsTable::SetCommandName(connection, contextId, commandName); + auto contextResult = GetExistingContextRowIdFromArgumentTable(connection, contextId); + THROW_HR_IF(E_NOT_SET, !contextResult); + SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "setCommandName_v1_0"); + SQLite::rowid_t argumentId = CheckpointArgumentsTable::SetCommandNameById(connection, contextResult.value(), commandName); savepoint.Commit(); return argumentId; } std::string CheckpointIndexInterface::GetCommandName(SQLite::Connection& connection, int contextId) { - return CheckpointArgumentsTable::GetCommandName(connection, contextId); + auto contextResult = GetExistingContextRowIdFromArgumentTable(connection, contextId); + THROW_HR_IF(E_NOT_SET, !contextResult); + return CheckpointArgumentsTable::GetCommandNameById(connection, contextResult.value()); } bool CheckpointIndexInterface::IsEmpty(SQLite::Connection& connection) { - return CheckpointArgumentsTable::IsEmpty(connection); + return CheckpointArgumentsTable::IsEmpty(connection) && CheckpointContextTable::IsEmpty(connection); } SQLite::rowid_t CheckpointIndexInterface::AddContextToArgumentTable(SQLite::Connection& connection, int contextId) { + auto contextResult = GetExistingContextRowIdFromArgumentTable(connection, contextId); + THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS), contextResult.has_value()); + SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "addContextToArgTable_v1_0"); SQLite::rowid_t rowId = CheckpointArgumentsTable::AddContext(connection, contextId); savepoint.Commit(); @@ -80,13 +98,19 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 void CheckpointIndexInterface::RemoveContextFromArgumentTable(SQLite::Connection& connection, int contextId) { + auto contextResult = GetExistingContextRowIdFromArgumentTable(connection, contextId); + THROW_HR_IF(E_NOT_SET, !contextResult); + SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "removeContextFromArgTable_v1_0"); - CheckpointArgumentsTable::RemoveContext(connection, contextId); + CheckpointArgumentsTable::RemoveContextById(connection, contextResult.value()); savepoint.Commit(); } SQLite::rowid_t CheckpointIndexInterface::AddContextToContextTable(SQLite::Connection& connection, int contextId) { + auto contextResult = GetExistingContextRowIdFromContextTable(connection, contextId); + THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS), contextResult.has_value()); + SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "addContextToContextTable_v1_0"); SQLite::rowid_t rowId = CheckpointContextTable::AddContext(connection, contextId); savepoint.Commit(); @@ -95,32 +119,36 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 void CheckpointIndexInterface::RemoveContextFromContextTable(SQLite::Connection& connection, int contextId) { + auto contextResult = GetExistingContextRowIdFromArgumentTable(connection, contextId); + THROW_HR_IF(E_NOT_SET, !contextResult); + SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "removeContextFromContextTable_v1_0"); - CheckpointContextTable::RemoveContext(connection, contextId); + CheckpointContextTable::RemoveContextById(connection, contextId); savepoint.Commit(); } bool CheckpointIndexInterface::UpdateArgumentByContextId(SQLite::Connection& connection, int contextId, std::string_view name, std::string_view value) { + auto contextResult = GetExistingContextRowIdFromArgumentTable(connection, contextId); + THROW_HR_IF(E_NOT_SET, !contextResult); + SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "updateContextArgument_v1_0"); - bool status = CheckpointArgumentsTable::UpdateArgumentByContextId(connection, contextId, name, value); + bool status = CheckpointArgumentsTable::UpdateArgumentById(connection, contextResult.value(), name, value); savepoint.Commit(); return status; } bool CheckpointIndexInterface::UpdateArgumentByContextId(SQLite::Connection& connection, int contextId, std::string_view name, bool value) { + auto contextResult = GetExistingContextRowIdFromArgumentTable(connection, contextId); + THROW_HR_IF(E_NOT_SET, !contextResult); + SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "updateContextArgument_v1_0"); - bool status = CheckpointArgumentsTable::UpdateArgumentByContextId(connection, contextId, name, value); + bool status = CheckpointArgumentsTable::UpdateArgumentById(connection, contextResult.value(), name, value); savepoint.Commit(); return status; } - std::vector CheckpointIndexInterface::GetAvailableArguments(SQLite::Connection& connection, int contextId) - { - return CheckpointArgumentsTable::GetAvailableArguments(connection, contextId); - } - bool CheckpointIndexInterface::ContainsArgument(SQLite::Connection& connection, int contextId, std::string_view name) { return CheckpointArgumentsTable::ContainsArgument(connection, contextId, name); @@ -128,16 +156,41 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 std::string CheckpointIndexInterface::GetStringArgumentByContextId(SQLite::Connection& connection, int contextId, std::string_view name) { - return CheckpointArgumentsTable::GetStringArgumentByContextId(connection, contextId, name); + auto contextResult = GetExistingContextRowIdFromArgumentTable(connection, contextId); + THROW_HR_IF(E_NOT_SET, !contextResult); + + return CheckpointArgumentsTable::GetStringArgumentById(connection, contextResult.value(), name); } bool CheckpointIndexInterface::GetBoolArgumentByContextId(SQLite::Connection& connection, int contextId, std::string_view name) { - return CheckpointArgumentsTable::GetBoolArgumentByContextId(connection, contextId, name); + auto contextResult = GetExistingContextRowIdFromArgumentTable(connection, contextId); + THROW_HR_IF(E_NOT_SET, !contextResult); + + return CheckpointArgumentsTable::GetBoolArgumentById(connection, contextResult.value(), name); } int CheckpointIndexInterface::GetFirstContextId(SQLite::Connection& connection) { return CheckpointArgumentsTable::GetFirstContextId(connection); } + + bool CheckpointIndexInterface::SetLastCheckpointByContextId(SQLite::Connection& connection, int contextId, int checkpointFlag) + { + auto contextResult = GetExistingContextRowIdFromContextTable(connection, contextId); + THROW_HR_IF(E_NOT_SET, !contextResult); + + SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "updateLastCheckpointFlag_v1_0"); + bool status = CheckpointContextTable::SetLastCheckpointById(connection, contextResult.value(), checkpointFlag); + savepoint.Commit(); + return status; + } + + int CheckpointIndexInterface::GetLastCheckpointByContextId(SQLite::Connection& connection, int contextId) + { + auto contextResult = GetExistingContextRowIdFromContextTable(connection, contextId); + THROW_HR_IF(E_NOT_SET, !contextResult); + + return CheckpointContextTable::GetLastCheckpointById(connection, contextResult.value()); + } } \ No newline at end of file diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/ICheckpointIndex.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/ICheckpointIndex.h index ec61769814..1c1dde33f8 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/Schema/ICheckpointIndex.h +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/ICheckpointIndex.h @@ -17,36 +17,55 @@ namespace AppInstaller::Repository::Microsoft::Schema virtual void CreateTables(SQLite::Connection& connection) = 0; // Version 1.0 + // Returns a bool value indicating whether all checkpoint tables are empty. + virtual bool IsEmpty(SQLite::Connection& connection) = 0; + + // Sets the client version associated with this checkpoint index. virtual SQLite::rowid_t SetClientVersion(SQLite::Connection& connection, std::string_view clientVersion) = 0; + // Gets the client version associated with this checkpoint index. virtual std::string GetClientVersion(SQLite::Connection& connection) = 0; + // Sets the command name for a given context id. virtual SQLite::rowid_t SetCommandName(SQLite::Connection& connection, int contextId, std::string_view commandName) = 0; + // Gets the command name for a given context id. virtual std::string GetCommandName(SQLite::Connection& connection, int contextId) = 0; - virtual bool IsEmpty(SQLite::Connection& connection) = 0; - + // Adds a new row with the given context id to the checkpoint argument table. virtual SQLite::rowid_t AddContextToArgumentTable(SQLite::Connection& connection, int contextId) = 0; + // removes the given context id from the checkpoint argument table. + virtual void RemoveContextFromArgumentTable(SQLite::Connection& connection, int contextId) = 0; + + // Adds a new row with the given context id to the checkpoint context table. virtual SQLite::rowid_t AddContextToContextTable(SQLite::Connection& connection, int contextId) = 0; + // Removes the given context id from the checkpoint context table. virtual void RemoveContextFromContextTable(SQLite::Connection& connection, int contextId) = 0; - virtual void RemoveContextFromArgumentTable(SQLite::Connection& connection, int contextId) = 0; + // Returns a boolean value indicating whether an argument exists for a given context id. + virtual bool ContainsArgument(SQLite::Connection& connection, int contextId, std::string_view name) = 0; + // Updates the boolean value of an argument for a given context id. virtual bool UpdateArgumentByContextId(SQLite::Connection& connection, int contextId, std::string_view name, std::string_view value) = 0; + // Updates the string value of an argument for a given context id. virtual bool UpdateArgumentByContextId(SQLite::Connection& connection, int contextId, std::string_view name, bool value) = 0; - virtual std::vector GetAvailableArguments(SQLite::Connection& connection, int contextId) = 0; - - virtual bool ContainsArgument(SQLite::Connection& connection, int contextId, std::string_view name) = 0; - + // Gets the string value of an argument for a given context id. virtual std::string GetStringArgumentByContextId(SQLite::Connection& connection, int contextId, std::string_view name) = 0; + // Gets the boolean value of an argument for a given context id. virtual bool GetBoolArgumentByContextId(SQLite::Connection& connection, int contextId, std::string_view name) = 0; + // Gets the first context id in the checkpoint table. virtual int GetFirstContextId(SQLite::Connection& connection) = 0; + + // Sets the last checkpoint for a given context id. + virtual bool SetLastCheckpointByContextId(SQLite::Connection& connection, int contextId, int checkpointFlag) = 0; + + // Gets the last checkpoint for a given context id. + virtual int GetLastCheckpointByContextId(SQLite::Connection& connection, int contextId) = 0; }; } \ No newline at end of file From 0f5ce842dfb6a2e4c32a57f6f0d431c3f6d04563 Mon Sep 17 00:00:00 2001 From: Ryan Fu Date: Wed, 2 Aug 2023 23:30:59 -0700 Subject: [PATCH 08/43] add initial unit tests --- src/AppInstallerCLICore/CheckpointManager.cpp | 196 ++++++------- src/AppInstallerCLICore/CheckpointManager.h | 9 +- .../Commands/InstallCommand.cpp | 9 +- .../Commands/ResumeCommand.cpp | 7 +- src/AppInstallerCLICore/ExecutionContext.h | 18 +- .../Workflows/WorkflowBase.cpp | 9 +- .../AppInstallerCLITests.vcxproj | 2 + .../AppInstallerCLITests.vcxproj.filters | 6 + src/AppInstallerCLITests/CheckpointIndex.cpp | 260 ++++++++++++++++++ src/AppInstallerCLITests/ResumeFlow.cpp | 75 +++++ src/AppInstallerCLITests/TestHooks.h | 14 + .../Public/AppInstallerRuntime.h | 2 +- src/AppInstallerCommonCore/Runtime.cpp | 1 + .../Microsoft/CheckpointIndex.cpp | 55 +++- .../Microsoft/CheckpointIndex.h | 3 + .../CheckpointArgumentsTable.cpp | 1 + .../CheckpointIndexInterface_1_0.cpp | 9 +- 17 files changed, 525 insertions(+), 151 deletions(-) create mode 100644 src/AppInstallerCLITests/CheckpointIndex.cpp create mode 100644 src/AppInstallerCLITests/ResumeFlow.cpp diff --git a/src/AppInstallerCLICore/CheckpointManager.cpp b/src/AppInstallerCLICore/CheckpointManager.cpp index af387d7b4e..4a0348f8a4 100644 --- a/src/AppInstallerCLICore/CheckpointManager.cpp +++ b/src/AppInstallerCLICore/CheckpointManager.cpp @@ -10,64 +10,48 @@ namespace AppInstaller::CLI::Checkpoint { - bool CheckpointManager::CleanUpIndex() + void CheckpointManager::Initialize(GUID checkpointId) { - // Check if index is empty, if so, remove file and return status. - if (m_checkpointIndex->IsEmpty()) + // Initialize should only be called once. + THROW_HR_IF(E_UNEXPECTED, m_checkpointId != GUID_NULL); + + if (checkpointId == GUID_NULL) { - // Remove file here if it is empty. + std::ignore = CoCreateGuid(&m_checkpointId); + AICLI_LOG(CLI, Info, << "Creating checkpoint index with guid: " << m_checkpointId); } - - return true; - } - - CheckpointManager::~CheckpointManager() - { - CleanUpIndex(); - } - - void CheckpointManager::Initialize() - { - // Initialize should not be called more than once. - if (m_checkpointId != GUID_NULL) + else { - return; + m_checkpointId = checkpointId; + AICLI_LOG(CLI, Info, << "Opening checkpoint index with guid: " << m_checkpointId); } - std::ignore = CoCreateGuid(&m_checkpointId); - AICLI_LOG(CLI, Info, << "Creating checkpoint index with the corresponding guid: " << m_checkpointId); - - //auto openDisposition = m_readOnly ? SQLiteStorageBase::OpenDisposition::Read : SQLiteStorageBase::OpenDisposition::ReadWrite; auto openDisposition = AppInstaller::Repository::Microsoft::SQLiteStorageBase::OpenDisposition::ReadWrite; auto checkpointIndex = AppInstaller::Repository::Microsoft::CheckpointIndex::OpenOrCreateDefault(m_checkpointId, openDisposition); if (!checkpointIndex) { AICLI_LOG(CLI, Error, << "Unable to open checkpoint index."); + THROW_HR_MSG(HRESULT_FROM_WIN32(ERROR_NOT_FOUND), "The saved state could not be found."); } m_checkpointIndex = std::move(checkpointIndex); m_checkpointIndex->SetClientVersion(AppInstaller::Runtime::GetClientVersion()); } - void CheckpointManager::InitializeFromGuid(GUID checkpointId) + void CheckpointManager::Checkpoint(Execution::Context& context, Execution::CheckpointFlag checkpointFlag) { - // Initialize should not be called more than once. - if (m_checkpointId != GUID_NULL) + Execution::CheckpointFlag contextCheckpointFlag = context.GetTargetCheckpoint(); + + // If the context target checkpoint is ahead or equal to the current checkpoint, we have previously executed this state, load checkpoint state from index. + // If the context target checkpoint is behind the current checkpoint, we have not yet processed this state, save checkpoint state to index. + if (contextCheckpointFlag > checkpointFlag || contextCheckpointFlag == checkpointFlag) { - return; + LoadCheckpoint(context, checkpointFlag); } - // THROW_HR_IF(E_UNEXPECTED, m_checkpointId != GUID_NULL); - - m_checkpointId = checkpointId; - auto openDisposition = AppInstaller::Repository::Microsoft::SQLiteStorageBase::OpenDisposition::ReadWrite; - auto checkpointIndex = AppInstaller::Repository::Microsoft::CheckpointIndex::OpenOrCreateDefault(m_checkpointId, openDisposition); - if (!checkpointIndex) + else if (contextCheckpointFlag < checkpointFlag) { - AICLI_LOG(CLI, Error, << "Unable to open checkpoint index."); - THROW_HR_MSG(HRESULT_FROM_WIN32(ERROR_NOT_FOUND), "The saved state could not be found."); + SaveCheckpoint(context, checkpointFlag); } - - m_checkpointIndex = std::move(checkpointIndex); } void CheckpointManager::AddContext(int contextId) @@ -80,12 +64,75 @@ namespace AppInstaller::CLI::Checkpoint m_checkpointIndex->RemoveContext(contextId); } - std::unique_ptr CheckpointManager::CreateContextFromCheckpointIndex() + std::string CheckpointManager::GetClientVersion() + { + // Need to throw exception if checkpoint index is null. + return m_checkpointIndex->GetClientVersion(); + } + + std::string CheckpointManager::GetCommandName(int contextId) + { + return m_checkpointIndex->GetCommandName(contextId); + } + + int CheckpointManager::GetFirstContextId() + { + return m_checkpointIndex->GetFirstContextId(); + } + + Execution::CheckpointFlag CheckpointManager::GetLastCheckpoint(int contextId) + { + return static_cast(m_checkpointIndex->GetLastCheckpoint(contextId)); + } + + bool CheckpointManager::CleanUpIndex() + { + if (m_checkpointIndex->IsEmpty()) + { + m_checkpointIndex.reset(); + + const auto& checkpointIndexPath = AppInstaller::Repository::Microsoft::CheckpointIndex::GetCheckpointIndexPath(m_checkpointId); + if (std::filesystem::remove(checkpointIndexPath)) + { + AICLI_LOG(CLI, Info, << "Checkpoint index deleted: " << checkpointIndexPath); + } + } + + return true; + } + + CheckpointManager::~CheckpointManager() + { + CleanUpIndex(); + } + + void CheckpointManager::SaveCheckpoint(Execution::Context& context, Execution::CheckpointFlag flag) + { + switch (flag) + { + case Execution::CheckpointFlag::CommandArguments: + RecordContextArgsToIndex(context); + break; + default: + THROW_HR(E_UNEXPECTED); + } + + m_checkpointIndex->SetLastCheckpoint(context.GetContextId(), static_cast(flag)); + context.SetCurrentCheckpoint(flag); + } + + void CheckpointManager::LoadCheckpoint(Execution::Context& context, Execution::CheckpointFlag flag) { - auto checkpointContext = std::make_unique(std::cout, std::cin); - auto previousthreadGlobals = checkpointContext->SetForCurrentThread(); - checkpointContext->EnableSignalTerminationHandler(); - return checkpointContext; + switch (flag) + { + case Execution::CheckpointFlag::CommandArguments: + PopulateContextArgsFromIndex(context); + break; + default: + THROW_HR(E_UNEXPECTED); + } + + context.SetCurrentCheckpoint(flag); } void CheckpointManager::PopulateContextArgsFromIndex(Execution::Context& context) @@ -145,71 +192,4 @@ namespace AppInstaller::CLI::Checkpoint } } } - - void CheckpointManager::Checkpoint(Execution::Context& context, Execution::CheckpointFlag checkpointFlag) - { - Execution::CheckpointFlag contextCheckpointFlag = context.GetCurrentCheckpoint(); - - // If the context is ahead of the current checkpoint, we have previously executed this state, load checkpoint state from index. - // If the context is behind the current checkpoint, we have not yet processed this state, save checkpoint state to index. - // If the checkpoints are equal, do nothing. - if (contextCheckpointFlag > checkpointFlag) - { - LoadCheckpoint(context, checkpointFlag); - } - else if (contextCheckpointFlag < checkpointFlag) - { - SaveCheckpoint(context, checkpointFlag); - } - } - - void CheckpointManager::SaveCheckpoint(Execution::Context& context, Execution::CheckpointFlag flag) - { - switch (flag) - { - case Execution::CheckpointFlag::CommandArguments: - RecordContextArgsToIndex(context); - break; - default: - THROW_HR(E_UNEXPECTED); - } - - m_checkpointIndex->SetLastCheckpoint(context.GetContextId(), static_cast(flag)); - context.SetCurrentCheckpoint(flag); - } - - void CheckpointManager::LoadCheckpoint(Execution::Context& context, Execution::CheckpointFlag flag) - { - switch (flag) - { - case Execution::CheckpointFlag::CommandArguments: - PopulateContextArgsFromIndex(context); - break; - default: - THROW_HR(E_UNEXPECTED); - } - - context.SetCurrentCheckpoint(flag); - } - - std::string CheckpointManager::GetClientVersion() - { - // Need to throw exception if checkpoint index is null. - return m_checkpointIndex->GetClientVersion(); - } - - std::string CheckpointManager::GetCommandName(int contextId) - { - return m_checkpointIndex->GetCommandName(contextId); - } - - int CheckpointManager::GetFirstContextId() - { - return m_checkpointIndex->GetFirstContextId(); - } - - Execution::CheckpointFlag CheckpointManager::GetLastCheckpoint(int contextId) - { - return static_cast(m_checkpointIndex->GetLastCheckpoint(contextId)); - } } diff --git a/src/AppInstallerCLICore/CheckpointManager.h b/src/AppInstallerCLICore/CheckpointManager.h index 5d06f29cd5..a081d95cf6 100644 --- a/src/AppInstallerCLICore/CheckpointManager.h +++ b/src/AppInstallerCLICore/CheckpointManager.h @@ -25,18 +25,14 @@ namespace AppInstaller::CLI::Checkpoint return checkpointManager; } - void Initialize(); + void Initialize(GUID checkpointId = {}); - void InitializeFromGuid(GUID checkpointId); + void Checkpoint(Execution::Context& context, Execution::CheckpointFlag flag); void AddContext(int contextId); void RemoveContext(int contextId); - void Checkpoint(Execution::Context& context, Execution::CheckpointFlag flag); - - std::unique_ptr CreateContextFromCheckpointIndex(); - std::string GetClientVersion(); std::string GetCommandName(int contextId); @@ -60,4 +56,3 @@ namespace AppInstaller::CLI::Checkpoint void RecordContextArgsToIndex(Execution::Context& context); }; } -// \ No newline at end of file diff --git a/src/AppInstallerCLICore/Commands/InstallCommand.cpp b/src/AppInstallerCLICore/Commands/InstallCommand.cpp index 368128b5fb..08dfba68f9 100644 --- a/src/AppInstallerCLICore/Commands/InstallCommand.cpp +++ b/src/AppInstallerCLICore/Commands/InstallCommand.cpp @@ -105,7 +105,9 @@ namespace AppInstaller::CLI void InstallCommand::ExecuteInternal(Context& context) const { - if (Settings::ExperimentalFeature::IsEnabled(Settings::ExperimentalFeature::Feature::Resume)) + bool resumeExperimentalFeatureEnabled = Settings::ExperimentalFeature::IsEnabled(Settings::ExperimentalFeature::Feature::Resume); + + if (resumeExperimentalFeatureEnabled && WI_IsFlagClear(context.GetFlags(), Execution::ContextFlag::Resume)) { CheckpointManager::Instance().Initialize(); CheckpointManager::Instance().AddContext(context.GetContextId()); @@ -151,9 +153,10 @@ namespace AppInstaller::CLI } } - if (Settings::ExperimentalFeature::IsEnabled(Settings::ExperimentalFeature::Feature::Resume)) + if (resumeExperimentalFeatureEnabled) { - if (context.IsTerminated()) + // TODO: Only allow certain termination HRs to persist the checkpoint index. + if (!context.IsTerminated()) { CheckpointManager::Instance().RemoveContext(context.GetContextId()); } diff --git a/src/AppInstallerCLICore/Commands/ResumeCommand.cpp b/src/AppInstallerCLICore/Commands/ResumeCommand.cpp index 3dd0993f15..e87b3869ea 100644 --- a/src/AppInstallerCLICore/Commands/ResumeCommand.cpp +++ b/src/AppInstallerCLICore/Commands/ResumeCommand.cpp @@ -39,7 +39,7 @@ namespace AppInstaller::CLI { GUID checkpointId = Utility::ConvertToGuid(std::string{ context.Args.GetArg(Execution::Args::Type::ResumeGuid) }); - CheckpointManager::Instance().InitializeFromGuid(checkpointId); + CheckpointManager::Instance().Initialize(checkpointId); if (AppInstaller::Runtime::GetClientVersion().get() != CheckpointManager::Instance().GetClientVersion()) { @@ -70,8 +70,9 @@ namespace AppInstaller::CLI auto resumeContext = context.CreateEmptyContext(rootContextId); resumeContext->SetExecutingCommand(commandToResume.get()); - // Set the current checkpoint of the root context. - resumeContext->SetCurrentCheckpoint(CheckpointManager::Instance().GetLastCheckpoint(rootContextId)); + // Set the target checkpoint of the root context. + resumeContext->SetTargetCheckpoint(CheckpointManager::Instance().GetLastCheckpoint(rootContextId)); + resumeContext->SetFlags(Execution::ContextFlag::Resume); // Load the arguments from the checkpoint index prior to executing the command. CheckpointManager::Instance().Checkpoint(*resumeContext, Execution::CheckpointFlag::CommandArguments); diff --git a/src/AppInstallerCLICore/ExecutionContext.h b/src/AppInstallerCLICore/ExecutionContext.h index 8eff68ac5e..38e7a1c5c1 100644 --- a/src/AppInstallerCLICore/ExecutionContext.h +++ b/src/AppInstallerCLICore/ExecutionContext.h @@ -72,6 +72,7 @@ namespace AppInstaller::CLI::Execution DisableInteractivity = 0x40, BypassIsStoreClientBlockedPolicyCheck = 0x80, InstallerDownloadOnly = 0x100, + Resume = 0x200 }; DEFINE_ENUM_FLAG_OPERATORS(ContextFlag); @@ -169,23 +170,20 @@ namespace AppInstaller::CLI::Execution // Enable tests to override behavior bool ShouldExecuteWorkflowTask(const Workflow::WorkflowTask& task); #endif - // Sets the target checkpoint of the context. - void SetTargetCheckpoint(CheckpointFlag flag) { m_targetCheckpoint = flag; }; + // Gets the id of the context. + int GetContextId() { return m_contextId; }; // Gets the current checkpoint of the context. CheckpointFlag GetCurrentCheckpoint() { return m_currentCheckpoint; }; + // Sets the current checkpoint flag of the context. + void SetCurrentCheckpoint(Execution::CheckpointFlag flag) { m_currentCheckpoint = flag; } + // Gets the target checkpoint of the context. CheckpointFlag GetTargetCheckpoint() { return m_targetCheckpoint; }; - // Sets the current checkpoint flag of the context. - void SetCurrentCheckpoint(Execution::CheckpointFlag flag) - { - m_currentCheckpoint = flag; - } - - // Gets the id of the context. - int GetContextId() { return m_contextId; }; + // Sets the target checkpoint of the context. + void SetTargetCheckpoint(CheckpointFlag flag) { m_targetCheckpoint = flag; }; protected: // Copies the args that are also needed in a sub-context. E.g., silent diff --git a/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp b/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp index 6c84ab94a2..d0597aaaee 100644 --- a/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp +++ b/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp @@ -1275,11 +1275,10 @@ AppInstaller::CLI::Execution::Context& operator<<(AppInstaller::CLI::Execution:: AppInstaller::CLI::Execution::Context& operator<<(AppInstaller::CLI::Execution::Context& context, const AppInstaller::CLI::Workflow::WorkflowTask& task) { - //// Maybe add a should we execute workflow check using checkpoint. - //if (context.GetCurrentCheckpoint() != context.GetTargetCheckpoint()) - //{ - // return context; - //} + if (context.GetCurrentCheckpoint() < context.GetTargetCheckpoint()) + { + return context; + } if (!context.IsTerminated()) { diff --git a/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj b/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj index 82e6059dcc..f3bd238a2f 100644 --- a/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj +++ b/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj @@ -198,6 +198,7 @@ + @@ -243,6 +244,7 @@ + diff --git a/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj.filters b/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj.filters index 6618fcdd05..e1525595e1 100644 --- a/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj.filters +++ b/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj.filters @@ -182,6 +182,9 @@ Source Files\Repository + + Source Files\CLI + Source Files\Repository @@ -308,6 +311,9 @@ Source Files\CLI + + Source Files\Repository + diff --git a/src/AppInstallerCLITests/CheckpointIndex.cpp b/src/AppInstallerCLITests/CheckpointIndex.cpp new file mode 100644 index 0000000000..27b9e61058 --- /dev/null +++ b/src/AppInstallerCLITests/CheckpointIndex.cpp @@ -0,0 +1,260 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#include "pch.h" +#include "TestCommon.h" +#include +#include + +using namespace std::string_literals; +using namespace TestCommon; +using namespace AppInstaller::CLI; +using namespace AppInstaller::Repository::Microsoft; +using namespace AppInstaller::Repository::SQLite; +using namespace AppInstaller::Repository::Microsoft::Schema; + +constexpr std::string_view s_ArgumentName_Id = "id"sv; +constexpr std::string_view s_ArgumentName_Version = "version"sv; +constexpr std::string_view s_ArgumentName_AcceptPackageAgreements = "accept-package-agreements"sv; +constexpr std::string_view s_ArgumentName_HashOverride = "ignore-security-hash"sv; + +TEST_CASE("CheckpointIndexCreateLatestAndReopen", "[checkpointIndex]") +{ + TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; + INFO("Using temporary file named: " << tempFile.GetPath()); + + Schema::Version versionCreated; + + // Create the index + { + CheckpointIndex index = CheckpointIndex::CreateNew(tempFile, Schema::Version::Latest()); + versionCreated = index.GetVersion(); + } + + // Reopen the index + { + INFO("Trying with ReadWrite"); + CheckpointIndex index = CheckpointIndex::Open(tempFile, SQLiteStorageBase::OpenDisposition::ReadWrite); + Schema::Version versionRead = index.GetVersion(); + REQUIRE(versionRead == versionCreated); + } +} + +TEST_CASE("CheckpointIndexAddContext", "[checkpointIndex]") +{ + TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; + INFO("Using temporary file named: " << tempFile.GetPath()); + + int testContextId = 0; + std::string_view testCommand = "install"sv; + std::string_view testClientVersion = "1.20.1234"sv; + + { + CheckpointIndex index = CheckpointIndex::CreateNew(tempFile, { 1, 0 }); + REQUIRE(index.IsEmpty()); + index.AddContext(testContextId); + index.SetCommandName(testContextId, testCommand); + index.SetClientVersion(testClientVersion); + } + + { + CheckpointIndex index = CheckpointIndex::Open(tempFile, SQLiteStorageBase::OpenDisposition::ReadWrite); + int firstContextId = index.GetFirstContextId(); + REQUIRE(testContextId == firstContextId); + REQUIRE(testCommand == index.GetCommandName(firstContextId)); + REQUIRE(testClientVersion == index.GetClientVersion()); + + index.RemoveContext(firstContextId); + REQUIRE(index.IsEmpty()); + } +} + +TEST_CASE("CheckpointIndex_ArgumentsTable_AddRemoveArguments", "[checkpointIndex]") +{ + TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; + INFO("Using temporary file named: " << tempFile.GetPath()); + + int testContextId = 0; + std::string_view testPackageIdentifier = "AppInstallerTest.TestExeInstaller"sv; + std::string_view testPackageVersion = "1.2.3.4"sv; + bool testAcceptPackageAgreements = true; + bool testHashOverride = true; + + { + CheckpointIndex index = CheckpointIndex::CreateNew(tempFile, { 1, 0 }); + REQUIRE(index.IsEmpty()); + index.AddContext(testContextId); + + // Verify arguments do not exist. + REQUIRE_FALSE(index.ContainsArgument(testContextId, s_ArgumentName_Id)); + REQUIRE_FALSE(index.ContainsArgument(testContextId, s_ArgumentName_Version)); + REQUIRE_FALSE(index.ContainsArgument(testContextId, s_ArgumentName_AcceptPackageAgreements)); + REQUIRE_FALSE(index.ContainsArgument(testContextId, s_ArgumentName_HashOverride)); + + // Add valid context arguments; + REQUIRE(index.UpdateArgument(testContextId, s_ArgumentName_Id, testPackageIdentifier)); + REQUIRE(index.UpdateArgument(testContextId, s_ArgumentName_Version, testPackageVersion)); + REQUIRE(index.UpdateArgument(testContextId, s_ArgumentName_AcceptPackageAgreements, testAcceptPackageAgreements)); + REQUIRE(index.UpdateArgument(testContextId, s_ArgumentName_HashOverride, testHashOverride)); + } + + { + // Open index and verify values. + CheckpointIndex index = CheckpointIndex::Open(tempFile, SQLiteStorageBase::OpenDisposition::ReadWrite); + int firstContextId = index.GetFirstContextId(); + + // Verify arguments exist. + REQUIRE(index.ContainsArgument(testContextId, s_ArgumentName_Id)); + REQUIRE(index.ContainsArgument(testContextId, s_ArgumentName_Version)); + REQUIRE(index.ContainsArgument(testContextId, s_ArgumentName_AcceptPackageAgreements)); + REQUIRE(index.ContainsArgument(testContextId, s_ArgumentName_HashOverride)); + + // Verify context arguments were added. + REQUIRE(testPackageIdentifier == index.GetStringArgument(testContextId, "id"sv)); + REQUIRE(testPackageVersion == index.GetStringArgument(testContextId, "version"sv)); + REQUIRE(testAcceptPackageAgreements == index.GetBoolArgument(testContextId, "accept-package-agreements"sv)); + REQUIRE(testHashOverride == index.GetBoolArgument(testContextId, "ignore-security-hash"sv)); + + index.RemoveContext(firstContextId); + REQUIRE(index.IsEmpty()); + } +} + +TEST_CASE("CheckpointIndex_ContextTable_AddRemoveContext", "[checkpointIndex]") +{ + TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; + INFO("Using temporary file named: " << tempFile.GetPath()); + + int testContextId = 0; + + { + CheckpointIndex index = CheckpointIndex::CreateNew(tempFile, { 1, 0 }); + REQUIRE(index.IsEmpty()); + index.AddContext(testContextId); + index.SetLastCheckpoint(testContextId, 0); + } + + { + CheckpointIndex index = CheckpointIndex::Open(tempFile, SQLiteStorageBase::OpenDisposition::ReadWrite); + REQUIRE(index.GetLastCheckpoint(testContextId) == 0); + index.SetLastCheckpoint(testContextId, 1); + REQUIRE(index.GetLastCheckpoint(testContextId) == 1); + + index.RemoveContext(testContextId); + REQUIRE(index.IsEmpty()); + } +} + +TEST_CASE("CheckpointIndex_VerifyContextOrder", "[checkpointIndex]") +{ + // Verify that the context id order is retrieved in order regardless of what sequence they are added. + TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; + INFO("Using temporary file named: " << tempFile.GetPath()); + + { + CheckpointIndex index = CheckpointIndex::CreateNew(tempFile, { 1, 0 }); + REQUIRE(index.IsEmpty()); + index.AddContext(3); + index.AddContext(1); + index.AddContext(0); + index.AddContext(2); + } + + { + CheckpointIndex index = CheckpointIndex::Open(tempFile, SQLiteStorageBase::OpenDisposition::ReadWrite); + REQUIRE(index.GetFirstContextId() == 0); + index.RemoveContext(0); + REQUIRE(index.GetFirstContextId() == 1); + index.RemoveContext(1); + REQUIRE(index.GetFirstContextId() == 2); + + // The value should persist if no context was removed. + REQUIRE(index.GetFirstContextId() == 2); + index.RemoveContext(2); + REQUIRE(index.GetFirstContextId() == 3); + index.RemoveContext(3); + + REQUIRE(index.IsEmpty()); + } +} + +std::vector GetBoolArgsFromCommand(Command command) +{ + const auto& commandArguments = command.GetArguments(); + std::vector boolArguments; + + for (const auto& argument : commandArguments) + { + if (argument.Type() == ArgumentType::Flag) + { + boolArguments.emplace_back(argument.Name()); + } + } + + return boolArguments; +} + +std::vector GetStringArgsFromCommand(Command command) +{ + const auto& commandArguments = command.GetArguments(); + std::vector stringArguments; + + for (const auto& argument : commandArguments) + { + if (argument.Type() != ArgumentType::Flag) + { + stringArguments.emplace_back(argument.Name()); + } + } + + return stringArguments; +} + +TEST_CASE("CheckpointIndex_ArgumentTable_VerifyAllArguments_InstallCommand", "[checkpointIndex]") +{ + TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; + INFO("Using temporary file named: " << tempFile.GetPath()); + + int testContextId = 0; + + InstallCommand installCommand({}); + std::vector boolArguments = GetBoolArgsFromCommand(installCommand); + std::vector stringArguments = GetStringArgsFromCommand(installCommand); + + { + CheckpointIndex index = CheckpointIndex::CreateNew(tempFile, { 1, 0 }); + index.AddContext(testContextId); + + for (const auto& argName : boolArguments) + { + REQUIRE_FALSE(index.ContainsArgument(testContextId, argName)); + REQUIRE(index.UpdateArgument(testContextId, argName, true)); + } + + std::string_view testValue = "testValue"sv; + for (const auto& argName : stringArguments) + { + REQUIRE_FALSE(index.ContainsArgument(testContextId, argName)); + REQUIRE(index.UpdateArgument(testContextId, argName, testValue)); + } + } + + { + CheckpointIndex index = CheckpointIndex::Open(tempFile, SQLiteStorageBase::OpenDisposition::ReadWrite); + + for (const auto& argName : boolArguments) + { + REQUIRE(index.ContainsArgument(testContextId, argName)); + REQUIRE(index.GetBoolArgument(testContextId, argName)); + } + + std::string_view testValue = "testValue"sv; + for (const auto& argName : stringArguments) + { + REQUIRE(index.ContainsArgument(testContextId, argName)); + REQUIRE(testValue == index.GetStringArgument(testContextId, argName)); + } + + index.RemoveContext(testContextId); + REQUIRE(index.IsEmpty()); + } +} diff --git a/src/AppInstallerCLITests/ResumeFlow.cpp b/src/AppInstallerCLITests/ResumeFlow.cpp new file mode 100644 index 0000000000..40bf3e9f6c --- /dev/null +++ b/src/AppInstallerCLITests/ResumeFlow.cpp @@ -0,0 +1,75 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#include "pch.h" +#include "WorkflowCommon.h" +#include "TestHooks.h" +#include +#include +#include +#include +#include + +using namespace AppInstaller::CLI; +using namespace AppInstaller::Repository::Microsoft; +using namespace AppInstaller::Settings; +using namespace TestCommon; + +TEST_CASE("Resume_VerifyIndexCreatedForTerminatedWorkflow", "[Resume]") +{ + // TODO: Update test when only certain termination HRs persist the checkpoint index. + + TestCommon::TempDirectory tempCheckpointIndexDirectory("TempCheckpointIndexDirectory", false); + const auto& tempCheckpointIndexDirectoryPath = tempCheckpointIndexDirectory.GetPath(); + TestHook::SetCheckpointIndexDirectory_Override checkpointIndexDirectoryOverride(tempCheckpointIndexDirectoryPath); + + TestCommon::TempDirectory tempDirectory("TempDirectory", false); + + TestCommon::TestUserSettings testSettings; + testSettings.Set(true); + + std::ostringstream installOutput; + TestContext context{ installOutput, std::cin }; + auto previousThreadGlobals = context.SetForCurrentThread(); + + const auto& testManifestPath = TestDataFile("InstallFlowTest_UnsupportedArguments.yaml").GetPath().u8string(); + context.Args.AddArg(Execution::Args::Type::Manifest, testManifestPath); + context.Args.AddArg(Execution::Args::Type::InstallLocation, tempDirectory); + + InstallCommand install({}); + context.SetExecutingCommand(&install); + install.Execute(context); + + // There should only be one file in the temp directory. + std::filesystem::path checkpointIndexPath; + for (const auto& entry : std::filesystem::directory_iterator(tempCheckpointIndexDirectoryPath)) + { + checkpointIndexPath = entry.path(); + } + + REQUIRE(std::filesystem::exists(checkpointIndexPath)); + + { + // Open up the index and verify the values. + CheckpointIndex index = CheckpointIndex::Open(checkpointIndexPath.u8string(), SQLiteStorageBase::OpenDisposition::ReadWrite); + int contextId = index.GetFirstContextId(); + REQUIRE(contextId == 0); + REQUIRE(index.GetCommandName(contextId) == "install"sv); + REQUIRE(index.GetStringArgument(contextId, "manifest"sv) == testManifestPath); + REQUIRE(index.GetStringArgument(contextId, "location"sv) == tempDirectory.GetPath().u8string()); + } +} + +TEST_CASE("Resume_InstallFlowSucceeded", "[Resume]") +{ + GUID testGuid; + CoCreateGuid(&testGuid); + + std::filesystem::path testIndexPath = CheckpointIndex::GetCheckpointIndexPath(testGuid); + + // Create checkpoint index that succeeds. Verify that checkpoint index is cleaned up. + { + CheckpointIndex checkpointIndex = CheckpointIndex::CreateNew(testIndexPath.u8string()); + + } + +} diff --git a/src/AppInstallerCLITests/TestHooks.h b/src/AppInstallerCLITests/TestHooks.h index b66aa7a507..e559be040c 100644 --- a/src/AppInstallerCLITests/TestHooks.h +++ b/src/AppInstallerCLITests/TestHooks.h @@ -40,6 +40,7 @@ namespace AppInstaller namespace Repository::Microsoft { void TestHook_SetPinningIndex_Override(std::optional&& indexPath); + void TestHook_SetCheckpointIndexDirectory_Override(std::optional&& checkpointIndexDirectory); } namespace Logging @@ -120,6 +121,19 @@ namespace TestHook } }; + struct SetCheckpointIndexDirectory_Override + { + SetCheckpointIndexDirectory_Override(const std::filesystem::path& checkpointIndexDirectoryPath) + { + AppInstaller::Repository::Microsoft::TestHook_SetCheckpointIndexDirectory_Override(checkpointIndexDirectoryPath); + } + + ~SetCheckpointIndexDirectory_Override() + { + AppInstaller::Repository::Microsoft::TestHook_SetCheckpointIndexDirectory_Override({}); + } + }; + struct MockDismHelper_Override { MockDismHelper_Override() diff --git a/src/AppInstallerCommonCore/Public/AppInstallerRuntime.h b/src/AppInstallerCommonCore/Public/AppInstallerRuntime.h index 3ce763fa28..4d2a00ab39 100644 --- a/src/AppInstallerCommonCore/Public/AppInstallerRuntime.h +++ b/src/AppInstallerCommonCore/Public/AppInstallerRuntime.h @@ -49,7 +49,7 @@ namespace AppInstaller::Runtime SelfPackageRoot, // The location where user downloads are stored. UserProfileDownloads, - // Always one more than the last path; for being able to iterate paths in tests. + // The location where checkpoint database files are stored. Max }; diff --git a/src/AppInstallerCommonCore/Runtime.cpp b/src/AppInstallerCommonCore/Runtime.cpp index 64f3c45c88..19a2e7e854 100644 --- a/src/AppInstallerCommonCore/Runtime.cpp +++ b/src/AppInstallerCommonCore/Runtime.cpp @@ -31,6 +31,7 @@ namespace AppInstaller::Runtime constexpr std::string_view s_PortablePackageRoot = "WinGet"sv; constexpr std::string_view s_PortablePackagesDirectory = "Packages"sv; constexpr std::string_view s_LinksDirectory = "Links"sv; + constexpr std::string_view s_CheckpointsDirectory = "Checkpoints"sv; constexpr std::string_view s_DevModeSubkey = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\AppModelUnlock"sv; constexpr std::string_view s_AllowDevelopmentWithoutDevLicense = "AllowDevelopmentWithoutDevLicense"sv; #ifndef WINGET_DISABLE_FOR_FUZZING diff --git a/src/AppInstallerRepositoryCore/Microsoft/CheckpointIndex.cpp b/src/AppInstallerRepositoryCore/Microsoft/CheckpointIndex.cpp index 37a14296fc..33a3f06ffe 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/CheckpointIndex.cpp +++ b/src/AppInstallerRepositoryCore/Microsoft/CheckpointIndex.cpp @@ -7,6 +7,11 @@ namespace AppInstaller::Repository::Microsoft { + namespace + { + constexpr std::string_view s_Checkpoints = "Checkpoints"sv; + } + CheckpointIndex CheckpointIndex::CreateNew(const std::string& filePath, Schema::Version version) { AICLI_LOG(Repo, Info, << "Creating new Checkpoint Index with version [" << version << "] at '" << filePath << "'"); @@ -28,15 +33,8 @@ namespace AppInstaller::Repository::Microsoft std::shared_ptr CheckpointIndex::OpenOrCreateDefault(GUID guid, OpenDisposition openDisposition) { - wchar_t buffer[256]; - if (!StringFromGUID2(guid, buffer, ARRAYSIZE(buffer))) - { - THROW_HR(E_UNEXPECTED); - } - - auto indexPath = Runtime::GetPathTo(Runtime::PathName::LocalState) / buffer; - indexPath.replace_extension(".db"); - AICLI_LOG(Repo, Info, << "Opening savepoint index"); + const auto& indexPath = GetCheckpointIndexPath(guid); + AICLI_LOG(Repo, Info, << "Opening checkpoint index"); try { @@ -46,13 +44,13 @@ namespace AppInstaller::Repository::Microsoft { try { - AICLI_LOG(Repo, Info, << "Opening existing savepoint index"); + AICLI_LOG(Repo, Info, << "Opening existing checkpoint index"); return std::make_shared(CheckpointIndex::Open(indexPath.u8string(), openDisposition)); } CATCH_LOG(); } - AICLI_LOG(Repo, Info, << "Attempting to delete bad index file"); + AICLI_LOG(Repo, Info, << "Attempting to delete bad checkpoint index file"); std::filesystem::remove_all(indexPath); } @@ -63,6 +61,41 @@ namespace AppInstaller::Repository::Microsoft return {}; } +#ifndef AICLI_DISABLE_TEST_HOOKS + std::optional s_CheckpointIndexDirectoryOverride{}; + void TestHook_SetCheckpointIndexDirectory_Override(std::optional&& checkpointIndexDirectory) + { + s_CheckpointIndexDirectoryOverride = std::move(checkpointIndexDirectory); + } +#endif + + std::filesystem::path CheckpointIndex::GetCheckpointIndexPath(GUID guid) + { + wchar_t checkpointGuid[256]; + THROW_HR_IF(E_UNEXPECTED, !StringFromGUID2(guid, checkpointGuid, ARRAYSIZE(checkpointGuid))); + + const auto DefaultIndexDirectoryPath = Runtime::GetPathTo(Runtime::PathName::LocalState) / s_Checkpoints; +#ifndef AICLI_DISABLE_TEST_HOOKS + const auto checkpointIndexDirectory = s_CheckpointIndexDirectoryOverride.has_value() ? s_CheckpointIndexDirectoryOverride.value() : DefaultIndexDirectoryPath; +#else + const auto checkpointIndexDirectory = DefaultIndexDirectoryPath; +#endif + + if (!std::filesystem::exists(checkpointIndexDirectory)) + { + std::filesystem::create_directories(checkpointIndexDirectory); + AICLI_LOG(Repo, Info, << "Creating checkpoint index directory: " << checkpointIndexDirectory); + } + else + { + THROW_HR_IF(ERROR_CANNOT_MAKE, !std::filesystem::is_directory(checkpointIndexDirectory)); + } + + auto indexPath = checkpointIndexDirectory / checkpointGuid; + indexPath.replace_extension(".db"); + return indexPath; + } + bool CheckpointIndex::IsEmpty() { return m_interface->IsEmpty(m_dbconn); diff --git a/src/AppInstallerRepositoryCore/Microsoft/CheckpointIndex.h b/src/AppInstallerRepositoryCore/Microsoft/CheckpointIndex.h index 53e7482714..5ef3e33779 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/CheckpointIndex.h +++ b/src/AppInstallerRepositoryCore/Microsoft/CheckpointIndex.h @@ -32,6 +32,9 @@ namespace AppInstaller::Repository::Microsoft // Opens or creates a CheckpointIndex database on the default path. static std::shared_ptr OpenOrCreateDefault(GUID guid, OpenDisposition openDisposition = OpenDisposition::ReadWrite); + // Gets the file path of the CheckpointIndex database. + static std::filesystem::path GetCheckpointIndexPath(GUID guid); + bool IsEmpty(); IdType SetClientVersion(std::string_view clientVersion); diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointArgumentsTable.cpp b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointArgumentsTable.cpp index b517c54b33..4a26575963 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointArgumentsTable.cpp +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointArgumentsTable.cpp @@ -9,6 +9,7 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 { using namespace SQLite; using namespace std::string_view_literals; + static constexpr std::string_view s_CheckpointArgumentsTable_Table_Name = "CheckpointArguments"sv; static constexpr std::string_view s_CheckpointArgumentsTable_ContextId_Column = "contextId"sv; static constexpr std::string_view s_CheckpointArgumentsTable_CommandName_Column = "commandName"sv; diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointIndexInterface_1_0.cpp b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointIndexInterface_1_0.cpp index d6c5503793..8d35e64cf0 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointIndexInterface_1_0.cpp +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointIndexInterface_1_0.cpp @@ -119,11 +119,11 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 void CheckpointIndexInterface::RemoveContextFromContextTable(SQLite::Connection& connection, int contextId) { - auto contextResult = GetExistingContextRowIdFromArgumentTable(connection, contextId); + auto contextResult = GetExistingContextRowIdFromContextTable(connection, contextId); THROW_HR_IF(E_NOT_SET, !contextResult); SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "removeContextFromContextTable_v1_0"); - CheckpointContextTable::RemoveContextById(connection, contextId); + CheckpointContextTable::RemoveContextById(connection, contextResult.value()); savepoint.Commit(); } @@ -151,7 +151,10 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 bool CheckpointIndexInterface::ContainsArgument(SQLite::Connection& connection, int contextId, std::string_view name) { - return CheckpointArgumentsTable::ContainsArgument(connection, contextId, name); + auto contextResult = GetExistingContextRowIdFromArgumentTable(connection, contextId); + THROW_HR_IF(E_NOT_SET, !contextResult); + + return CheckpointArgumentsTable::ContainsArgument(connection, contextResult.value(), name); } std::string CheckpointIndexInterface::GetStringArgumentByContextId(SQLite::Connection& connection, int contextId, std::string_view name) From e3786aad60e22d12f8f71d8a1e5a2020dc706b34 Mon Sep 17 00:00:00 2001 From: Ryan Fu Date: Fri, 4 Aug 2023 14:27:04 -0700 Subject: [PATCH 09/43] save tests --- .../AppInstallerCLICore.vcxproj | 2 + .../AppInstallerCLICore.vcxproj.filters | 6 + src/AppInstallerCLICore/CheckpointManager.cpp | 34 ++- src/AppInstallerCLICore/CheckpointManager.h | 14 +- .../Commands/ResumeCommand.cpp | 46 ++-- src/AppInstallerCLICore/Resources.h | 4 + .../Workflows/ResumeFlow.cpp | 77 +++++++ .../Workflows/ResumeFlow.h | 19 ++ src/AppInstallerCLIE2ETests/Constants.cs | 6 + .../Helpers/TestCommon.cs | 16 ++ src/AppInstallerCLIE2ETests/ResumeCommand.cs | 86 ++++++++ .../Shared/Strings/en-us/winget.resw | 16 ++ src/AppInstallerCLITests/ResumeFlow.cpp | 205 ++++++++++++++++-- src/AppInstallerCLITests/Strings.cpp | 14 +- src/AppInstallerCLITests/TestHooks.h | 19 ++ .../AppInstallerStrings.cpp | 24 ++ src/AppInstallerSharedLib/Errors.cpp | 6 + .../Public/AppInstallerErrors.h | 5 +- .../Public/AppInstallerStrings.h | 4 + 19 files changed, 539 insertions(+), 64 deletions(-) create mode 100644 src/AppInstallerCLICore/Workflows/ResumeFlow.cpp create mode 100644 src/AppInstallerCLICore/Workflows/ResumeFlow.h create mode 100644 src/AppInstallerCLIE2ETests/ResumeCommand.cs diff --git a/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj b/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj index 148100bfbf..f8ce7f0465 100644 --- a/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj +++ b/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj @@ -414,6 +414,7 @@ + @@ -484,6 +485,7 @@ + diff --git a/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj.filters b/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj.filters index fd06c81667..d4b1df0aed 100644 --- a/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj.filters +++ b/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj.filters @@ -230,6 +230,9 @@ Commands + + Workflows + Header Files @@ -433,6 +436,9 @@ Source Files + + Workflows + diff --git a/src/AppInstallerCLICore/CheckpointManager.cpp b/src/AppInstallerCLICore/CheckpointManager.cpp index 4a0348f8a4..c04fea85fa 100644 --- a/src/AppInstallerCLICore/CheckpointManager.cpp +++ b/src/AppInstallerCLICore/CheckpointManager.cpp @@ -31,7 +31,7 @@ namespace AppInstaller::CLI::Checkpoint if (!checkpointIndex) { AICLI_LOG(CLI, Error, << "Unable to open checkpoint index."); - THROW_HR_MSG(HRESULT_FROM_WIN32(ERROR_NOT_FOUND), "The saved state could not be found."); + THROW_HR_MSG(HRESULT_FROM_WIN32(ERROR_NOT_FOUND), "The checkpoint state could not be found."); } m_checkpointIndex = std::move(checkpointIndex); @@ -54,6 +54,11 @@ namespace AppInstaller::CLI::Checkpoint } } + bool CheckpointManager::HasContext() + { + return !m_checkpointIndex->IsEmpty(); + } + void CheckpointManager::AddContext(int contextId) { m_checkpointIndex->AddContext(contextId); @@ -85,20 +90,37 @@ namespace AppInstaller::CLI::Checkpoint return static_cast(m_checkpointIndex->GetLastCheckpoint(contextId)); } - bool CheckpointManager::CleanUpIndex() +#ifndef AICLI_DISABLE_TEST_HOOKS + static bool s_MockCheckpointManagerCleanUp_Override = false; + + void TestHook_MockCheckpointManagerCleanUp_Override(bool status) + { + s_MockCheckpointManagerCleanUp_Override = status; + } +#endif + + void CheckpointManager::CleanUpIndex() { - if (m_checkpointIndex->IsEmpty()) +#ifndef AICLI_DISABLE_TEST_HOOKS + // Unit tests will handle clean up so this hook is needed to avoid attempting to reset and clean up again. + if (s_MockCheckpointManagerCleanUp_Override) { - m_checkpointIndex.reset(); + return; + } +#endif + + bool isIndexEmpty = m_checkpointIndex->IsEmpty(); + m_checkpointIndex.reset(); + + if (isIndexEmpty) + { const auto& checkpointIndexPath = AppInstaller::Repository::Microsoft::CheckpointIndex::GetCheckpointIndexPath(m_checkpointId); if (std::filesystem::remove(checkpointIndexPath)) { AICLI_LOG(CLI, Info, << "Checkpoint index deleted: " << checkpointIndexPath); } } - - return true; } CheckpointManager::~CheckpointManager() diff --git a/src/AppInstallerCLICore/CheckpointManager.h b/src/AppInstallerCLICore/CheckpointManager.h index a081d95cf6..1e9c952067 100644 --- a/src/AppInstallerCLICore/CheckpointManager.h +++ b/src/AppInstallerCLICore/CheckpointManager.h @@ -29,6 +29,8 @@ namespace AppInstaller::CLI::Checkpoint void Checkpoint(Execution::Context& context, Execution::CheckpointFlag flag); + bool HasContext(); + void AddContext(int contextId); void RemoveContext(int contextId); @@ -41,13 +43,23 @@ namespace AppInstaller::CLI::Checkpoint Execution::CheckpointFlag GetLastCheckpoint(int contextId); +#ifndef AICLI_DISABLE_TEST_HOOKS + // Only used by unit tests for proper cleanup. + void ManualReset() + { + m_checkpointId = GUID_NULL; + m_checkpointIndex.reset(); + } +#endif + + private: CheckpointManager() = default; ~CheckpointManager(); GUID m_checkpointId = {}; std::shared_ptr m_checkpointIndex = nullptr; - bool CleanUpIndex(); + void CleanUpIndex(); void SaveCheckpoint(Execution::Context& context, Execution::CheckpointFlag flag); void LoadCheckpoint(Execution::Context& context, Execution::CheckpointFlag flag); diff --git a/src/AppInstallerCLICore/Commands/ResumeCommand.cpp b/src/AppInstallerCLICore/Commands/ResumeCommand.cpp index e87b3869ea..2288991a1b 100644 --- a/src/AppInstallerCLICore/Commands/ResumeCommand.cpp +++ b/src/AppInstallerCLICore/Commands/ResumeCommand.cpp @@ -4,7 +4,7 @@ #include "AppInstallerRuntime.h" #include "CheckpointManager.h" #include "ResumeCommand.h" -#include "RootCommand.h" +#include "Workflows/ResumeFlow.h" #include "Resources.h" namespace AppInstaller::CLI @@ -37,45 +37,25 @@ namespace AppInstaller::CLI void ResumeCommand::ExecuteInternal(Execution::Context& context) const { - GUID checkpointId = Utility::ConvertToGuid(std::string{ context.Args.GetArg(Execution::Args::Type::ResumeGuid) }); + context << + Workflow::EnsureSupportForResume; - CheckpointManager::Instance().Initialize(checkpointId); - - if (AppInstaller::Runtime::GetClientVersion().get() != CheckpointManager::Instance().GetClientVersion()) + if (context.IsTerminated()) { - AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_CLIENTVERSION_MISMATCH); + return; } - // Get the root context id from the index. int rootContextId = CheckpointManager::Instance().GetFirstContextId(); + auto resumeContextPtr = context.CreateEmptyContext(rootContextId); - // Determine the command to execute. - std::string commandName = CheckpointManager::Instance().GetCommandName(rootContextId); - std::unique_ptr commandToResume; - - for (auto& command : std::make_unique()->GetCommands()) - { - if ( - Utility::CaseInsensitiveEquals(commandName, command->Name()) || - Utility::CaseInsensitiveContains(command->Aliases(), commandName) - ) - { - AICLI_LOG(CLI, Info, << "Resuming command: " << commandName); - commandToResume = std::move(command); - break; - } - } - - // Create a new context, set the executing command and current checkpoint. - auto resumeContext = context.CreateEmptyContext(rootContextId); - resumeContext->SetExecutingCommand(commandToResume.get()); + Context& resumeContext = *resumeContextPtr; + auto previousThreadGlobals = resumeContext.SetForCurrentThread(); - // Set the target checkpoint of the root context. - resumeContext->SetTargetCheckpoint(CheckpointManager::Instance().GetLastCheckpoint(rootContextId)); - resumeContext->SetFlags(Execution::ContextFlag::Resume); + resumeContext << + Workflow::LoadInitialResumeState; - // Load the arguments from the checkpoint index prior to executing the command. - CheckpointManager::Instance().Checkpoint(*resumeContext, Execution::CheckpointFlag::CommandArguments); - commandToResume->Execute(*resumeContext); + auto executingCommandPtr = resumeContext.GetExecutingCommand(); + Command& executingCommand = *executingCommandPtr; + executingCommand.Execute(resumeContext); } } diff --git a/src/AppInstallerCLICore/Resources.h b/src/AppInstallerCLICore/Resources.h index b2dd78b92e..3beefef7f7 100644 --- a/src/AppInstallerCLICore/Resources.h +++ b/src/AppInstallerCLICore/Resources.h @@ -43,6 +43,7 @@ namespace AppInstaller::CLI::Resource WINGET_DEFINE_RESOURCE_STRINGID(Cancelled); WINGET_DEFINE_RESOURCE_STRINGID(CancellingOperation); WINGET_DEFINE_RESOURCE_STRINGID(ChannelArgumentDescription); + WINGET_DEFINE_RESOURCE_STRINGID(ClientVersionMismatchError); WINGET_DEFINE_RESOURCE_STRINGID(Command); WINGET_DEFINE_RESOURCE_STRINGID(CommandArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(CommandLineArgumentDescription); @@ -280,6 +281,7 @@ namespace AppInstaller::CLI::Resource WINGET_DEFINE_RESOURCE_STRINGID(InvalidJsonFile); WINGET_DEFINE_RESOURCE_STRINGID(InvalidNameError); WINGET_DEFINE_RESOURCE_STRINGID(InvalidPathToNestedInstaller); + WINGET_DEFINE_RESOURCE_STRINGID(InvalidResumeGuidError); WINGET_DEFINE_RESOURCE_STRINGID(KeyDirectoriesHeader); WINGET_DEFINE_RESOURCE_STRINGID(LicenseAgreement); WINGET_DEFINE_RESOURCE_STRINGID(Links); @@ -406,6 +408,8 @@ namespace AppInstaller::CLI::Resource WINGET_DEFINE_RESOURCE_STRINGID(ResumeCommandLongDescription); WINGET_DEFINE_RESOURCE_STRINGID(ResumeCommandShortDescription); WINGET_DEFINE_RESOURCE_STRINGID(ResumeGuidArgumentDescription); + WINGET_DEFINE_RESOURCE_STRINGID(ResumeGuidNotFoundError); + WINGET_DEFINE_RESOURCE_STRINGID(ResumeStateDataNotFoundError); WINGET_DEFINE_RESOURCE_STRINGID(RetroArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(SearchCommandLongDescription); WINGET_DEFINE_RESOURCE_STRINGID(SearchCommandShortDescription); diff --git a/src/AppInstallerCLICore/Workflows/ResumeFlow.cpp b/src/AppInstallerCLICore/Workflows/ResumeFlow.cpp new file mode 100644 index 0000000000..c176cf7f8a --- /dev/null +++ b/src/AppInstallerCLICore/Workflows/ResumeFlow.cpp @@ -0,0 +1,77 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#include "pch.h" +#include "AppInstallerRuntime.h" +#include "ResumeFlow.h" +#include "CheckpointManager.h" +#include "Microsoft/CheckpointIndex.h" +#include "Commands/RootCommand.h" + +using namespace AppInstaller::CLI::Execution; +using namespace AppInstaller::CLI::Checkpoint; +using namespace AppInstaller::Manifest; +using namespace AppInstaller::Repository; +using namespace AppInstaller::Settings; +using namespace AppInstaller::Utility; +using namespace AppInstaller::Utility::literals; + +namespace AppInstaller::CLI::Workflow +{ + void EnsureSupportForResume(Execution::Context& context) + { + std::string resumeGuidString { context.Args.GetArg(Execution::Args::Type::ResumeGuid) }; + if (!Utility::IsValidGuidString(resumeGuidString)) + { + context.Reporter.Error() << Resource::String::InvalidResumeGuidError(Utility::LocIndView{ resumeGuidString }) << std::endl; + AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_INVALID_RESUME_GUID); + } + + GUID checkpointId = Utility::ConvertToGuid(resumeGuidString); + + if (!std::filesystem::exists(AppInstaller::Repository::Microsoft::CheckpointIndex::GetCheckpointIndexPath(checkpointId))) + { + context.Reporter.Error() << Resource::String::ResumeGuidNotFoundError << std::endl; + AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_RESUME_GUID_NOT_FOUND); + } + + CheckpointManager::Instance().Initialize(checkpointId); + + if (AppInstaller::Runtime::GetClientVersion().get() != CheckpointManager::Instance().GetClientVersion()) + { + context.Reporter.Error() << Resource::String::ClientVersionMismatchError << std::endl; + AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_CLIENTVERSION_MISMATCH); + } + + if (!CheckpointManager::Instance().HasContext()) + { + context.Reporter.Error() << Resource::String::ResumeStateDataNotFoundError << std::endl; + AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_INVALID_RESUME_STATE); + } + } + + void LoadInitialResumeState(Execution::Context& context) + { + int contextId = context.GetContextId(); + std::string commandName = CheckpointManager::Instance().GetCommandName(contextId); + std::unique_ptr commandToResume; + + for (auto& command : std::make_unique()->GetCommands()) + { + if ( + Utility::CaseInsensitiveEquals(commandName, command->Name()) || + Utility::CaseInsensitiveContains(command->Aliases(), commandName) + ) + { + AICLI_LOG(CLI, Info, << "Resuming command: " << commandName); + commandToResume = std::move(command); + break; + } + } + + context.SetExecutingCommand(commandToResume.get()); + context.SetTargetCheckpoint(CheckpointManager::Instance().GetLastCheckpoint(contextId)); + context.SetFlags(Execution::ContextFlag::Resume); + + CheckpointManager::Instance().Checkpoint(context, Execution::CheckpointFlag::CommandArguments); + } +} diff --git a/src/AppInstallerCLICore/Workflows/ResumeFlow.h b/src/AppInstallerCLICore/Workflows/ResumeFlow.h new file mode 100644 index 0000000000..c216dc920e --- /dev/null +++ b/src/AppInstallerCLICore/Workflows/ResumeFlow.h @@ -0,0 +1,19 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once +#include "ExecutionContext.h" + +namespace AppInstaller::CLI::Workflow +{ + // Ensures that the arguments provided supports a resume. + // Required Args: None + // Inputs: ResumeGuid + // Outputs: None + void EnsureSupportForResume(Execution::Context& context); + + // Loads the initial state of the context from the initialized checkpoint index. + // Required Args: None + // Inputs: None + // Outputs: None + void LoadInitialResumeState(Execution::Context& context); +} diff --git a/src/AppInstallerCLIE2ETests/Constants.cs b/src/AppInstallerCLIE2ETests/Constants.cs index 65e66dae9a..9758ae3861 100644 --- a/src/AppInstallerCLIE2ETests/Constants.cs +++ b/src/AppInstallerCLIE2ETests/Constants.cs @@ -66,6 +66,8 @@ public class Constants public const string WinGetUtil = "WinGetUtil"; public const string E2ETestLogsPathPackaged = @"Packages\WinGetDevCLI_8wekyb3d8bbwe\LocalState\DiagOutputDir"; public const string E2ETestLogsPathUnpackaged = @"WinGet\defaultState"; + public const string CheckpointDirectoryPackaged = @"Packages\WinGetDevCLI_8wekyb3d8bbwe\LocalState\Checkpoints"; + public const string CheckpointDirectoryUnpackaged = @"Microsoft\WinGet\State\defaultState\Checkpoints"; // Installer filename public const string TestCommandExe = "testCommand.exe"; @@ -246,6 +248,10 @@ public class ErrorCode public const int ERROR_PACKAGE_IS_PINNED = unchecked((int)0x8A150068); public const int ERROR_PACKAGE_IS_STUB = unchecked((int)0x8A150069); public const int ERROR_DOWNLOAD_DEPENDENCIES = unchecked((int)0x8A15006A); + public const int ERROR_INVALID_RESUME_GUID = unchecked((int)0x8A15006B); + public const int ERROR_RESUME_GUID_NOT_FOUND = unchecked((int)0x8A15006C); + public const int ERROR_CLIENTVERSION_MISMATCH = unchecked((int)0x8A15006D); + public const int ERROR_INVALID_RESUME_STATE = unchecked((int)0x8A15006F); public const int ERROR_INSTALL_PACKAGE_IN_USE = unchecked((int)0x8A150101); public const int ERROR_INSTALL_INSTALL_IN_PROGRESS = unchecked((int)0x8A150102); diff --git a/src/AppInstallerCLIE2ETests/Helpers/TestCommon.cs b/src/AppInstallerCLIE2ETests/Helpers/TestCommon.cs index 0797ae0222..b078016ede 100644 --- a/src/AppInstallerCLIE2ETests/Helpers/TestCommon.cs +++ b/src/AppInstallerCLIE2ETests/Helpers/TestCommon.cs @@ -428,6 +428,22 @@ public static string GetDefaultDownloadDirectory() return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), "Downloads"); } + /// + /// Gets the checkpoints directory based on whether the command is invoked in desktop package or not. + /// + /// The default checkpoints directory. + public static string GetCheckpointsDirectory() + { + if (TestSetup.Parameters.PackagedContext) + { + return Path.Combine(Environment.GetEnvironmentVariable("LocalAppData"), Constants.CheckpointDirectoryPackaged); + } + else + { + return Path.Combine(Environment.GetEnvironmentVariable("LocalAppData"), Constants.CheckpointDirectoryUnpackaged); + } + } + /// /// Verify portable package. /// diff --git a/src/AppInstallerCLIE2ETests/ResumeCommand.cs b/src/AppInstallerCLIE2ETests/ResumeCommand.cs new file mode 100644 index 0000000000..2056336fa5 --- /dev/null +++ b/src/AppInstallerCLIE2ETests/ResumeCommand.cs @@ -0,0 +1,86 @@ +// ----------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. Licensed under the MIT License. +// +// ----------------------------------------------------------------------------- + +namespace AppInstallerCLIE2ETests +{ + using System.IO; + using System.Linq; + using AppInstallerCLIE2ETests.Helpers; + using NUnit.Framework; + + /// + /// Test download command. + /// + public class ResumeCommand : BaseCommand + { + /// + /// One time setup. + /// + [OneTimeSetUp] + public void OneTimeSetup() + { + WinGetSettingsHelper.ConfigureFeature("resume", true); + WinGetSettingsHelper.ConfigureFeature("windowsFeature", true); + } + + /// + /// Installs a test exe installer and verifies that the checkpoint index is cleaned up. + /// + [Test] + public void InstallExe_VerifyIndexDoesNotExist() + { + var checkpointsDir = TestCommon.GetCheckpointsDirectory(); + + // If the checkpoints directory does not yet exist, set to 0. The directory should be created when the command is invoked. + int initialCheckpointsCount = Directory.Exists(checkpointsDir) ? Directory.GetFiles(checkpointsDir).Length : 0; + + var installDir = TestCommon.GetRandomTestDir(); + var result = TestCommon.RunAICLICommand("install", $"AppInstallerTest.TestExeInstaller --silent -l {installDir}"); + Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); + Assert.True(result.StdOut.Contains("Successfully installed")); + Assert.True(TestCommon.VerifyTestExeInstalledAndCleanup(installDir, "/execustom")); + + int actualCheckpointsCount = Directory.GetFiles(checkpointsDir).Length; + + // The checkpoints count should not change as the index file should be cleaned up after a successful install. + Assert.AreEqual(initialCheckpointsCount, actualCheckpointsCount); + } + + /// + /// Installs a test exe installer and verifies that the checkpoint index is cleaned up. + /// + [Test] + public void ResumeIndex() + { + var checkpointsDir = TestCommon.GetCheckpointsDirectory(); + + // If the checkpoints directory does not yet exist, set to 0. The directory should be created when the command is invoked. + int initialCheckpointsCount = Directory.Exists(checkpointsDir) ? Directory.GetFiles(checkpointsDir).Length : 0; + + var installResult = TestCommon.RunAICLICommand("install", "--id AppInstallerTest.WindowsFeature"); + Assert.AreEqual(Constants.ErrorCode.ERROR_INSTALL_MISSING_DEPENDENCY, installResult.ExitCode); + Assert.True(installResult.StdOut.Contains("The feature [invalidFeature] was not found.")); + + int actualCheckpointsCount = Directory.GetFiles(checkpointsDir).Length; + + // One new index file should be created after running the install command. + Assert.AreEqual(initialCheckpointsCount + 1, actualCheckpointsCount); + + var checkpointsDirectoryInfo = new DirectoryInfo(checkpointsDir); + + var indexFile = checkpointsDirectoryInfo.GetFiles() + .OrderByDescending(f => f.LastWriteTime) + .First(); + + var indexFileName = Path.GetFileNameWithoutExtension(indexFile.Name); + + // Resume output should be the same as the install result. + var resumeResult = TestCommon.RunAICLICommand("resume", $"-g {indexFileName}"); + Assert.AreEqual(Constants.ErrorCode.ERROR_INSTALL_MISSING_DEPENDENCY, resumeResult.ExitCode); + Assert.True(resumeResult.StdOut.Contains("The feature [invalidFeature] was not found.")); + } + } +} \ No newline at end of file diff --git a/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw b/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw index 426f04959a..757cf9f19a 100644 --- a/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw +++ b/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw @@ -2065,4 +2065,20 @@ Please specify one of them using the --source option to proceed. The unique guid identifier of the saved state to resume + + The resume guid string provided is invalid: {0} + {Locked="{0}", "guid"} +{0} represents the guid string that the user provided. +{1} 'guid' is the short name form of a globally unique identifier. + + + Resuming the state from a different client version is not supported. + + + The resume state does not exist: {0} + {Locked="{0}" Error message displayed when the user provides a guid that does not correspond to a valid saved state. {0} is a placeholder replaced by the provided guid string. + + + No data found in the resume state. + \ No newline at end of file diff --git a/src/AppInstallerCLITests/ResumeFlow.cpp b/src/AppInstallerCLITests/ResumeFlow.cpp index 40bf3e9f6c..a9b14f7058 100644 --- a/src/AppInstallerCLITests/ResumeFlow.cpp +++ b/src/AppInstallerCLITests/ResumeFlow.cpp @@ -7,69 +7,234 @@ #include #include #include +#include #include +#include +using namespace std::string_literals; using namespace AppInstaller::CLI; using namespace AppInstaller::Repository::Microsoft; using namespace AppInstaller::Settings; using namespace TestCommon; -TEST_CASE("Resume_VerifyIndexCreatedForTerminatedWorkflow", "[Resume]") +namespace { - // TODO: Update test when only certain termination HRs persist the checkpoint index. + // IMPORTANT: + // Since checkpoint manager is a static singleton class, the deconstructor is not called until after the tests are done. + // This will cause issues with the teardown process, as the temp directory will try to remove itself while the checkpoint manager still holds + // the handle to the index file. To resolve this, clean up for the Checkpoint Manager has been disabled and any tests that involve an index must call + // 'Checkpoint::CheckpointManager::Instance().ManualReset()'. + auto mockCheckpointManagerCleanUp = TestHook::MockCheckpointManagerCleanUp_Override(); +} + +TEST_CASE("ResumeFlow_InvalidGuid", "[Resume]") +{ + std::ostringstream resumeOutput; + TestContext context{ resumeOutput, std::cin }; + auto previousThreadGlobals = context.SetForCurrentThread(); + + context.Args.AddArg(Execution::Args::Type::ResumeGuid, "badGuid"sv); + + ResumeCommand resume({}); + context.SetExecutingCommand(&resume); + resume.Execute(context); + INFO(resumeOutput.str()); + + REQUIRE(context.GetTerminationHR() == APPINSTALLER_CLI_ERROR_INVALID_RESUME_GUID); + auto expectedMessage = Resource::String::InvalidResumeGuidError(AppInstaller::Utility::LocIndString{ "badGuid"s }); + REQUIRE(resumeOutput.str().find(Resource::LocString(expectedMessage).get()) != std::string::npos); +} + +TEST_CASE("ResumeFlow_IndexNotFound", "[Resume]") +{ + std::string tempGuidString = "{ec3a098c-a815-4d52-8866-946c03093a37}"; + + std::ostringstream resumeOutput; + TestContext context{ resumeOutput, std::cin }; + auto previousThreadGlobals = context.SetForCurrentThread(); + context.Args.AddArg(Execution::Args::Type::ResumeGuid, tempGuidString); + + ResumeCommand resume({}); + context.SetExecutingCommand(&resume); + resume.Execute(context); + INFO(resumeOutput.str()); + + REQUIRE_TERMINATED_WITH(context, APPINSTALLER_CLI_ERROR_RESUME_GUID_NOT_FOUND); + REQUIRE(resumeOutput.str().find(Resource::LocString(Resource::String::ResumeGuidNotFoundError).get()) != std::string::npos); +} + +TEST_CASE("ResumeFlow_InvalidClientVersion", "[Resume]") +{ + TestCommon::TempDirectory tempCheckpointIndexDirectory("TempCheckpointIndexDirectory", true); + + const auto& tempCheckpointIndexDirectoryPath = tempCheckpointIndexDirectory.GetPath(); + TestHook::SetCheckpointIndexDirectory_Override checkpointIndexDirectoryOverride(tempCheckpointIndexDirectoryPath); + // Create temp guid and populate with invalid client version. + std::string tempGuidString = "{b157d11f-4487-4e03-9447-9f9d50d66d8e}"; + std::string tempFileName = tempGuidString + ".db"; + auto tempIndexPath = tempCheckpointIndexDirectoryPath / tempFileName; + + INFO("Using temporary file named: " << tempIndexPath); + + { + CheckpointIndex index = CheckpointIndex::CreateNew(tempIndexPath.u8string()); + index.SetClientVersion("1.2.3.4"); + } + + std::ostringstream resumeOutput; + TestContext context{ resumeOutput, std::cin }; + auto previousThreadGlobals = context.SetForCurrentThread(); + context.Args.AddArg(Execution::Args::Type::ResumeGuid, tempGuidString); + + ResumeCommand resume({}); + context.SetExecutingCommand(&resume); + resume.Execute(context); + INFO(resumeOutput.str()); + + REQUIRE_TERMINATED_WITH(context, APPINSTALLER_CLI_ERROR_CLIENTVERSION_MISMATCH); + REQUIRE(resumeOutput.str().find(Resource::LocString(Resource::String::ClientVersionMismatchError).get()) != std::string::npos); + + // Manually reset index to allow for proper clean up. + Checkpoint::CheckpointManager::Instance().ManualReset(); +} + +TEST_CASE("ResumeFlow_EmptyCheckpointIndex", "Resume") +{ TestCommon::TempDirectory tempCheckpointIndexDirectory("TempCheckpointIndexDirectory", false); + const auto& tempCheckpointIndexDirectoryPath = tempCheckpointIndexDirectory.GetPath(); TestHook::SetCheckpointIndexDirectory_Override checkpointIndexDirectoryOverride(tempCheckpointIndexDirectoryPath); - TestCommon::TempDirectory tempDirectory("TempDirectory", false); + // Create temp guid and populate with invalid client version. + std::string tempGuidString = "{43ca664c-3eae-4f73-99ee-18cf83912c02}"; + std::string tempFileName = tempGuidString + ".db"; + auto tempIndexPath = tempCheckpointIndexDirectoryPath / tempFileName; + + INFO("Using temporary file named: " << tempIndexPath); + + { + CheckpointIndex index = CheckpointIndex::CreateNew(tempGuidString); + index.SetClientVersion(AppInstaller::Runtime::GetClientVersion()); + } + + std::ostringstream resumeOutput; + TestContext context{ resumeOutput, std::cin }; + auto previousThreadGlobals = context.SetForCurrentThread(); + context.Args.AddArg(Execution::Args::Type::ResumeGuid, tempGuidString); + + ResumeCommand resume({}); + context.SetExecutingCommand(&resume); + resume.Execute(context); + INFO(resumeOutput.str()); + + REQUIRE_TERMINATED_WITH(context, APPINSTALLER_CLI_ERROR_INVALID_RESUME_STATE); + REQUIRE(resumeOutput.str().find(Resource::LocString(Resource::String::ResumeStateDataNotFoundError).get()) != std::string::npos); + + // Manually reset index to allow for proper clean up. + Checkpoint::CheckpointManager::Instance().ManualReset(); +} + +TEST_CASE("ResumeFlow_VerifyStateRemovedForSuccess", "[Resume]") +{ + TestCommon::TempDirectory tempCheckpointIndexDirectory("TempCheckpointIndexDirectory", false); + + const auto& tempCheckpointIndexDirectoryPath = tempCheckpointIndexDirectory.GetPath(); + TestHook::SetCheckpointIndexDirectory_Override checkpointIndexDirectoryOverride(tempCheckpointIndexDirectoryPath); TestCommon::TestUserSettings testSettings; testSettings.Set(true); + TestCommon::TempFile installResultPath("TestExeInstalled.txt"); + std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); + OverrideForShellExecute(context); - const auto& testManifestPath = TestDataFile("InstallFlowTest_UnsupportedArguments.yaml").GetPath().u8string(); + const auto& testManifestPath = TestDataFile("InstallFlowTest_Exe.yaml").GetPath().u8string(); context.Args.AddArg(Execution::Args::Type::Manifest, testManifestPath); - context.Args.AddArg(Execution::Args::Type::InstallLocation, tempDirectory); InstallCommand install({}); context.SetExecutingCommand(&install); install.Execute(context); - // There should only be one file in the temp directory. - std::filesystem::path checkpointIndexPath; + // Verify Installer is called and parameters are passed in. + REQUIRE(std::filesystem::exists(installResultPath.GetPath())); + std::ifstream installResultFile(installResultPath.GetPath()); + REQUIRE(installResultFile.is_open()); + std::string installResultStr; + std::getline(installResultFile, installResultStr); + REQUIRE(installResultStr.find("/custom") != std::string::npos); + REQUIRE(installResultStr.find("/silentwithprogress") != std::string::npos); + + // Only one checkpoint file should be created. + std::vector checkpointFiles; for (const auto& entry : std::filesystem::directory_iterator(tempCheckpointIndexDirectoryPath)) { - checkpointIndexPath = entry.path(); + checkpointFiles.emplace_back(entry.path()); } + REQUIRE(checkpointFiles.size() == 1); + + std::filesystem::path checkpointIndexPath = checkpointFiles[0]; REQUIRE(std::filesystem::exists(checkpointIndexPath)); { - // Open up the index and verify the values. CheckpointIndex index = CheckpointIndex::Open(checkpointIndexPath.u8string(), SQLiteStorageBase::OpenDisposition::ReadWrite); - int contextId = index.GetFirstContextId(); - REQUIRE(contextId == 0); - REQUIRE(index.GetCommandName(contextId) == "install"sv); - REQUIRE(index.GetStringArgument(contextId, "manifest"sv) == testManifestPath); - REQUIRE(index.GetStringArgument(contextId, "location"sv) == tempDirectory.GetPath().u8string()); + REQUIRE(index.IsEmpty()); } + + // Manually reset index to allow for proper index clean up. + Checkpoint::CheckpointManager::Instance().ManualReset(); } -TEST_CASE("Resume_InstallFlowSucceeded", "[Resume]") +TEST_CASE("ResumeFlow_VerifyStateSavedForFailure", "[Resume]") { - GUID testGuid; - CoCreateGuid(&testGuid); + TestCommon::TempDirectory tempCheckpointIndexDirectory("TempCheckpointIndexDirectory", false); + + const auto& tempCheckpointIndexDirectoryPath = tempCheckpointIndexDirectory.GetPath(); + TestHook::SetCheckpointIndexDirectory_Override checkpointIndexDirectoryOverride(tempCheckpointIndexDirectoryPath); - std::filesystem::path testIndexPath = CheckpointIndex::GetCheckpointIndexPath(testGuid); + TestCommon::TestUserSettings testSettings; + testSettings.Set(true); - // Create checkpoint index that succeeds. Verify that checkpoint index is cleaned up. + std::ostringstream installOutput; + TestContext context{ installOutput, std::cin }; + auto previousThreadGlobals = context.SetForCurrentThread(); + + const auto& testManifestPath = TestDataFile("InstallFlowTest_UnsupportedArguments.yaml").GetPath().u8string(); + context.Args.AddArg(Execution::Args::Type::Manifest, testManifestPath); + context.Args.AddArg(Execution::Args::Type::InstallLocation, "installLocation"sv); + + InstallCommand install({}); + context.SetExecutingCommand(&install); + install.Execute(context); + INFO(installOutput.str()); + + // Verify unsupported arguments error message is shown + REQUIRE(context.GetTerminationHR() == APPINSTALLER_CLI_ERROR_UNSUPPORTED_ARGUMENT); + + // Only one checkpoint file should be created. + std::vector checkpointFiles; + for (const auto& entry : std::filesystem::directory_iterator(tempCheckpointIndexDirectoryPath)) { - CheckpointIndex checkpointIndex = CheckpointIndex::CreateNew(testIndexPath.u8string()); + checkpointFiles.emplace_back(entry.path()); + } + + REQUIRE(checkpointFiles.size() == 1); + + std::filesystem::path checkpointIndexPath = checkpointFiles[0]; + REQUIRE(std::filesystem::exists(checkpointIndexPath)); + { + CheckpointIndex index = CheckpointIndex::Open(checkpointIndexPath.u8string(), SQLiteStorageBase::OpenDisposition::ReadWrite); + REQUIRE_FALSE(index.IsEmpty()); + int contextId = index.GetFirstContextId(); + REQUIRE(index.GetStringArgument(contextId, "manifest"sv) == testManifestPath); + REQUIRE(index.GetStringArgument(contextId, "location"sv) == "installLocation"sv); } + // Manually reset index to allow for proper index clean up. + Checkpoint::CheckpointManager::Instance().ManualReset(); } diff --git a/src/AppInstallerCLITests/Strings.cpp b/src/AppInstallerCLITests/Strings.cpp index 6bf06449ea..74cb68a7c4 100644 --- a/src/AppInstallerCLITests/Strings.cpp +++ b/src/AppInstallerCLITests/Strings.cpp @@ -280,17 +280,25 @@ TEST_CASE("SplitWithSeparator", "[string]") REQUIRE(test3[0] == "test"); } -TEST_CASE("ConvertToGuid", "[string]") +TEST_CASE("ConvertGuid", "[string]") { std::string validGuidString1 = "{4d1e55b2-f16f-11cf-88cb-001111000030}"; std::string validGuidString2 = "4d1e55b2-f16f-11cf-88cb-001111000030"; - std::string invalidGuidString1 = "4d1e55b2-f16f-11cf-001111000030"; - std::string invalidGuidString2 = "4d1e55b2f16f11cf88cb001111000030"; + std::string invalidGuidString1 = "4d1e55b2-f16f-11cf-001111000030"; // Not the correct length. + std::string invalidGuidString2 = "4d1e55b2f16f11cf88cb001111000030"; // Missing hyphens. + std::string invalidGuidString3 = "fakeGuid"; GUID guid = { 0x4d1e55b2, 0xf16f, 0x11cf, 0x88, 0xcb, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30 }; + REQUIRE(IsValidGuidString(validGuidString1)); + REQUIRE(IsValidGuidString(validGuidString2)); + REQUIRE_FALSE(IsValidGuidString(invalidGuidString1)); + REQUIRE_FALSE(IsValidGuidString(invalidGuidString2)); + REQUIRE_FALSE(IsValidGuidString(invalidGuidString3)); + REQUIRE(ConvertToGuid(validGuidString1) == guid); REQUIRE(ConvertToGuid(validGuidString2) == guid); REQUIRE_THROWS(ConvertToGuid(invalidGuidString1)); REQUIRE_THROWS(ConvertToGuid(invalidGuidString2)); + REQUIRE_THROWS(ConvertToGuid(invalidGuidString3)); } diff --git a/src/AppInstallerCLITests/TestHooks.h b/src/AppInstallerCLITests/TestHooks.h index e559be040c..82e611f430 100644 --- a/src/AppInstallerCLITests/TestHooks.h +++ b/src/AppInstallerCLITests/TestHooks.h @@ -9,6 +9,7 @@ #include #include +#include #include #include #include @@ -72,6 +73,11 @@ namespace AppInstaller void TestHook_SetWindowsFeatureGetDisplayNameResult_Override(Utility::LocIndString* displayName); void TestHook_SetWindowsFeatureGetRestartStatusResult_Override(AppInstaller::WindowsFeature::DismRestartType* restartType); } + + namespace CLI::Checkpoint + { + void TestHook_MockCheckpointManagerCleanUp_Override(bool status); + } } namespace TestHook @@ -242,4 +248,17 @@ namespace TestHook private: std::vector m_extractedIcons; }; + + struct MockCheckpointManagerCleanUp_Override + { + MockCheckpointManagerCleanUp_Override() + { + AppInstaller::CLI::Checkpoint::TestHook_MockCheckpointManagerCleanUp_Override(true); + } + + ~MockCheckpointManagerCleanUp_Override() + { + AppInstaller::CLI::Checkpoint::TestHook_MockCheckpointManagerCleanUp_Override(false); + } + }; } \ No newline at end of file diff --git a/src/AppInstallerSharedLib/AppInstallerStrings.cpp b/src/AppInstallerSharedLib/AppInstallerStrings.cpp index 033a762086..0c031d4906 100644 --- a/src/AppInstallerSharedLib/AppInstallerStrings.cpp +++ b/src/AppInstallerSharedLib/AppInstallerStrings.cpp @@ -856,6 +856,30 @@ namespace AppInstaller::Utility return result; } + bool IsValidGuidString(const std::string& value) + { + if (value.empty()) + { + return false; + } + + const std::regex guidPattern("^[{]?[0-9a-fA-F]{8}-([0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}[}]?$"); + + std::string guidString = value; + + if (value.front() != '{') + { + guidString.insert(0, 1, '{'); + } + + if (value.back() != '}') + { + guidString.push_back('}'); + } + + return regex_match(value, guidPattern); + } + GUID ConvertToGuid(const std::string& value) { std::string guidString = value; diff --git a/src/AppInstallerSharedLib/Errors.cpp b/src/AppInstallerSharedLib/Errors.cpp index a8ca0dc40e..30747862ea 100644 --- a/src/AppInstallerSharedLib/Errors.cpp +++ b/src/AppInstallerSharedLib/Errors.cpp @@ -228,8 +228,14 @@ namespace AppInstaller return "Application shutdown signal received"; case APPINSTALLER_CLI_ERROR_DOWNLOAD_DEPENDENCIES: return "Failed to download package dependencies."; + case APPINSTALLER_CLI_ERROR_INVALID_RESUME_GUID: + return "Argument for resume guid is invalid."; + case APPINSTALLER_CLI_ERROR_RESUME_GUID_NOT_FOUND: + return "The guid provided does not correspond to a valid resume state."; case APPINSTALLER_CLI_ERROR_CLIENTVERSION_MISMATCH: return "The current client version did not match the client version of the saved state."; + case APPINSTALLER_CLI_ERROR_INVALID_RESUME_STATE: + return "The resume state data is invalid."; // Install errors case APPINSTALLER_CLI_ERROR_INSTALL_PACKAGE_IN_USE: diff --git a/src/AppInstallerSharedLib/Public/AppInstallerErrors.h b/src/AppInstallerSharedLib/Public/AppInstallerErrors.h index 6772f7b924..da190868d4 100644 --- a/src/AppInstallerSharedLib/Public/AppInstallerErrors.h +++ b/src/AppInstallerSharedLib/Public/AppInstallerErrors.h @@ -120,7 +120,10 @@ #define APPINSTALLER_CLI_ERROR_PACKAGE_IS_STUB ((HRESULT)0x8A150069) #define APPINSTALLER_CLI_ERROR_APPTERMINATION_RECEIVED ((HRESULT)0x8A15006A) #define APPINSTALLER_CLI_ERROR_DOWNLOAD_DEPENDENCIES ((HRESULT)0x8A15006B) -#define APPINSTALLER_CLI_ERROR_CLIENTVERSION_MISMATCH ((HRESULT)0x8A15006C) +#define APPINSTALLER_CLI_ERROR_INVALID_RESUME_GUID ((HRESULT)0x8A15006C) +#define APPINSTALLER_CLI_ERROR_RESUME_GUID_NOT_FOUND ((HRESULT)0x8A15006D) +#define APPINSTALLER_CLI_ERROR_CLIENTVERSION_MISMATCH ((HRESULT)0x8A15006E) +#define APPINSTALLER_CLI_ERROR_INVALID_RESUME_STATE ((HRESULT)0x8A15006F) // Install errors. #define APPINSTALLER_CLI_ERROR_INSTALL_PACKAGE_IN_USE ((HRESULT)0x8A150101) diff --git a/src/AppInstallerSharedLib/Public/AppInstallerStrings.h b/src/AppInstallerSharedLib/Public/AppInstallerStrings.h index 671bca37c0..0c06e64a2d 100644 --- a/src/AppInstallerSharedLib/Public/AppInstallerStrings.h +++ b/src/AppInstallerSharedLib/Public/AppInstallerStrings.h @@ -263,5 +263,9 @@ namespace AppInstaller::Utility return inputStr; } + // Returns a boolean value indicating whether the provided string is a valid guid. + bool IsValidGuidString(const std::string& value); + + // Converts the given string into a guid. GUID ConvertToGuid(const std::string& value); } From 940599fef709ae30d9144206aabb6b673f372179 Mon Sep 17 00:00:00 2001 From: Ryan Fu Date: Mon, 7 Aug 2023 10:18:13 -0700 Subject: [PATCH 10/43] fix e2E test and cleanup --- src/AppInstallerCLICore/Argument.cpp | 47 ------------------- src/AppInstallerCLICore/Argument.h | 3 -- src/AppInstallerCLICore/CheckpointManager.cpp | 1 - src/AppInstallerCLICore/CheckpointManager.h | 2 +- .../Commands/ResumeCommand.cpp | 37 +++++++++++++-- .../Workflows/ResumeFlow.cpp | 33 ++----------- .../Workflows/ResumeFlow.h | 6 --- src/AppInstallerCLIE2ETests/ResumeCommand.cs | 1 + .../Shared/Strings/en-us/winget.resw | 3 +- src/AppInstallerCLITests/ResumeFlow.cpp | 16 ++++--- .../SQLiteWrapper.cpp | 7 --- .../SQLiteWrapper.h | 3 -- 12 files changed, 50 insertions(+), 109 deletions(-) diff --git a/src/AppInstallerCLICore/Argument.cpp b/src/AppInstallerCLICore/Argument.cpp index d2a0611a57..50b25dff9f 100644 --- a/src/AppInstallerCLICore/Argument.cpp +++ b/src/AppInstallerCLICore/Argument.cpp @@ -356,53 +356,6 @@ namespace AppInstaller::CLI } } - std::string_view Argument::GetName(Execution::Args::Type type) - { - switch (type) - { - case Args::Type::Query: - return "Query"sv; - case Args::Type::MultiQuery: - return "MultiQuery"sv; - case Args::Type::Manifest: - return "Manifest"sv; - case Args::Type::Id: - return "Id"sv; - case Args::Type::Name: - return "Name"sv; - case Args::Type::Moniker: - return "Moniker"sv; - case Args::Type::Tag: - return "Tag"sv; - case Args::Type::Command: - return "Command"sv; - case Args::Type::Source: - return "Source"sv; - case Args::Type::DependencySource: - return "DependencySource"sv; - case Args::Type::Count: - return "Count"sv; - case Args::Type::Exact: - return "Exact"sv; - case Args::Type::Version: - return "Version"sv; - case Args::Type::Channel: - return "Channel"sv; - case Args::Type::Interactive: - return "Interactive"sv; - case Args::Type::Silent: - return "Silent"sv; - case Args::Type::Locale: - return "Locale"sv; - case Args::Type::InstallArchitecture: - return "Architecture"sv; - case Args::Type::InstallScope: - return "Scope"sv; - default: - THROW_HR(E_UNEXPECTED); - } - } - void Argument::GetCommon(std::vector& args) { args.push_back(ForType(Args::Type::Help)); diff --git a/src/AppInstallerCLICore/Argument.h b/src/AppInstallerCLICore/Argument.h index a5b4ebec76..e8e32e909d 100644 --- a/src/AppInstallerCLICore/Argument.h +++ b/src/AppInstallerCLICore/Argument.h @@ -178,9 +178,6 @@ namespace AppInstaller::CLI // Gets the common arguments for all commands. static void GetCommon(std::vector& args); - // Gets the name for a given argument type. - static std::string_view GetName(Execution::Args::Type type); - // Static argument validation helpers; throw CommandException when validation fails. // Requires that at most one argument from the list is present. diff --git a/src/AppInstallerCLICore/CheckpointManager.cpp b/src/AppInstallerCLICore/CheckpointManager.cpp index c04fea85fa..6bd0258d93 100644 --- a/src/AppInstallerCLICore/CheckpointManager.cpp +++ b/src/AppInstallerCLICore/CheckpointManager.cpp @@ -71,7 +71,6 @@ namespace AppInstaller::CLI::Checkpoint std::string CheckpointManager::GetClientVersion() { - // Need to throw exception if checkpoint index is null. return m_checkpointIndex->GetClientVersion(); } diff --git a/src/AppInstallerCLICore/CheckpointManager.h b/src/AppInstallerCLICore/CheckpointManager.h index 1e9c952067..201d5f3ae8 100644 --- a/src/AppInstallerCLICore/CheckpointManager.h +++ b/src/AppInstallerCLICore/CheckpointManager.h @@ -44,7 +44,7 @@ namespace AppInstaller::CLI::Checkpoint Execution::CheckpointFlag GetLastCheckpoint(int contextId); #ifndef AICLI_DISABLE_TEST_HOOKS - // Only used by unit tests for proper cleanup. + // Only used by unit tests to release any test indexes for proper cleanup. void ManualReset() { m_checkpointId = GUID_NULL; diff --git a/src/AppInstallerCLICore/Commands/ResumeCommand.cpp b/src/AppInstallerCLICore/Commands/ResumeCommand.cpp index 2288991a1b..7828ea5ff3 100644 --- a/src/AppInstallerCLICore/Commands/ResumeCommand.cpp +++ b/src/AppInstallerCLICore/Commands/ResumeCommand.cpp @@ -6,6 +6,7 @@ #include "ResumeCommand.h" #include "Workflows/ResumeFlow.h" #include "Resources.h" +#include "RootCommand.h" namespace AppInstaller::CLI { @@ -51,11 +52,37 @@ namespace AppInstaller::CLI Context& resumeContext = *resumeContextPtr; auto previousThreadGlobals = resumeContext.SetForCurrentThread(); - resumeContext << - Workflow::LoadInitialResumeState; + std::string commandName = CheckpointManager::Instance().GetCommandName(rootContextId); + std::unique_ptr commandToResume; - auto executingCommandPtr = resumeContext.GetExecutingCommand(); - Command& executingCommand = *executingCommandPtr; - executingCommand.Execute(resumeContext); + // Find the command using the command name. + AICLI_LOG(CLI, Info, << "Resuming command: " << commandName); + for (auto& command : std::make_unique()->GetCommands()) + { + if ( + Utility::CaseInsensitiveEquals(commandName, command->Name()) || + Utility::CaseInsensitiveContains(command->Aliases(), commandName) + ) + { + commandToResume = std::move(command); + break; + } + } + + THROW_HR_IF_MSG(E_UNEXPECTED, !commandToResume, "Command to resume not found."); + + resumeContext.SetExecutingCommand(commandToResume.get()); + resumeContext.SetTargetCheckpoint(CheckpointManager::Instance().GetLastCheckpoint(rootContextId)); + resumeContext.SetFlags(Execution::ContextFlag::Resume); + + CheckpointManager::Instance().Checkpoint(resumeContext, Execution::CheckpointFlag::CommandArguments); + + commandToResume->Execute(resumeContext); + + // If the resumeContext is terminated, set the termination HR to report the correct HR from the resume context. + if (resumeContext.IsTerminated()) + { + context.SetTerminationHR(resumeContext.GetTerminationHR()); + } } } diff --git a/src/AppInstallerCLICore/Workflows/ResumeFlow.cpp b/src/AppInstallerCLICore/Workflows/ResumeFlow.cpp index c176cf7f8a..809dcf3f01 100644 --- a/src/AppInstallerCLICore/Workflows/ResumeFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/ResumeFlow.cpp @@ -30,15 +30,16 @@ namespace AppInstaller::CLI::Workflow if (!std::filesystem::exists(AppInstaller::Repository::Microsoft::CheckpointIndex::GetCheckpointIndexPath(checkpointId))) { - context.Reporter.Error() << Resource::String::ResumeGuidNotFoundError << std::endl; + context.Reporter.Error() << Resource::String::ResumeGuidNotFoundError(Utility::LocIndView{ resumeGuidString }) << std::endl; AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_RESUME_GUID_NOT_FOUND); } CheckpointManager::Instance().Initialize(checkpointId); - if (AppInstaller::Runtime::GetClientVersion().get() != CheckpointManager::Instance().GetClientVersion()) + const auto& resumeStateClientVersion = CheckpointManager::Instance().GetClientVersion(); + if (AppInstaller::Runtime::GetClientVersion().get() != resumeStateClientVersion) { - context.Reporter.Error() << Resource::String::ClientVersionMismatchError << std::endl; + context.Reporter.Error() << Resource::String::ClientVersionMismatchError(Utility::LocIndView{ resumeStateClientVersion }) << std::endl; AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_CLIENTVERSION_MISMATCH); } @@ -48,30 +49,4 @@ namespace AppInstaller::CLI::Workflow AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_INVALID_RESUME_STATE); } } - - void LoadInitialResumeState(Execution::Context& context) - { - int contextId = context.GetContextId(); - std::string commandName = CheckpointManager::Instance().GetCommandName(contextId); - std::unique_ptr commandToResume; - - for (auto& command : std::make_unique()->GetCommands()) - { - if ( - Utility::CaseInsensitiveEquals(commandName, command->Name()) || - Utility::CaseInsensitiveContains(command->Aliases(), commandName) - ) - { - AICLI_LOG(CLI, Info, << "Resuming command: " << commandName); - commandToResume = std::move(command); - break; - } - } - - context.SetExecutingCommand(commandToResume.get()); - context.SetTargetCheckpoint(CheckpointManager::Instance().GetLastCheckpoint(contextId)); - context.SetFlags(Execution::ContextFlag::Resume); - - CheckpointManager::Instance().Checkpoint(context, Execution::CheckpointFlag::CommandArguments); - } } diff --git a/src/AppInstallerCLICore/Workflows/ResumeFlow.h b/src/AppInstallerCLICore/Workflows/ResumeFlow.h index c216dc920e..1935f40fc7 100644 --- a/src/AppInstallerCLICore/Workflows/ResumeFlow.h +++ b/src/AppInstallerCLICore/Workflows/ResumeFlow.h @@ -10,10 +10,4 @@ namespace AppInstaller::CLI::Workflow // Inputs: ResumeGuid // Outputs: None void EnsureSupportForResume(Execution::Context& context); - - // Loads the initial state of the context from the initialized checkpoint index. - // Required Args: None - // Inputs: None - // Outputs: None - void LoadInitialResumeState(Execution::Context& context); } diff --git a/src/AppInstallerCLIE2ETests/ResumeCommand.cs b/src/AppInstallerCLIE2ETests/ResumeCommand.cs index 2056336fa5..1d580c5fba 100644 --- a/src/AppInstallerCLIE2ETests/ResumeCommand.cs +++ b/src/AppInstallerCLIE2ETests/ResumeCommand.cs @@ -23,6 +23,7 @@ public class ResumeCommand : BaseCommand public void OneTimeSetup() { WinGetSettingsHelper.ConfigureFeature("resume", true); + WinGetSettingsHelper.ConfigureFeature("dependencies", true); WinGetSettingsHelper.ConfigureFeature("windowsFeature", true); } diff --git a/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw b/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw index 757cf9f19a..9d718fda0b 100644 --- a/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw +++ b/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw @@ -2072,7 +2072,8 @@ Please specify one of them using the --source option to proceed. {1} 'guid' is the short name form of a globally unique identifier. - Resuming the state from a different client version is not supported. + Resuming the state from a different client version is not supported: {0} + {Locked= "{0}"} Message displayed to inform the user that the client version of the resume state does not match the current client version. {0} is a placeholder for the client version that created the resume state. The resume state does not exist: {0} diff --git a/src/AppInstallerCLITests/ResumeFlow.cpp b/src/AppInstallerCLITests/ResumeFlow.cpp index a9b14f7058..a6daf55503 100644 --- a/src/AppInstallerCLITests/ResumeFlow.cpp +++ b/src/AppInstallerCLITests/ResumeFlow.cpp @@ -60,7 +60,8 @@ TEST_CASE("ResumeFlow_IndexNotFound", "[Resume]") INFO(resumeOutput.str()); REQUIRE_TERMINATED_WITH(context, APPINSTALLER_CLI_ERROR_RESUME_GUID_NOT_FOUND); - REQUIRE(resumeOutput.str().find(Resource::LocString(Resource::String::ResumeGuidNotFoundError).get()) != std::string::npos); + auto expectedMessage = Resource::String::ResumeGuidNotFoundError(AppInstaller::Utility::LocIndString(tempGuidString)); + REQUIRE(resumeOutput.str().find(Resource::LocString(expectedMessage).get()) != std::string::npos); } TEST_CASE("ResumeFlow_InvalidClientVersion", "[Resume]") @@ -74,12 +75,13 @@ TEST_CASE("ResumeFlow_InvalidClientVersion", "[Resume]") std::string tempGuidString = "{b157d11f-4487-4e03-9447-9f9d50d66d8e}"; std::string tempFileName = tempGuidString + ".db"; auto tempIndexPath = tempCheckpointIndexDirectoryPath / tempFileName; + std::string invalidClientVersion{ "1.2.3.4 "}; INFO("Using temporary file named: " << tempIndexPath); { CheckpointIndex index = CheckpointIndex::CreateNew(tempIndexPath.u8string()); - index.SetClientVersion("1.2.3.4"); + index.SetClientVersion(invalidClientVersion); } std::ostringstream resumeOutput; @@ -93,13 +95,14 @@ TEST_CASE("ResumeFlow_InvalidClientVersion", "[Resume]") INFO(resumeOutput.str()); REQUIRE_TERMINATED_WITH(context, APPINSTALLER_CLI_ERROR_CLIENTVERSION_MISMATCH); - REQUIRE(resumeOutput.str().find(Resource::LocString(Resource::String::ClientVersionMismatchError).get()) != std::string::npos); + auto expectedMessage = Resource::String::ClientVersionMismatchError(AppInstaller::Utility::LocIndString(invalidClientVersion)); + REQUIRE(resumeOutput.str().find(Resource::LocString(expectedMessage).get()) != std::string::npos); // Manually reset index to allow for proper clean up. Checkpoint::CheckpointManager::Instance().ManualReset(); } -TEST_CASE("ResumeFlow_EmptyCheckpointIndex", "Resume") +TEST_CASE("ResumeFlow_EmptyIndex", "Resume") { TestCommon::TempDirectory tempCheckpointIndexDirectory("TempCheckpointIndexDirectory", false); @@ -135,7 +138,7 @@ TEST_CASE("ResumeFlow_EmptyCheckpointIndex", "Resume") Checkpoint::CheckpointManager::Instance().ManualReset(); } -TEST_CASE("ResumeFlow_VerifyStateRemovedForSuccess", "[Resume]") +TEST_CASE("ResumeFlow_VerifyContextStateRemovedForInstallSuccess", "[Resume]") { TestCommon::TempDirectory tempCheckpointIndexDirectory("TempCheckpointIndexDirectory", false); @@ -189,7 +192,8 @@ TEST_CASE("ResumeFlow_VerifyStateRemovedForSuccess", "[Resume]") Checkpoint::CheckpointManager::Instance().ManualReset(); } -TEST_CASE("ResumeFlow_VerifyStateSavedForFailure", "[Resume]") +// TODO: This test will need to be updated once saving the resume state is restricted to certain HRs. +TEST_CASE("ResumeFlow_VerifyContextStateSavedForInstallFailure", "[Resume]") { TestCommon::TempDirectory tempCheckpointIndexDirectory("TempCheckpointIndexDirectory", false); diff --git a/src/AppInstallerRepositoryCore/SQLiteWrapper.cpp b/src/AppInstallerRepositoryCore/SQLiteWrapper.cpp index a878fd329a..38b88d6982 100644 --- a/src/AppInstallerRepositoryCore/SQLiteWrapper.cpp +++ b/src/AppInstallerRepositoryCore/SQLiteWrapper.cpp @@ -294,13 +294,6 @@ namespace AppInstaller::Repository::SQLite return type == SQLITE_NULL; } - std::string Statement::GetColumnName(int column) - { - THROW_HR_IF(E_BOUNDS, m_state != State::HasRow); - const char* columnName = sqlite3_column_name(m_stmt.get(), column); - return std::string{ columnName }; - } - void Statement::Reset() { AICLI_LOG(SQL, Verbose, << "Reset statement #" << m_connectionId << '-' << m_id); diff --git a/src/AppInstallerRepositoryCore/SQLiteWrapper.h b/src/AppInstallerRepositoryCore/SQLiteWrapper.h index bec857604b..3a5913ce95 100644 --- a/src/AppInstallerRepositoryCore/SQLiteWrapper.h +++ b/src/AppInstallerRepositoryCore/SQLiteWrapper.h @@ -243,9 +243,6 @@ namespace AppInstaller::Repository::SQLite // The index is 0 based. bool GetColumnIsNull(int column); - // Gets the name of the specified column from the current row. - std::string GetColumnName(int column); - // Gets the value of the specified column from the current row. // The index is 0 based. template From ba3bcb618c7e4135908401a0cf75e0739c7455f4 Mon Sep 17 00:00:00 2001 From: Ryan Fu Date: Mon, 7 Aug 2023 10:51:24 -0700 Subject: [PATCH 11/43] fix CLIcore filter --- .../AppInstallerCLICore.vcxproj.filters | 6 +++--- src/AppInstallerCLICore/CheckpointManager.cpp | 9 +++++++-- .../Commands/InstallCommand.cpp | 7 ++++--- src/AppInstallerCLICore/Commands/ResumeCommand.cpp | 14 ++++++++------ src/AppInstallerCLICore/Workflows/ResumeFlow.cpp | 10 +++++----- 5 files changed, 27 insertions(+), 19 deletions(-) diff --git a/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj.filters b/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj.filters index d4b1df0aed..b2f33e04b2 100644 --- a/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj.filters +++ b/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj.filters @@ -433,12 +433,12 @@ Commands - - Source Files - Workflows + + Source Files + diff --git a/src/AppInstallerCLICore/CheckpointManager.cpp b/src/AppInstallerCLICore/CheckpointManager.cpp index 6bd0258d93..786b076b72 100644 --- a/src/AppInstallerCLICore/CheckpointManager.cpp +++ b/src/AppInstallerCLICore/CheckpointManager.cpp @@ -15,7 +15,8 @@ namespace AppInstaller::CLI::Checkpoint // Initialize should only be called once. THROW_HR_IF(E_UNEXPECTED, m_checkpointId != GUID_NULL); - if (checkpointId == GUID_NULL) + bool createCheckpointIndex = (checkpointId == GUID_NULL); + if (createCheckpointIndex) { std::ignore = CoCreateGuid(&m_checkpointId); AICLI_LOG(CLI, Info, << "Creating checkpoint index with guid: " << m_checkpointId); @@ -35,7 +36,11 @@ namespace AppInstaller::CLI::Checkpoint } m_checkpointIndex = std::move(checkpointIndex); - m_checkpointIndex->SetClientVersion(AppInstaller::Runtime::GetClientVersion()); + + if (createCheckpointIndex) + { + m_checkpointIndex->SetClientVersion(AppInstaller::Runtime::GetClientVersion()); + } } void CheckpointManager::Checkpoint(Execution::Context& context, Execution::CheckpointFlag checkpointFlag) diff --git a/src/AppInstallerCLICore/Commands/InstallCommand.cpp b/src/AppInstallerCLICore/Commands/InstallCommand.cpp index 08dfba68f9..5a9c5edbb1 100644 --- a/src/AppInstallerCLICore/Commands/InstallCommand.cpp +++ b/src/AppInstallerCLICore/Commands/InstallCommand.cpp @@ -109,9 +109,10 @@ namespace AppInstaller::CLI if (resumeExperimentalFeatureEnabled && WI_IsFlagClear(context.GetFlags(), Execution::ContextFlag::Resume)) { - CheckpointManager::Instance().Initialize(); - CheckpointManager::Instance().AddContext(context.GetContextId()); - CheckpointManager::Instance().Checkpoint(context, Execution::CheckpointFlag::CommandArguments); + auto& checkpointManager = CheckpointManager::Instance(); + checkpointManager.Initialize(); + checkpointManager.AddContext(context.GetContextId()); + checkpointManager.Checkpoint(context, Execution::CheckpointFlag::CommandArguments); } context.SetFlags(ContextFlag::ShowSearchResultsOnPartialFailure); diff --git a/src/AppInstallerCLICore/Commands/ResumeCommand.cpp b/src/AppInstallerCLICore/Commands/ResumeCommand.cpp index 7828ea5ff3..f73ded8b46 100644 --- a/src/AppInstallerCLICore/Commands/ResumeCommand.cpp +++ b/src/AppInstallerCLICore/Commands/ResumeCommand.cpp @@ -3,10 +3,10 @@ #include "pch.h" #include "AppInstallerRuntime.h" #include "CheckpointManager.h" -#include "ResumeCommand.h" -#include "Workflows/ResumeFlow.h" #include "Resources.h" +#include "ResumeCommand.h" #include "RootCommand.h" +#include "Workflows/ResumeFlow.h" namespace AppInstaller::CLI { @@ -46,13 +46,15 @@ namespace AppInstaller::CLI return; } - int rootContextId = CheckpointManager::Instance().GetFirstContextId(); + auto& checkpointManager = CheckpointManager::Instance(); + + int rootContextId = checkpointManager.GetFirstContextId(); auto resumeContextPtr = context.CreateEmptyContext(rootContextId); Context& resumeContext = *resumeContextPtr; auto previousThreadGlobals = resumeContext.SetForCurrentThread(); - std::string commandName = CheckpointManager::Instance().GetCommandName(rootContextId); + std::string commandName = checkpointManager.GetCommandName(rootContextId); std::unique_ptr commandToResume; // Find the command using the command name. @@ -72,10 +74,10 @@ namespace AppInstaller::CLI THROW_HR_IF_MSG(E_UNEXPECTED, !commandToResume, "Command to resume not found."); resumeContext.SetExecutingCommand(commandToResume.get()); - resumeContext.SetTargetCheckpoint(CheckpointManager::Instance().GetLastCheckpoint(rootContextId)); + resumeContext.SetTargetCheckpoint(checkpointManager.GetLastCheckpoint(rootContextId)); resumeContext.SetFlags(Execution::ContextFlag::Resume); - CheckpointManager::Instance().Checkpoint(resumeContext, Execution::CheckpointFlag::CommandArguments); + checkpointManager.Checkpoint(resumeContext, Execution::CheckpointFlag::CommandArguments); commandToResume->Execute(resumeContext); diff --git a/src/AppInstallerCLICore/Workflows/ResumeFlow.cpp b/src/AppInstallerCLICore/Workflows/ResumeFlow.cpp index 809dcf3f01..90872b220e 100644 --- a/src/AppInstallerCLICore/Workflows/ResumeFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/ResumeFlow.cpp @@ -2,10 +2,9 @@ // Licensed under the MIT License. #include "pch.h" #include "AppInstallerRuntime.h" -#include "ResumeFlow.h" #include "CheckpointManager.h" #include "Microsoft/CheckpointIndex.h" -#include "Commands/RootCommand.h" +#include "ResumeFlow.h" using namespace AppInstaller::CLI::Execution; using namespace AppInstaller::CLI::Checkpoint; @@ -34,16 +33,17 @@ namespace AppInstaller::CLI::Workflow AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_RESUME_GUID_NOT_FOUND); } - CheckpointManager::Instance().Initialize(checkpointId); + auto& checkpointManager = CheckpointManager::Instance(); + checkpointManager.Initialize(checkpointId); - const auto& resumeStateClientVersion = CheckpointManager::Instance().GetClientVersion(); + const auto& resumeStateClientVersion = checkpointManager.GetClientVersion(); if (AppInstaller::Runtime::GetClientVersion().get() != resumeStateClientVersion) { context.Reporter.Error() << Resource::String::ClientVersionMismatchError(Utility::LocIndView{ resumeStateClientVersion }) << std::endl; AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_CLIENTVERSION_MISMATCH); } - if (!CheckpointManager::Instance().HasContext()) + if (!checkpointManager.HasContext()) { context.Reporter.Error() << Resource::String::ResumeStateDataNotFoundError << std::endl; AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_INVALID_RESUME_STATE); From 8d9a3b4cef17e2daf9390ca197cf90f2fb0f8667 Mon Sep 17 00:00:00 2001 From: Ryan Fu Date: Wed, 9 Aug 2023 17:24:47 -0700 Subject: [PATCH 12/43] save work --- src/AppInstallerCLICore/Argument.cpp | 4 +- src/AppInstallerCLICore/CheckpointManager.cpp | 175 ++---------------- src/AppInstallerCLICore/CheckpointManager.h | 46 +---- src/AppInstallerCLICore/Command.cpp | 17 +- src/AppInstallerCLICore/Command.h | 2 + .../Commands/InstallCommand.cpp | 29 +-- .../Commands/InstallCommand.h | 2 + .../Commands/ResumeCommand.cpp | 44 ++--- src/AppInstallerCLICore/Core.cpp | 2 + src/AppInstallerCLICore/ExecutionArgs.h | 2 +- src/AppInstallerCLICore/ExecutionContext.cpp | 93 ++++++++-- src/AppInstallerCLICore/ExecutionContext.h | 60 +++--- .../Workflows/ResumeFlow.cpp | 26 ++- .../Workflows/ResumeFlow.h | 15 ++ .../Workflows/WorkflowBase.cpp | 7 +- src/AppInstallerCLITests/ResumeFlow.cpp | 8 +- .../Checkpoint_1_0/CheckpointContextTable.cpp | 23 ++- .../CheckpointMetadataTable.cpp | 16 +- 18 files changed, 269 insertions(+), 302 deletions(-) diff --git a/src/AppInstallerCLICore/Argument.cpp b/src/AppInstallerCLICore/Argument.cpp index 50b25dff9f..4d88b35222 100644 --- a/src/AppInstallerCLICore/Argument.cpp +++ b/src/AppInstallerCLICore/Argument.cpp @@ -175,7 +175,7 @@ namespace AppInstaller::CLI return { type, "installed"_liv, ArgTypeCategory::None }; // Resume command - case Execution::Args::Type::ResumeGuid: + case Execution::Args::Type::ResumeId: return { type, "resume-guid"_liv, 'g', ArgTypeCategory::None }; // Configuration commands @@ -349,7 +349,7 @@ namespace AppInstaller::CLI return Argument{ type, Resource::String::DownloadDirectoryArgumentDescription, ArgumentType::Standard, Argument::Visibility::Help, false }; case Args::Type::InstallerType: return Argument{ type, Resource::String::InstallerTypeArgumentDescription, ArgumentType::Standard, Argument::Visibility::Help, false }; - case Args::Type::ResumeGuid: + case Args::Type::ResumeId: return Argument{ type, Resource::String::ResumeGuidArgumentDescription, ArgumentType::Standard, true }; default: THROW_HR(E_UNEXPECTED); diff --git a/src/AppInstallerCLICore/CheckpointManager.cpp b/src/AppInstallerCLICore/CheckpointManager.cpp index 786b076b72..f7c511c1c9 100644 --- a/src/AppInstallerCLICore/CheckpointManager.cpp +++ b/src/AppInstallerCLICore/CheckpointManager.cpp @@ -10,68 +10,41 @@ namespace AppInstaller::CLI::Checkpoint { - void CheckpointManager::Initialize(GUID checkpointId) + CheckpointManager::CheckpointManager(GUID id) { - // Initialize should only be called once. - THROW_HR_IF(E_UNEXPECTED, m_checkpointId != GUID_NULL); - - bool createCheckpointIndex = (checkpointId == GUID_NULL); - if (createCheckpointIndex) + if (m_checkpointId == GUID_NULL) { std::ignore = CoCreateGuid(&m_checkpointId); - AICLI_LOG(CLI, Info, << "Creating checkpoint index with guid: " << m_checkpointId); - } - else - { - m_checkpointId = checkpointId; - AICLI_LOG(CLI, Info, << "Opening checkpoint index with guid: " << m_checkpointId); } + AICLI_LOG(CLI, Info, << "Checkpoint manager id: " << m_checkpointId); + auto openDisposition = AppInstaller::Repository::Microsoft::SQLiteStorageBase::OpenDisposition::ReadWrite; auto checkpointIndex = AppInstaller::Repository::Microsoft::CheckpointIndex::OpenOrCreateDefault(m_checkpointId, openDisposition); if (!checkpointIndex) { AICLI_LOG(CLI, Error, << "Unable to open checkpoint index."); - THROW_HR_MSG(HRESULT_FROM_WIN32(ERROR_NOT_FOUND), "The checkpoint state could not be found."); + // TODO: Handle failure to open index gracefully. } m_checkpointIndex = std::move(checkpointIndex); - - if (createCheckpointIndex) - { - m_checkpointIndex->SetClientVersion(AppInstaller::Runtime::GetClientVersion()); - } + m_checkpointIndex->SetClientVersion(AppInstaller::Runtime::GetClientVersion()); } - void CheckpointManager::Checkpoint(Execution::Context& context, Execution::CheckpointFlag checkpointFlag) + template<> + void CheckpointManager::RecordContextData(std::string_view checkpointName, Manifest::ManifestInstaller installer) { - Execution::CheckpointFlag contextCheckpointFlag = context.GetTargetCheckpoint(); + // Capture all relevant data from the installer. + m_checkpointIndex->SetCommandName(0, installer.PackageFamilyName); + }; - // If the context target checkpoint is ahead or equal to the current checkpoint, we have previously executed this state, load checkpoint state from index. - // If the context target checkpoint is behind the current checkpoint, we have not yet processed this state, save checkpoint state to index. - if (contextCheckpointFlag > checkpointFlag || contextCheckpointFlag == checkpointFlag) - { - LoadCheckpoint(context, checkpointFlag); - } - else if (contextCheckpointFlag < checkpointFlag) - { - SaveCheckpoint(context, checkpointFlag); - } - } - - bool CheckpointManager::HasContext() + void CheckpointManager::RecordMetadata( + std::string_view checkpointName, + std::string_view commandName, + std::string_view commandLineString, + std::string clientVersion) { - return !m_checkpointIndex->IsEmpty(); - } - - void CheckpointManager::AddContext(int contextId) - { - m_checkpointIndex->AddContext(contextId); - } - - void CheckpointManager::RemoveContext(int contextId) - { - m_checkpointIndex->RemoveContext(contextId); + // Capture these arguments from the checkpoint metadata. } std::string CheckpointManager::GetClientVersion() @@ -84,35 +57,8 @@ namespace AppInstaller::CLI::Checkpoint return m_checkpointIndex->GetCommandName(contextId); } - int CheckpointManager::GetFirstContextId() - { - return m_checkpointIndex->GetFirstContextId(); - } - - Execution::CheckpointFlag CheckpointManager::GetLastCheckpoint(int contextId) - { - return static_cast(m_checkpointIndex->GetLastCheckpoint(contextId)); - } - -#ifndef AICLI_DISABLE_TEST_HOOKS - static bool s_MockCheckpointManagerCleanUp_Override = false; - - void TestHook_MockCheckpointManagerCleanUp_Override(bool status) - { - s_MockCheckpointManagerCleanUp_Override = status; - } -#endif - void CheckpointManager::CleanUpIndex() { -#ifndef AICLI_DISABLE_TEST_HOOKS - // Unit tests will handle clean up so this hook is needed to avoid attempting to reset and clean up again. - if (s_MockCheckpointManagerCleanUp_Override) - { - return; - } -#endif - bool isIndexEmpty = m_checkpointIndex->IsEmpty(); m_checkpointIndex.reset(); @@ -132,90 +78,9 @@ namespace AppInstaller::CLI::Checkpoint CleanUpIndex(); } - void CheckpointManager::SaveCheckpoint(Execution::Context& context, Execution::CheckpointFlag flag) - { - switch (flag) - { - case Execution::CheckpointFlag::CommandArguments: - RecordContextArgsToIndex(context); - break; - default: - THROW_HR(E_UNEXPECTED); - } - - m_checkpointIndex->SetLastCheckpoint(context.GetContextId(), static_cast(flag)); - context.SetCurrentCheckpoint(flag); - } - - void CheckpointManager::LoadCheckpoint(Execution::Context& context, Execution::CheckpointFlag flag) - { - switch (flag) - { - case Execution::CheckpointFlag::CommandArguments: - PopulateContextArgsFromIndex(context); - break; - default: - THROW_HR(E_UNEXPECTED); - } - - context.SetCurrentCheckpoint(flag); - } - - void CheckpointManager::PopulateContextArgsFromIndex(Execution::Context& context) + std::string CheckpointManager::GetArguments() { - int contextId = context.GetContextId(); - - const auto& executingCommand = context.GetExecutingCommand(); - if (executingCommand != nullptr) - { - const auto& commandArguments = executingCommand->GetArguments(); - for (const auto& argument : commandArguments) - { - if (m_checkpointIndex->ContainsArgument(contextId, argument.Name())) - { - Execution::Args::Type executionArgsType = argument.ExecArgType(); - if (argument.Type() == ArgumentType::Flag) - { - if (m_checkpointIndex->GetBoolArgument(contextId, argument.Name())) - { - context.Args.AddArg(executionArgsType); - } - } - else - { - context.Args.AddArg(executionArgsType, m_checkpointIndex->GetStringArgument(contextId, argument.Name())); - } - } - } - } - } - - void CheckpointManager::RecordContextArgsToIndex(Execution::Context& context) - { - int contextId = context.GetContextId(); - - const auto& executingCommand = context.GetExecutingCommand(); - if (executingCommand != nullptr) - { - m_checkpointIndex->SetCommandName(contextId, executingCommand->Name()); - - const auto& commandArguments = executingCommand->GetArguments(); - for (const auto& argument : commandArguments) - { - Execution::Args::Type type = argument.ExecArgType(); - if (context.Args.Contains(type)) - { - if (argument.Type() == ArgumentType::Flag) - { - m_checkpointIndex->UpdateArgument(contextId, argument.Name(), true); - } - else - { - const auto& argumentValue = context.Args.GetArg(type); - m_checkpointIndex->UpdateArgument(contextId, argument.Name(), argumentValue); - } - } - } - } + // Return the actual command line arguments. + m_checkpointIndex->GetCommandName(0); } } diff --git a/src/AppInstallerCLICore/CheckpointManager.h b/src/AppInstallerCLICore/CheckpointManager.h index 201d5f3ae8..a3027e5dd7 100644 --- a/src/AppInstallerCLICore/CheckpointManager.h +++ b/src/AppInstallerCLICore/CheckpointManager.h @@ -3,6 +3,7 @@ #pragma once #include "ExecutionContext.h" +#include "Microsoft/CheckpointIndex.h" #include namespace AppInstaller::Repository::Microsoft @@ -14,57 +15,24 @@ namespace AppInstaller::CLI::Checkpoint { struct CheckpointManager { - CheckpointManager(const CheckpointManager&) = delete; - CheckpointManager& operator=(const CheckpointManager&) = delete; - CheckpointManager(CheckpointManager&&) = delete; - CheckpointManager& operator=(CheckpointManager&&) = delete; - - static CheckpointManager& Instance() - { - static CheckpointManager checkpointManager; - return checkpointManager; - } - - void Initialize(GUID checkpointId = {}); - - void Checkpoint(Execution::Context& context, Execution::CheckpointFlag flag); - - bool HasContext(); - - void AddContext(int contextId); - - void RemoveContext(int contextId); + CheckpointManager(GUID id = {}); + ~CheckpointManager(); std::string GetClientVersion(); std::string GetCommandName(int contextId); - int GetFirstContextId(); - - Execution::CheckpointFlag GetLastCheckpoint(int contextId); + std::string GetArguments(); -#ifndef AICLI_DISABLE_TEST_HOOKS - // Only used by unit tests to release any test indexes for proper cleanup. - void ManualReset() - { - m_checkpointId = GUID_NULL; - m_checkpointIndex.reset(); - } -#endif + void RecordMetadata(std::string_view checkpointName, std::string_view commandName, std::string_view commandLineString, std::string clientVersion); + template + void RecordContextData(std::string_view checkpointName, T data) {}; private: - CheckpointManager() = default; - ~CheckpointManager(); GUID m_checkpointId = {}; std::shared_ptr m_checkpointIndex = nullptr; void CleanUpIndex(); - - void SaveCheckpoint(Execution::Context& context, Execution::CheckpointFlag flag); - void LoadCheckpoint(Execution::Context& context, Execution::CheckpointFlag flag); - - void PopulateContextArgsFromIndex(Execution::Context& context); - void RecordContextArgsToIndex(Execution::Context& context); }; } diff --git a/src/AppInstallerCLICore/Command.cpp b/src/AppInstallerCLICore/Command.cpp index 2d82fd28c9..9260945314 100644 --- a/src/AppInstallerCLICore/Command.cpp +++ b/src/AppInstallerCLICore/Command.cpp @@ -840,7 +840,7 @@ namespace AppInstaller::CLI void Command::Complete(Execution::Context&, Execution::Args::Type) const { - // Derived commands must suppy context sensitive argument values. + // Derived commands must supply context sensitive argument values. } void Command::Execute(Execution::Context& context) const @@ -853,6 +853,16 @@ namespace AppInstaller::CLI throw GroupPolicyException(Settings::TogglePolicy::Policy::WinGet); } + if (Settings::ExperimentalFeature::IsEnabled(ExperimentalFeature::Feature::Resume)) + { + // Only the resume command requires a resume id. If not present, create checkpoint index and capture initial arguments. + if (!context.Args.Contains(Execution::Args::Type::ResumeId)) + { + AICLI_LOG(CLI, Info, << "Initializing checkpoint manager."); + context.Checkpoint("CheckpointInitialized", {}); + } + } + AICLI_LOG(CLI, Info, << "Executing command: " << Name()); if (context.Args.Contains(Execution::Args::Type::Help)) { @@ -875,6 +885,11 @@ namespace AppInstaller::CLI context.Reporter.PromptForEnter(); } } + + void Command::Resume(Execution::Context& context) const + { + // Command must implement this function, which should load everything from the context. + } void Command::SelectCurrentCommandIfUnrecognizedSubcommandFound(bool value) { diff --git a/src/AppInstallerCLICore/Command.h b/src/AppInstallerCLICore/Command.h index 0f9a7d14eb..30326c24e4 100644 --- a/src/AppInstallerCLICore/Command.h +++ b/src/AppInstallerCLICore/Command.h @@ -112,6 +112,8 @@ namespace AppInstaller::CLI virtual void Execute(Execution::Context& context) const; + virtual void Resume(Execution::Context& context) const; + protected: void SelectCurrentCommandIfUnrecognizedSubcommandFound(bool value); diff --git a/src/AppInstallerCLICore/Commands/InstallCommand.cpp b/src/AppInstallerCLICore/Commands/InstallCommand.cpp index 5a9c5edbb1..654b219d4e 100644 --- a/src/AppInstallerCLICore/Commands/InstallCommand.cpp +++ b/src/AppInstallerCLICore/Commands/InstallCommand.cpp @@ -8,6 +8,7 @@ #include "Workflows/InstallFlow.h" #include "Workflows/UpdateFlow.h" #include "Workflows/MultiQueryFlow.h" +#include "Workflows/ResumeFlow.h" #include "Workflows/WorkflowBase.h" #include "Resources.h" @@ -103,18 +104,16 @@ namespace AppInstaller::CLI Argument::ValidateCommonArguments(execArgs); } - void InstallCommand::ExecuteInternal(Context& context) const + void InstallCommand::Resume(Context& context) const { - bool resumeExperimentalFeatureEnabled = Settings::ExperimentalFeature::IsEnabled(Settings::ExperimentalFeature::Feature::Resume); + context.LoadCheckpoints(); + context.SetFlags(Execution::ContextFlag::Resume); + context.DisableWorkflowExecution(true); + ExecuteInternal(context); + } - if (resumeExperimentalFeatureEnabled && WI_IsFlagClear(context.GetFlags(), Execution::ContextFlag::Resume)) - { - auto& checkpointManager = CheckpointManager::Instance(); - checkpointManager.Initialize(); - checkpointManager.AddContext(context.GetContextId()); - checkpointManager.Checkpoint(context, Execution::CheckpointFlag::CommandArguments); - } - + void InstallCommand::ExecuteInternal(Context& context) const + { context.SetFlags(ContextFlag::ShowSearchResultsOnPartialFailure); if (context.Args.Contains(Execution::Args::Type::Manifest)) @@ -123,6 +122,7 @@ namespace AppInstaller::CLI Workflow::ReportExecutionStage(ExecutionStage::Discovery) << Workflow::GetManifestFromArg << Workflow::SelectInstaller << + Workflow::Checkpoint("InstallerSelected"sv, {Execution::Data::Installer}) << Workflow::EnsureApplicableInstaller << Workflow::InstallSinglePackage; } @@ -153,14 +153,5 @@ namespace AppInstaller::CLI context << Workflow::InstallOrUpgradeSinglePackage(OperationType::Install); } } - - if (resumeExperimentalFeatureEnabled) - { - // TODO: Only allow certain termination HRs to persist the checkpoint index. - if (!context.IsTerminated()) - { - CheckpointManager::Instance().RemoveContext(context.GetContextId()); - } - } } } diff --git a/src/AppInstallerCLICore/Commands/InstallCommand.h b/src/AppInstallerCLICore/Commands/InstallCommand.h index ab41da9bff..190c984661 100644 --- a/src/AppInstallerCLICore/Commands/InstallCommand.h +++ b/src/AppInstallerCLICore/Commands/InstallCommand.h @@ -16,6 +16,8 @@ namespace AppInstaller::CLI void Complete(Execution::Context& context, Execution::Args::Type valueType) const override; + void Resume(Execution::Context& context) const override; + Utility::LocIndView HelpLink() const override; protected: diff --git a/src/AppInstallerCLICore/Commands/ResumeCommand.cpp b/src/AppInstallerCLICore/Commands/ResumeCommand.cpp index f73ded8b46..9f7c2064de 100644 --- a/src/AppInstallerCLICore/Commands/ResumeCommand.cpp +++ b/src/AppInstallerCLICore/Commands/ResumeCommand.cpp @@ -2,22 +2,21 @@ // Licensed under the MIT License. #include "pch.h" #include "AppInstallerRuntime.h" -#include "CheckpointManager.h" #include "Resources.h" #include "ResumeCommand.h" #include "RootCommand.h" +#include "Microsoft/CheckpointIndex.h" #include "Workflows/ResumeFlow.h" namespace AppInstaller::CLI { using namespace std::string_view_literals; using namespace Execution; - using namespace Checkpoint; std::vector ResumeCommand::GetArguments() const { return { - Argument::ForType(Execution::Args::Type::ResumeGuid), + Argument::ForType(Execution::Args::Type::ResumeId), }; } @@ -38,26 +37,29 @@ namespace AppInstaller::CLI void ResumeCommand::ExecuteInternal(Execution::Context& context) const { - context << - Workflow::EnsureSupportForResume; - - if (context.IsTerminated()) + std::string resumeGuidString { context.Args.GetArg(Execution::Args::Type::ResumeId) }; + if (!Utility::IsValidGuidString(resumeGuidString)) { - return; + context.Reporter.Error() << Resource::String::InvalidResumeGuidError(Utility::LocIndView{ resumeGuidString }) << std::endl; + AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_INVALID_RESUME_GUID); } - auto& checkpointManager = CheckpointManager::Instance(); + GUID checkpointId = Utility::ConvertToGuid(resumeGuidString); - int rootContextId = checkpointManager.GetFirstContextId(); - auto resumeContextPtr = context.CreateEmptyContext(rootContextId); + if (!std::filesystem::exists(AppInstaller::Repository::Microsoft::CheckpointIndex::GetCheckpointIndexPath(checkpointId))) + { + context.Reporter.Error() << Resource::String::ResumeGuidNotFoundError(Utility::LocIndView{ resumeGuidString }) << std::endl; + AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_RESUME_GUID_NOT_FOUND); + } - Context& resumeContext = *resumeContextPtr; - auto previousThreadGlobals = resumeContext.SetForCurrentThread(); + Execution::Context resumeContext{ std::cout, std::cin }; + auto previousThreadGlobals = context.SetForCurrentThread(); + context.EnableSignalTerminationHandler(); - std::string commandName = checkpointManager.GetCommandName(rootContextId); + std::string commandName = context.GetCheckpointCommand(); std::unique_ptr commandToResume; - // Find the command using the command name. + // Find the command using the root command. AICLI_LOG(CLI, Info, << "Resuming command: " << commandName); for (auto& command : std::make_unique()->GetCommands()) { @@ -74,17 +76,7 @@ namespace AppInstaller::CLI THROW_HR_IF_MSG(E_UNEXPECTED, !commandToResume, "Command to resume not found."); resumeContext.SetExecutingCommand(commandToResume.get()); - resumeContext.SetTargetCheckpoint(checkpointManager.GetLastCheckpoint(rootContextId)); resumeContext.SetFlags(Execution::ContextFlag::Resume); - - checkpointManager.Checkpoint(resumeContext, Execution::CheckpointFlag::CommandArguments); - - commandToResume->Execute(resumeContext); - - // If the resumeContext is terminated, set the termination HR to report the correct HR from the resume context. - if (resumeContext.IsTerminated()) - { - context.SetTerminationHR(resumeContext.GetTerminationHR()); - } + commandToResume->Resume(resumeContext); } } diff --git a/src/AppInstallerCLICore/Core.cpp b/src/AppInstallerCLICore/Core.cpp index 8c887cc1d1..92ca1615c9 100644 --- a/src/AppInstallerCLICore/Core.cpp +++ b/src/AppInstallerCLICore/Core.cpp @@ -104,6 +104,8 @@ namespace AppInstaller::CLI return strstr.str(); }()); + + context.SetCommandLineArgs(utf8Args); Invocation invocation{ std::move(utf8Args) }; // The root command is our fallback in the event of very bad or very little input diff --git a/src/AppInstallerCLICore/ExecutionArgs.h b/src/AppInstallerCLICore/ExecutionArgs.h index 11c9ed1dfc..bf194c122b 100644 --- a/src/AppInstallerCLICore/ExecutionArgs.h +++ b/src/AppInstallerCLICore/ExecutionArgs.h @@ -106,7 +106,7 @@ namespace AppInstaller::CLI::Execution PinInstalled, // Resume Command - ResumeGuid, + ResumeId, // Configuration ConfigurationFile, diff --git a/src/AppInstallerCLICore/ExecutionContext.cpp b/src/AppInstallerCLICore/ExecutionContext.cpp index 1aedfc4c1c..c489d89189 100644 --- a/src/AppInstallerCLICore/ExecutionContext.cpp +++ b/src/AppInstallerCLICore/ExecutionContext.cpp @@ -5,10 +5,14 @@ #include "COMContext.h" #include "Argument.h" #include "winget/UserSettings.h" +#include "winget/Runtime.h" +#include "CheckpointManager.h" +#include "Command.h" namespace AppInstaller::CLI::Execution { using namespace Settings; + using namespace Checkpoint; namespace { @@ -248,8 +252,6 @@ namespace AppInstaller::CLI::Execution } } - int Context::s_contextCount = 0; - Context::~Context() { if (m_disableSignalTerminationHandlerOnExit) @@ -272,19 +274,6 @@ namespace AppInstaller::CLI::Execution return clone; } - std::unique_ptr Context::CreateEmptyContext(int contextId) - { - auto emptyContext = std::make_unique(Reporter, m_threadGlobals); - // If the parent is hooked up to the CTRL signal, have the clone be as well - if (m_disableSignalTerminationHandlerOnExit) - { - emptyContext->EnableSignalTerminationHandler(); - } - - emptyContext->SetContextId(contextId); - return emptyContext; - } - void Context::CopyArgsToSubContext(Context* subContext) { auto argProperties = ArgumentCommon::GetFromExecArgs(Args); @@ -422,4 +411,78 @@ namespace AppInstaller::CLI::Execution return SignalTerminationHandler::Instance().WaitForAppShutdownEvent(); } #endif + + std::string Context::GetCheckpointCommand() + { + return m_checkpointManager->GetCommandName(); + } + + void Context::LoadCheckpoints() + { + InitializeCheckpointManager(); + + // Call a function here that retrieves the checkpoint in reverse order so that the entire state is completely populated. + // Retreives all metadata from the stored info in the checkpoint index and applies the state change to the context. + } + + // Initialized the checkpoint manager if it does not exist, then captures the automatic metadata as well as the context data. + void Context::Checkpoint(std::string_view checkpointName, std::vector contextData) + { + InitializeCheckpointManager(); + + // Maybe store these in the checkpoint manager so that we don't have to compute them every time. + const auto& clientVersion = AppInstaller::Runtime::GetClientVersion().get(); + const auto& command = GetExecutingCommand(); + + std::string_view commandName; + if (command != nullptr) + { + commandName = command->Name(); + } + + m_checkpointManager->RecordMetadata(checkpointName, commandName, m_commandLineString, clientVersion); + + for (Execution::Data data : contextData) + { + switch (data) + { + case Execution::Data::Installer: + m_checkpointManager->RecordContextData(checkpointName, Get()); + default: + THROW_HR(HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED)); + } + } + } + + void Context::SetCommandArguments(std::vector args) + { + std::stringstream strstr; + for (const auto& arg : args) + { + strstr << arg << ' '; + } + + const std::string& commandLine = strstr.str(); + } + + std::vector Context::GetCommandArguments() + { + return m_commandLineArgs; + } + + void Context::InitializeCheckpointManager() + { + if (!m_checkpointManager) + { + if (Args.Contains(Execution::Args::Type::ResumeId)) + { + GUID resumeGuid = Utility::ConvertToGuid(std::string{ Args.GetArg(Execution::Args::Type::ResumeId) }); + m_checkpointManager = std::make_unique(resumeGuid); + } + else + { + m_checkpointManager = std::make_unique(); + } + } + } } diff --git a/src/AppInstallerCLICore/ExecutionContext.h b/src/AppInstallerCLICore/ExecutionContext.h index 38e7a1c5c1..409d6ce583 100644 --- a/src/AppInstallerCLICore/ExecutionContext.h +++ b/src/AppInstallerCLICore/ExecutionContext.h @@ -5,6 +5,7 @@ #include "ExecutionReporter.h" #include "ExecutionArgs.h" #include "ExecutionContextData.h" +#include "CheckpointManager.h" #include "CompletionData.h" #include @@ -52,12 +53,6 @@ namespace AppInstaller::CLI::Workflow namespace AppInstaller::CLI::Execution { - enum class CheckpointFlag : uint32_t - { - None, - CommandArguments, - }; - // bit masks used as Context flags enum class ContextFlag : int { @@ -88,12 +83,12 @@ namespace AppInstaller::CLI::Execution // arguments via Execution::Args. struct Context : EnumBasedVariantMap { - Context(std::ostream& out, std::istream& in) : Reporter(out, in) { s_contextCount++; } + Context(std::ostream& out, std::istream& in) : Reporter(out, in) {} // Constructor for creating a sub-context. Context(Execution::Reporter& reporter, ThreadLocalStorage::WingetThreadGlobals& threadGlobals) : Reporter(reporter, Execution::Reporter::clone_t{}), - m_threadGlobals(threadGlobals, ThreadLocalStorage::WingetThreadGlobals::create_sub_thread_globals_t{}) { s_contextCount++; } + m_threadGlobals(threadGlobals, ThreadLocalStorage::WingetThreadGlobals::create_sub_thread_globals_t{}) {} virtual ~Context(); @@ -106,9 +101,6 @@ namespace AppInstaller::CLI::Execution // Creates a child of this context. virtual std::unique_ptr CreateSubContext(); - // Creates an empty context. - virtual std::unique_ptr CreateEmptyContext(int contextId = 0); - // Enables reception of CTRL signals and window messages. void EnableSignalTerminationHandler(bool enabled = true); @@ -170,20 +162,30 @@ namespace AppInstaller::CLI::Execution // Enable tests to override behavior bool ShouldExecuteWorkflowTask(const Workflow::WorkflowTask& task); #endif - // Gets the id of the context. - int GetContextId() { return m_contextId; }; + // Sets the current checkpoint flag of the context. + void SetCurrentCheckpoint(std::string_view checkpointName) { m_checkpoint = checkpointName; } - // Gets the current checkpoint of the context. - CheckpointFlag GetCurrentCheckpoint() { return m_currentCheckpoint; }; + // Retrieves the checkpoints stored in the checkpoint index and populates the context state. + void LoadCheckpoints(); - // Sets the current checkpoint flag of the context. - void SetCurrentCheckpoint(Execution::CheckpointFlag flag) { m_currentCheckpoint = flag; } + // Records the provided context data to the checkpoint index. If it already exists, loads the context instead. + void Checkpoint(std::string_view checkpointName, std::vector contextData); + + // Gets the target checkpoint of the resume state. + std::string_view GetCheckpoint() { return m_checkpoint; }; + + std::string GetCheckpointCommand(); - // Gets the target checkpoint of the context. - CheckpointFlag GetTargetCheckpoint() { return m_targetCheckpoint; }; + std::vector GetCommandLineArgs() { return m_commandLineArgs; }; + + std::vector GetCommandArguments(); + + void SetCommandArguments(std::vector args); + + void DisableWorkflowExecution(bool state) { m_disableWorkflowExecution = state; }; + + bool ShouldExecuteWorkflow() { return !m_disableWorkflowExecution; }; - // Sets the target checkpoint of the context. - void SetTargetCheckpoint(CheckpointFlag flag) { m_targetCheckpoint = flag; }; protected: // Copies the args that are also needed in a sub-context. E.g., silent @@ -194,9 +196,6 @@ namespace AppInstaller::CLI::Execution // use this if AICLI_DISABLE_TEST_HOOKS is defined. std::function m_shouldExecuteWorkflowTask; - // Sets the id of the context. - void SetContextId(int contextId) { m_contextId = contextId; }; - private: DestructionToken m_disableSignalTerminationHandlerOnExit = false; bool m_isTerminated = false; @@ -206,9 +205,14 @@ namespace AppInstaller::CLI::Execution Workflow::ExecutionStage m_executionStage = Workflow::ExecutionStage::Initial; AppInstaller::ThreadLocalStorage::WingetThreadGlobals m_threadGlobals; AppInstaller::CLI::Command* m_executingCommand = nullptr; - CheckpointFlag m_currentCheckpoint = CheckpointFlag::None; - CheckpointFlag m_targetCheckpoint = CheckpointFlag::None; - static int s_contextCount; - int m_contextId = s_contextCount; + + // This store the entire command line string that is used to execute a command. + std::unique_ptr m_checkpointManager; + bool m_disableWorkflowExecution = false; + std::string_view m_checkpoint = {}; + std::string m_commandLineString = {}; + std::vector m_commandLineArgs = {}; + + void InitializeCheckpointManager(); }; } diff --git a/src/AppInstallerCLICore/Workflows/ResumeFlow.cpp b/src/AppInstallerCLICore/Workflows/ResumeFlow.cpp index 321cbec47d..e4e947bbdd 100644 --- a/src/AppInstallerCLICore/Workflows/ResumeFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/ResumeFlow.cpp @@ -18,7 +18,7 @@ namespace AppInstaller::CLI::Workflow { void EnsureSupportForResume(Execution::Context& context) { - std::string resumeGuidString { context.Args.GetArg(Execution::Args::Type::ResumeGuid) }; + std::string resumeGuidString { context.Args.GetArg(Execution::Args::Type::ResumeId) }; if (!Utility::IsValidGuidString(resumeGuidString)) { context.Reporter.Error() << Resource::String::InvalidResumeGuidError(Utility::LocIndView{ resumeGuidString }) << std::endl; @@ -33,7 +33,7 @@ namespace AppInstaller::CLI::Workflow AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_RESUME_GUID_NOT_FOUND); } - auto& checkpointManager = CheckpointManager::Instance(); + // Find a good place to check this. checkpointManager.Initialize(checkpointId); const auto& resumeStateClientVersion = checkpointManager.GetClientVersion(); @@ -49,4 +49,26 @@ namespace AppInstaller::CLI::Workflow AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_INVALID_RESUME_STATE); } } + + void Checkpoint::operator()(Execution::Context& context) const + { + if (!Settings::ExperimentalFeature::IsEnabled(Settings::ExperimentalFeature::Feature::Resume)) + { + return; + } + + if (WI_IsFlagClear(context.GetFlags(), Execution::ContextFlag::Resume)) + { + if (context.GetCheckpoint() == m_checkpointName) + { + // We have reached the checkpoint, begin enabling workflows. + context.DisableWorkflowExecution(false); + } + } + else + { + // If it is not a resume, simply capture the data in the index. + context.Checkpoint(m_checkpointName, m_contextData); + } + } } diff --git a/src/AppInstallerCLICore/Workflows/ResumeFlow.h b/src/AppInstallerCLICore/Workflows/ResumeFlow.h index 1935f40fc7..2edf839a7d 100644 --- a/src/AppInstallerCLICore/Workflows/ResumeFlow.h +++ b/src/AppInstallerCLICore/Workflows/ResumeFlow.h @@ -10,4 +10,19 @@ namespace AppInstaller::CLI::Workflow // Inputs: ResumeGuid // Outputs: None void EnsureSupportForResume(Execution::Context& context); + + // Applies a checkpoint to the context workflow. + struct Checkpoint : public WorkflowTask + { + Checkpoint(std::string_view checkpointName, std::vector contextData) : + WorkflowTask("ApplyCheckpoint"), + m_checkpointName(checkpointName), + m_contextData(contextData) {} + + void operator()(Execution::Context& context) const override; + + private: + std::string_view m_checkpointName; + std::vector m_contextData; + }; } diff --git a/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp b/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp index d0597aaaee..56cf862d32 100644 --- a/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp +++ b/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp @@ -1275,9 +1275,12 @@ AppInstaller::CLI::Execution::Context& operator<<(AppInstaller::CLI::Execution:: AppInstaller::CLI::Execution::Context& operator<<(AppInstaller::CLI::Execution::Context& context, const AppInstaller::CLI::Workflow::WorkflowTask& task) { - if (context.GetCurrentCheckpoint() < context.GetTargetCheckpoint()) + if (AppInstaller::Settings::ExperimentalFeature::IsEnabled(AppInstaller::Settings::ExperimentalFeature::Feature::Resume)) { - return context; + if (WI_IsFlagSet(context.GetFlags(), AppInstaller::CLI::Execution::ContextFlag::Resume) && !context.ShouldExecuteWorkflow()) + { + return context; + } } if (!context.IsTerminated()) diff --git a/src/AppInstallerCLITests/ResumeFlow.cpp b/src/AppInstallerCLITests/ResumeFlow.cpp index 1a099f1fd3..896f177687 100644 --- a/src/AppInstallerCLITests/ResumeFlow.cpp +++ b/src/AppInstallerCLITests/ResumeFlow.cpp @@ -33,7 +33,7 @@ TEST_CASE("ResumeFlow_InvalidGuid", "[Resume]") TestContext context{ resumeOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); - context.Args.AddArg(Execution::Args::Type::ResumeGuid, "badGuid"sv); + context.Args.AddArg(Execution::Args::Type::ResumeId, "badGuid"sv); ResumeCommand resume({}); context.SetExecutingCommand(&resume); @@ -52,7 +52,7 @@ TEST_CASE("ResumeFlow_IndexNotFound", "[Resume]") std::ostringstream resumeOutput; TestContext context{ resumeOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); - context.Args.AddArg(Execution::Args::Type::ResumeGuid, tempGuidString); + context.Args.AddArg(Execution::Args::Type::ResumeId, tempGuidString); ResumeCommand resume({}); context.SetExecutingCommand(&resume); @@ -87,7 +87,7 @@ TEST_CASE("ResumeFlow_InvalidClientVersion", "[Resume]") std::ostringstream resumeOutput; TestContext context{ resumeOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); - context.Args.AddArg(Execution::Args::Type::ResumeGuid, tempGuidString); + context.Args.AddArg(Execution::Args::Type::ResumeId, tempGuidString); ResumeCommand resume({}); context.SetExecutingCommand(&resume); @@ -124,7 +124,7 @@ TEST_CASE("ResumeFlow_EmptyIndex", "Resume") std::ostringstream resumeOutput; TestContext context{ resumeOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); - context.Args.AddArg(Execution::Args::Type::ResumeGuid, tempGuidString); + context.Args.AddArg(Execution::Args::Type::ResumeId, tempGuidString); ResumeCommand resume({}); context.SetExecutingCommand(&resume); diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointContextTable.cpp b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointContextTable.cpp index 098b6c3f1d..c54dfb6184 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointContextTable.cpp +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointContextTable.cpp @@ -10,8 +10,19 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 using namespace SQLite; using namespace std::string_view_literals; static constexpr std::string_view s_CheckpointContextTable_Table_Name = "CheckpointContext"sv; - static constexpr std::string_view s_CheckpointContextTable_ContextId_Column = "ContextId"sv; - static constexpr std::string_view s_CheckpointContextTable_LastCheckpoint_Column = "LastCheckpoint"sv; + static constexpr std::string_view s_CheckpointContextTable_CheckpointName_Column = "CheckpointName"sv; + static constexpr std::string_view s_CheckpointContextTable_ContextData_Column = "ContextData"sv; + static constexpr std::string_view s_CheckpointContextTable_Name_Column = "Name"sv; + static constexpr std::string_view s_CheckpointContextTable_Value_Column = "Value"sv; + + static constexpr std::string_view s_CheckpointMetadataTable_CheckpointName = "CheckpointName"sv; + static constexpr std::string_view s_CheckpointMetadataTable_ClientVersion = "ClientVersion"sv; + static constexpr std::string_view s_CheckpointMetadataTable_CommandName = "CommandName"sv; + static constexpr std::string_view s_CheckpointMetadataTable_CommandArguments = "CommandArguments"sv; + static constexpr std::string_view s_CheckpointMetadataTable_CommitTime = "CommandArguments"sv; + + // Everytime we commit to the table we write here: + std::string_view CheckpointContextTable::TableName() { @@ -26,14 +37,16 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 StatementBuilder createTableBuilder; createTableBuilder.CreateTable(s_CheckpointContextTable_Table_Name).BeginColumns(); - createTableBuilder.Column(ColumnBuilder(s_CheckpointContextTable_ContextId_Column, Type::Int).Unique().NotNull()); - createTableBuilder.Column(ColumnBuilder(s_CheckpointContextTable_LastCheckpoint_Column, Type::Int)); + createTableBuilder.Column(ColumnBuilder(s_CheckpointContextTable_CheckpointName_Column, Type::Text).NotNull()); + createTableBuilder.Column(ColumnBuilder(s_CheckpointContextTable_ContextData_Column, Type::Int)); + createTableBuilder.Column(ColumnBuilder(s_CheckpointContextTable_Name_Column, Type::Text)); + createTableBuilder.Column(ColumnBuilder(s_CheckpointContextTable_Value_Column, Type::Text)); createTableBuilder.EndColumns(); createTableBuilder.Execute(connection); savepoint.Commit(); } - std::optional CheckpointContextTable::SelectByContextId(const SQLite::Connection& connection, int contextId) + std::optional CheckpointContextTable::SelectByCheckpointName(const SQLite::Connection& connection, std::string checkpointName) { SQLite::Builder::StatementBuilder builder; builder.Select(SQLite::RowIDName).From(s_CheckpointContextTable_Table_Name).Where(s_CheckpointContextTable_ContextId_Column); diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointMetadataTable.cpp b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointMetadataTable.cpp index 8b25ee2194..3186c1e5c6 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointMetadataTable.cpp +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointMetadataTable.cpp @@ -12,17 +12,27 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 static constexpr std::string_view s_CheckpointMetadataTable_Name_Column = "Name"sv; static constexpr std::string_view s_CheckpointMetadataTable_Value_Column = "Value"sv; + static constexpr std::string_view s_CheckpointMetadataTable_CheckpointName = "CheckpointName"sv; static constexpr std::string_view s_CheckpointMetadataTable_ClientVersion = "ClientVersion"sv; + static constexpr std::string_view s_CheckpointMetadataTable_CommandName = "CommandName"sv; + static constexpr std::string_view s_CheckpointMetadataTable_CommandArguments = "CommandArguments"sv; + static constexpr std::string_view s_CheckpointMetadataTable_CommitTime = "CommandArguments"sv; + + static constexpr std::string_view s_CheckpointContextTable_CheckpointName_Column = "CheckpointName"sv; + static constexpr std::string_view s_CheckpointContextTable_ContextData_Column = "ContextData"sv; + static constexpr std::string_view s_CheckpointContextTable_Name_Column = "Name"sv; + static constexpr std::string_view s_CheckpointContextTable_Value_Column = "Value"sv; namespace { - SQLite::rowid_t SetNamedValue(SQLite::Connection& connection, std::string_view name, std::string_view value) + SQLite::rowid_t SetNamedValue(SQLite::Connection& connection, std::string_view checkpoint, std::string_view name, std::string_view value) { SQLite::Builder::StatementBuilder builder; builder.InsertInto(s_CheckpointMetadataTable_Table_Name) - .Columns({ s_CheckpointMetadataTable_Name_Column, + .Columns({ s_CheckpointContextTable_CheckpointName_Column, + s_CheckpointMetadataTable_Name_Column, s_CheckpointMetadataTable_Value_Column }) - .Values(name, value); + .Values(checkpoint, name, value); builder.Execute(connection); return connection.GetLastInsertRowID(); From e15a142052714167ed7761621136d307f25d8d78 Mon Sep 17 00:00:00 2001 From: Ryan Fu Date: Thu, 10 Aug 2023 13:55:02 -0700 Subject: [PATCH 13/43] savework --- src/AppInstallerCLICore/Commands/InstallCommand.cpp | 11 ++++++++++- src/AppInstallerCLICore/Core.cpp | 2 +- src/AppInstallerCLICore/Workflows/ResumeFlow.cpp | 2 +- .../Schema/Checkpoint_1_0/CheckpointMetadataTable.cpp | 2 +- 4 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/AppInstallerCLICore/Commands/InstallCommand.cpp b/src/AppInstallerCLICore/Commands/InstallCommand.cpp index 654b219d4e..53f1c62715 100644 --- a/src/AppInstallerCLICore/Commands/InstallCommand.cpp +++ b/src/AppInstallerCLICore/Commands/InstallCommand.cpp @@ -106,7 +106,15 @@ namespace AppInstaller::CLI void InstallCommand::Resume(Context& context) const { + // Load arguments from index. + auto commandArguments = context.GetCommandArguments(); + Invocation invocation{ std::move(commandArguments) }; + ParseArguments(invocation, context.Args); + + // Load checkpoints from index. context.LoadCheckpoints(); + + // Set flags and disable workflow execution. context.SetFlags(Execution::ContextFlag::Resume); context.DisableWorkflowExecution(true); ExecuteInternal(context); @@ -130,7 +138,8 @@ namespace AppInstaller::CLI { context << Workflow::ReportExecutionStage(ExecutionStage::Discovery) << - Workflow::OpenSource(); + Workflow::OpenSource() << + Workflow::Checkpoint("SourceAdded"sv, { Execution::Data::Source }); if (!context.Args.Contains(Execution::Args::Type::Force)) { diff --git a/src/AppInstallerCLICore/Core.cpp b/src/AppInstallerCLICore/Core.cpp index 92ca1615c9..aca5556879 100644 --- a/src/AppInstallerCLICore/Core.cpp +++ b/src/AppInstallerCLICore/Core.cpp @@ -105,7 +105,7 @@ namespace AppInstaller::CLI }()); - context.SetCommandLineArgs(utf8Args); + context.SetCommandArguments(utf8Args); Invocation invocation{ std::move(utf8Args) }; // The root command is our fallback in the event of very bad or very little input diff --git a/src/AppInstallerCLICore/Workflows/ResumeFlow.cpp b/src/AppInstallerCLICore/Workflows/ResumeFlow.cpp index e4e947bbdd..4fd7b0e596 100644 --- a/src/AppInstallerCLICore/Workflows/ResumeFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/ResumeFlow.cpp @@ -57,7 +57,7 @@ namespace AppInstaller::CLI::Workflow return; } - if (WI_IsFlagClear(context.GetFlags(), Execution::ContextFlag::Resume)) + if (WI_IsFlagSet(context.GetFlags(), Execution::ContextFlag::Resume)) { if (context.GetCheckpoint() == m_checkpointName) { diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointMetadataTable.cpp b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointMetadataTable.cpp index 3186c1e5c6..498db9b556 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointMetadataTable.cpp +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointMetadataTable.cpp @@ -16,7 +16,7 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 static constexpr std::string_view s_CheckpointMetadataTable_ClientVersion = "ClientVersion"sv; static constexpr std::string_view s_CheckpointMetadataTable_CommandName = "CommandName"sv; static constexpr std::string_view s_CheckpointMetadataTable_CommandArguments = "CommandArguments"sv; - static constexpr std::string_view s_CheckpointMetadataTable_CommitTime = "CommandArguments"sv; + static constexpr std::string_view s_CheckpointMetadataTable_CommitTime = "CommandArguments"sv;c static constexpr std::string_view s_CheckpointContextTable_CheckpointName_Column = "CheckpointName"sv; static constexpr std::string_view s_CheckpointContextTable_ContextData_Column = "ContextData"sv; From 22840a51df0c5d93167d01480b34da6b3ee415ba Mon Sep 17 00:00:00 2001 From: Ryan Fu Date: Fri, 11 Aug 2023 15:22:53 -0700 Subject: [PATCH 14/43] save work --- src/AppInstallerCLICore/CheckpointManager.cpp | 41 +-- src/AppInstallerCLICore/CheckpointManager.h | 18 +- src/AppInstallerCLICore/Command.cpp | 6 +- .../Commands/InstallCommand.cpp | 3 +- src/AppInstallerCLICore/ExecutionContext.cpp | 55 ++-- src/AppInstallerCLICore/ExecutionContext.h | 5 +- .../Workflows/ResumeFlow.cpp | 49 ++-- .../AppInstallerRepositoryCore.vcxproj | 2 - ...AppInstallerRepositoryCore.vcxproj.filters | 6 - .../Microsoft/CheckpointIndex.cpp | 103 ++----- .../Microsoft/CheckpointIndex.h | 25 +- .../CheckpointArgumentsTable.cpp | 253 ------------------ .../Checkpoint_1_0/CheckpointArgumentsTable.h | 61 ----- .../Checkpoint_1_0/CheckpointContextTable.cpp | 80 +++--- .../Checkpoint_1_0/CheckpointContextTable.h | 20 +- .../Checkpoint_1_0/CheckpointIndexInterface.h | 21 +- .../CheckpointIndexInterface_1_0.cpp | 158 ++--------- .../CheckpointMetadataTable.cpp | 47 ++-- .../Checkpoint_1_0/CheckpointMetadataTable.h | 12 + .../Microsoft/Schema/ICheckpointIndex.h | 48 +--- 20 files changed, 237 insertions(+), 776 deletions(-) delete mode 100644 src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointArgumentsTable.cpp delete mode 100644 src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointArgumentsTable.h diff --git a/src/AppInstallerCLICore/CheckpointManager.cpp b/src/AppInstallerCLICore/CheckpointManager.cpp index f7c511c1c9..36fb7757fc 100644 --- a/src/AppInstallerCLICore/CheckpointManager.cpp +++ b/src/AppInstallerCLICore/CheckpointManager.cpp @@ -1,7 +1,6 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" -#include "AppInstallerRuntime.h" #include "CheckpointManager.h" #include "Microsoft/CheckpointIndex.h" #include "Microsoft/SQLiteStorageBase.h" @@ -12,13 +11,24 @@ namespace AppInstaller::CLI::Checkpoint { CheckpointManager::CheckpointManager(GUID id) { - if (m_checkpointId == GUID_NULL) + m_checkpointId = id; + AICLI_LOG(CLI, Info, << "Opening checkpoint index with id: " << m_checkpointId); + auto openDisposition = AppInstaller::Repository::Microsoft::SQLiteStorageBase::OpenDisposition::ReadWrite; + auto checkpointIndex = AppInstaller::Repository::Microsoft::CheckpointIndex::OpenOrCreateDefault(m_checkpointId, openDisposition); + if (!checkpointIndex) { - std::ignore = CoCreateGuid(&m_checkpointId); + AICLI_LOG(CLI, Error, << "Unable to open checkpoint index."); + // TODO: Handle failure to open index gracefully. } - AICLI_LOG(CLI, Info, << "Checkpoint manager id: " << m_checkpointId); + m_checkpointIndex = std::move(checkpointIndex); + } + + CheckpointManager::CheckpointManager(std::string_view commandName, std::string_view commandArguments, std::string_view clientVersion) + { + std::ignore = CoCreateGuid(&m_checkpointId); + AICLI_LOG(CLI, Info, << "Creating checkpoint index with id: " << m_checkpointId); auto openDisposition = AppInstaller::Repository::Microsoft::SQLiteStorageBase::OpenDisposition::ReadWrite; auto checkpointIndex = AppInstaller::Repository::Microsoft::CheckpointIndex::OpenOrCreateDefault(m_checkpointId, openDisposition); if (!checkpointIndex) @@ -28,33 +38,25 @@ namespace AppInstaller::CLI::Checkpoint } m_checkpointIndex = std::move(checkpointIndex); - m_checkpointIndex->SetClientVersion(AppInstaller::Runtime::GetClientVersion()); + m_checkpointIndex->SetCommandName(commandName); + m_checkpointIndex->SetCommandArguments(commandArguments); + m_checkpointIndex->SetClientVersion(clientVersion); } template<> void CheckpointManager::RecordContextData(std::string_view checkpointName, Manifest::ManifestInstaller installer) { - // Capture all relevant data from the installer. - m_checkpointIndex->SetCommandName(0, installer.PackageFamilyName); + m_checkpointIndex->AddContextData(checkpointName, 1, "installerUrl"sv, installer.Url); }; - void CheckpointManager::RecordMetadata( - std::string_view checkpointName, - std::string_view commandName, - std::string_view commandLineString, - std::string clientVersion) - { - // Capture these arguments from the checkpoint metadata. - } - std::string CheckpointManager::GetClientVersion() { return m_checkpointIndex->GetClientVersion(); } - std::string CheckpointManager::GetCommandName(int contextId) + std::string CheckpointManager::GetCommandName() { - return m_checkpointIndex->GetCommandName(contextId); + return m_checkpointIndex->GetCommandName(); } void CheckpointManager::CleanUpIndex() @@ -80,7 +82,6 @@ namespace AppInstaller::CLI::Checkpoint std::string CheckpointManager::GetArguments() { - // Return the actual command line arguments. - m_checkpointIndex->GetCommandName(0); + return m_checkpointIndex->GetCommandArguments(); } } diff --git a/src/AppInstallerCLICore/CheckpointManager.h b/src/AppInstallerCLICore/CheckpointManager.h index a3027e5dd7..65604cf986 100644 --- a/src/AppInstallerCLICore/CheckpointManager.h +++ b/src/AppInstallerCLICore/CheckpointManager.h @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once -#include "ExecutionContext.h" - #include "Microsoft/CheckpointIndex.h" #include @@ -15,19 +13,25 @@ namespace AppInstaller::CLI::Checkpoint { struct CheckpointManager { - CheckpointManager(GUID id = {}); + // Constructor for an existing resume id. + CheckpointManager(GUID id); + // Constructor for a new resume save state. + CheckpointManager(std::string_view commandName, std::string_view commandArguments, std::string_view clientVersion); + ~CheckpointManager(); std::string GetClientVersion(); - std::string GetCommandName(int contextId); + std::string GetCommandName(); std::string GetArguments(); - void RecordMetadata(std::string_view checkpointName, std::string_view commandName, std::string_view commandLineString, std::string clientVersion); - template - void RecordContextData(std::string_view checkpointName, T data) {}; + void RecordContextData(std::string_view checkpointName, T data) + { + UNREFERENCED_PARAMETER(checkpointName); + UNREFERENCED_PARAMETER(data); + } private: GUID m_checkpointId = {}; diff --git a/src/AppInstallerCLICore/Command.cpp b/src/AppInstallerCLICore/Command.cpp index 9260945314..eed93f956c 100644 --- a/src/AppInstallerCLICore/Command.cpp +++ b/src/AppInstallerCLICore/Command.cpp @@ -858,8 +858,7 @@ namespace AppInstaller::CLI // Only the resume command requires a resume id. If not present, create checkpoint index and capture initial arguments. if (!context.Args.Contains(Execution::Args::Type::ResumeId)) { - AICLI_LOG(CLI, Info, << "Initializing checkpoint manager."); - context.Checkpoint("CheckpointInitialized", {}); + context.InitializeCheckpointManager(Name(), "install Microsoft.PowerToys"sv, AppInstaller::Runtime::GetClientVersion()); } } @@ -888,7 +887,8 @@ namespace AppInstaller::CLI void Command::Resume(Execution::Context& context) const { - // Command must implement this function, which should load everything from the context. + context.Reporter.Error() << Resource::String::PendingWorkError << std::endl; + THROW_HR(E_NOTIMPL); } void Command::SelectCurrentCommandIfUnrecognizedSubcommandFound(bool value) diff --git a/src/AppInstallerCLICore/Commands/InstallCommand.cpp b/src/AppInstallerCLICore/Commands/InstallCommand.cpp index 53f1c62715..bb9b56fe2e 100644 --- a/src/AppInstallerCLICore/Commands/InstallCommand.cpp +++ b/src/AppInstallerCLICore/Commands/InstallCommand.cpp @@ -138,8 +138,7 @@ namespace AppInstaller::CLI { context << Workflow::ReportExecutionStage(ExecutionStage::Discovery) << - Workflow::OpenSource() << - Workflow::Checkpoint("SourceAdded"sv, { Execution::Data::Source }); + Workflow::OpenSource(); if (!context.Args.Contains(Execution::Args::Type::Force)) { diff --git a/src/AppInstallerCLICore/ExecutionContext.cpp b/src/AppInstallerCLICore/ExecutionContext.cpp index c489d89189..fadb214a54 100644 --- a/src/AppInstallerCLICore/ExecutionContext.cpp +++ b/src/AppInstallerCLICore/ExecutionContext.cpp @@ -5,8 +5,6 @@ #include "COMContext.h" #include "Argument.h" #include "winget/UserSettings.h" -#include "winget/Runtime.h" -#include "CheckpointManager.h" #include "Command.h" namespace AppInstaller::CLI::Execution @@ -419,8 +417,6 @@ namespace AppInstaller::CLI::Execution void Context::LoadCheckpoints() { - InitializeCheckpointManager(); - // Call a function here that retrieves the checkpoint in reverse order so that the entire state is completely populated. // Retreives all metadata from the stored info in the checkpoint index and applies the state change to the context. } @@ -428,21 +424,7 @@ namespace AppInstaller::CLI::Execution // Initialized the checkpoint manager if it does not exist, then captures the automatic metadata as well as the context data. void Context::Checkpoint(std::string_view checkpointName, std::vector contextData) { - InitializeCheckpointManager(); - - // Maybe store these in the checkpoint manager so that we don't have to compute them every time. - const auto& clientVersion = AppInstaller::Runtime::GetClientVersion().get(); - const auto& command = GetExecutingCommand(); - - std::string_view commandName; - if (command != nullptr) - { - commandName = command->Name(); - } - - m_checkpointManager->RecordMetadata(checkpointName, commandName, m_commandLineString, clientVersion); - - for (Execution::Data data : contextData) + for (auto data : contextData) { switch (data) { @@ -456,13 +438,14 @@ namespace AppInstaller::CLI::Execution void Context::SetCommandArguments(std::vector args) { - std::stringstream strstr; - for (const auto& arg : args) - { - strstr << arg << ' '; - } - - const std::string& commandLine = strstr.str(); + m_commandLineArgs = args; + //std::stringstream strstr; + //for (const auto& arg : args) + //{ + // strstr << arg << ' '; + //} + + //const std::string& commandLine = strstr.str(); } std::vector Context::GetCommandArguments() @@ -470,19 +453,13 @@ namespace AppInstaller::CLI::Execution return m_commandLineArgs; } - void Context::InitializeCheckpointManager() + void Context::InitializeCheckpointManager(GUID id) { - if (!m_checkpointManager) - { - if (Args.Contains(Execution::Args::Type::ResumeId)) - { - GUID resumeGuid = Utility::ConvertToGuid(std::string{ Args.GetArg(Execution::Args::Type::ResumeId) }); - m_checkpointManager = std::make_unique(resumeGuid); - } - else - { - m_checkpointManager = std::make_unique(); - } - } + m_checkpointManager = std::make_unique(id); + } + + void Context::InitializeCheckpointManager(std::string_view commandName, std::string_view commandArguments, std::string_view clientVersion) + { + m_checkpointManager = std::make_unique(commandName, commandArguments, clientVersion); } } diff --git a/src/AppInstallerCLICore/ExecutionContext.h b/src/AppInstallerCLICore/ExecutionContext.h index 409d6ce583..5987e41d73 100644 --- a/src/AppInstallerCLICore/ExecutionContext.h +++ b/src/AppInstallerCLICore/ExecutionContext.h @@ -186,6 +186,9 @@ namespace AppInstaller::CLI::Execution bool ShouldExecuteWorkflow() { return !m_disableWorkflowExecution; }; + void InitializeCheckpointManager(GUID id); + + void InitializeCheckpointManager(std::string_view commandName, std::string_view commandArguments, std::string_view clientVersion); protected: // Copies the args that are also needed in a sub-context. E.g., silent @@ -212,7 +215,5 @@ namespace AppInstaller::CLI::Execution std::string_view m_checkpoint = {}; std::string m_commandLineString = {}; std::vector m_commandLineArgs = {}; - - void InitializeCheckpointManager(); }; } diff --git a/src/AppInstallerCLICore/Workflows/ResumeFlow.cpp b/src/AppInstallerCLICore/Workflows/ResumeFlow.cpp index 4fd7b0e596..981b62b5b0 100644 --- a/src/AppInstallerCLICore/Workflows/ResumeFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/ResumeFlow.cpp @@ -18,36 +18,33 @@ namespace AppInstaller::CLI::Workflow { void EnsureSupportForResume(Execution::Context& context) { - std::string resumeGuidString { context.Args.GetArg(Execution::Args::Type::ResumeId) }; - if (!Utility::IsValidGuidString(resumeGuidString)) - { - context.Reporter.Error() << Resource::String::InvalidResumeGuidError(Utility::LocIndView{ resumeGuidString }) << std::endl; - AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_INVALID_RESUME_GUID); - } + UNREFERENCED_PARAMETER(context); + //std::string resumeGuidString { context.Args.GetArg(Execution::Args::Type::ResumeId) }; + //if (!Utility::IsValidGuidString(resumeGuidString)) + //{ + // context.Reporter.Error() << Resource::String::InvalidResumeGuidError(Utility::LocIndView{ resumeGuidString }) << std::endl; + // AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_INVALID_RESUME_GUID); + //} - GUID checkpointId = Utility::ConvertToGuid(resumeGuidString); + //GUID checkpointId = Utility::ConvertToGuid(resumeGuidString); - if (!std::filesystem::exists(AppInstaller::Repository::Microsoft::CheckpointIndex::GetCheckpointIndexPath(checkpointId))) - { - context.Reporter.Error() << Resource::String::ResumeGuidNotFoundError(Utility::LocIndView{ resumeGuidString }) << std::endl; - AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_RESUME_GUID_NOT_FOUND); - } + //if (!std::filesystem::exists(AppInstaller::Repository::Microsoft::CheckpointIndex::GetCheckpointIndexPath(checkpointId))) + //{ + // context.Reporter.Error() << Resource::String::ResumeGuidNotFoundError(Utility::LocIndView{ resumeGuidString }) << std::endl; + // AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_RESUME_GUID_NOT_FOUND); + //} - // Find a good place to check this. - checkpointManager.Initialize(checkpointId); + //if (AppInstaller::Runtime::GetClientVersion().get() != resumeStateClientVersion) + //{ + // context.Reporter.Error() << Resource::String::ClientVersionMismatchError(Utility::LocIndView{ resumeStateClientVersion }) << std::endl; + // AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_CLIENT_VERSION_MISMATCH); + //} - const auto& resumeStateClientVersion = checkpointManager.GetClientVersion(); - if (AppInstaller::Runtime::GetClientVersion().get() != resumeStateClientVersion) - { - context.Reporter.Error() << Resource::String::ClientVersionMismatchError(Utility::LocIndView{ resumeStateClientVersion }) << std::endl; - AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_CLIENT_VERSION_MISMATCH); - } - - if (!checkpointManager.HasContext()) - { - context.Reporter.Error() << Resource::String::ResumeStateDataNotFoundError << std::endl; - AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_INVALID_RESUME_STATE); - } + //if (!checkpointManager.HasContext()) + //{ + // context.Reporter.Error() << Resource::String::ResumeStateDataNotFoundError << std::endl; + // AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_INVALID_RESUME_STATE); + //} } void Checkpoint::operator()(Execution::Context& context) const diff --git a/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj b/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj index a3d39faa4d..46699aa8de 100644 --- a/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj +++ b/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj @@ -395,7 +395,6 @@ - @@ -500,7 +499,6 @@ - diff --git a/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj.filters b/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj.filters index 77dfd83908..9e8daa72b5 100644 --- a/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj.filters +++ b/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj.filters @@ -396,9 +396,6 @@ Microsoft - - Microsoft\Schema\Checkpoint_1_0 - Microsoft\Schema\Checkpoint_1_0 @@ -638,9 +635,6 @@ Source Files - - Microsoft\Schema\Checkpoint_1_0 - Microsoft\Schema\Checkpoint_1_0 diff --git a/src/AppInstallerRepositoryCore/Microsoft/CheckpointIndex.cpp b/src/AppInstallerRepositoryCore/Microsoft/CheckpointIndex.cpp index 33a3f06ffe..5cbd731d8c 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/CheckpointIndex.cpp +++ b/src/AppInstallerRepositoryCore/Microsoft/CheckpointIndex.cpp @@ -120,124 +120,63 @@ namespace AppInstaller::Repository::Microsoft return m_interface->GetClientVersion(m_dbconn); } - void CheckpointIndex::AddContext(int contextId) - { - std::lock_guard lockInterface{ *m_interfaceLock }; - AICLI_LOG(Repo, Verbose, << "Adding context [" << contextId << "] to checkpoint index"); - - SQLite::Savepoint savepoint = SQLite::Savepoint::Create(m_dbconn, "checkpointindex_addcontext"); - - m_interface->AddContextToArgumentTable(m_dbconn, contextId); - m_interface->AddContextToContextTable(m_dbconn, contextId); - - SetLastWriteTime(); - savepoint.Commit(); - } - - void CheckpointIndex::RemoveContext(int contextId) - { - std::lock_guard lockInterface{ *m_interfaceLock }; - AICLI_LOG(Repo, Verbose, << "Removing context [" << contextId << "] from checkpoint index"); - - SQLite::Savepoint savepoint = SQLite::Savepoint::Create(m_dbconn, "checkpointindex_removecontext"); - - m_interface->RemoveContextFromArgumentTable(m_dbconn, contextId); - m_interface->RemoveContextFromContextTable(m_dbconn, contextId); - - SetLastWriteTime(); - savepoint.Commit(); - } - - CheckpointIndex::IdType CheckpointIndex::SetCommandName(int contextId, std::string_view commandName) + CheckpointIndex::IdType CheckpointIndex::SetCommandName(std::string_view commandName) { std::lock_guard lockInterface{ *m_interfaceLock }; AICLI_LOG(Repo, Verbose, << "Setting command name [" << commandName << "]"); SQLite::Savepoint savepoint = SQLite::Savepoint::Create(m_dbconn, "checkpointindex_setcommandname"); - IdType result = m_interface->SetCommandName(m_dbconn, contextId, commandName); + IdType result = m_interface->SetCommandName(m_dbconn, commandName); SetLastWriteTime(); - savepoint.Commit(); - return result; } - std::string CheckpointIndex::GetCommandName(int contextId) - { - return m_interface->GetCommandName(m_dbconn, contextId); - } - - bool CheckpointIndex::UpdateArgument(int contextId, std::string_view name, std::string_view value) + std::string CheckpointIndex::GetCommandName() { - std::lock_guard lockInterface{ *m_interfaceLock }; - AICLI_LOG(Repo, Verbose, << "Updating argument column for [" << name << "] with value [" << value << "]"); - - SQLite::Savepoint savepoint = SQLite::Savepoint::Create(m_dbconn, "checkpointindex_updatestringargument"); - - bool result = m_interface->UpdateArgumentByContextId(m_dbconn, contextId, name, value); - - SetLastWriteTime(); - - savepoint.Commit(); - - return result; + return m_interface->GetCommandName(m_dbconn); } - bool CheckpointIndex::UpdateArgument(int contextId, std::string_view name, bool value) + CheckpointIndex::IdType CheckpointIndex::SetCommandArguments(std::string_view commandArguments) { std::lock_guard lockInterface{ *m_interfaceLock }; - AICLI_LOG(Repo, Verbose, << "Updating argument column for [" << name << "] with value [" << value << "]"); + AICLI_LOG(Repo, Verbose, << "Setting command arguments [" << commandArguments << "]"); - SQLite::Savepoint savepoint = SQLite::Savepoint::Create(m_dbconn, "checkpointindex_updateboolargument"); + SQLite::Savepoint savepoint = SQLite::Savepoint::Create(m_dbconn, "checkpointindex_setcommandarguments"); - bool result = m_interface->UpdateArgumentByContextId(m_dbconn, contextId, name, value); + IdType result = m_interface->SetClientVersion(m_dbconn, commandArguments); SetLastWriteTime(); - savepoint.Commit(); - return result; } - bool CheckpointIndex::ContainsArgument(int contextId, std::string_view name) - { - return m_interface->ContainsArgument(m_dbconn, contextId, name); - } - - std::string CheckpointIndex::GetStringArgument(int contextId, std::string_view name) + std::string CheckpointIndex::GetCommandArguments() { - return m_interface->GetStringArgumentByContextId(m_dbconn, contextId, name); + return m_interface->GetCommandArguments(m_dbconn); } - bool CheckpointIndex::GetBoolArgument(int contextId, std::string_view name) + CheckpointIndex::IdType CheckpointIndex::AddContextData(std::string_view checkpointName, int contextData, std::string_view name, std::string_view value) { - return m_interface->GetBoolArgumentByContextId(m_dbconn, contextId, name); - } + std::lock_guard lockInterface{ *m_interfaceLock }; + AICLI_LOG(Repo, Verbose, << "Setting context data [" << contextData << "] for [" << name << "] with value [" << value << "] value"); - int CheckpointIndex::GetFirstContextId() - { - return m_interface->GetFirstContextId(m_dbconn); + SQLite::Savepoint savepoint = SQLite::Savepoint::Create(m_dbconn, "checkpointindex_addcontextdata"); + SQLite::rowid_t rowId = m_interface->AddContextData(m_dbconn, checkpointName, contextData, name, value); + savepoint.Commit(); + return rowId; } - bool CheckpointIndex::SetLastCheckpoint(int contextId, int checkpointFlag) + void CheckpointIndex::RemoveContextData(std::string_view checkpointName, int contextData) { std::lock_guard lockInterface{ *m_interfaceLock }; - AICLI_LOG(Repo, Verbose, << "Updating last checkpoint flag for [" << contextId << "] with value [" << checkpointFlag << "]"); - - SQLite::Savepoint savepoint = SQLite::Savepoint::Create(m_dbconn, "checkpointindex_updatelastcheckpointflag"); + AICLI_LOG(Repo, Verbose, << "Removing context data [" << contextData << "]"); - bool result = m_interface->SetLastCheckpointByContextId(m_dbconn, contextId, checkpointFlag); - - SetLastWriteTime(); + SQLite::Savepoint savepoint = SQLite::Savepoint::Create(m_dbconn, "checkpointindex_addcontextdata"); + m_interface->RemoveContextData(m_dbconn, checkpointName, contextData); savepoint.Commit(); - return result; - } - - int CheckpointIndex::GetLastCheckpoint(int contextId) - { - return m_interface->GetLastCheckpointByContextId(m_dbconn, contextId); } std::unique_ptr CheckpointIndex::CreateICheckpointIndex() const diff --git a/src/AppInstallerRepositoryCore/Microsoft/CheckpointIndex.h b/src/AppInstallerRepositoryCore/Microsoft/CheckpointIndex.h index 5ef3e33779..129522502a 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/CheckpointIndex.h +++ b/src/AppInstallerRepositoryCore/Microsoft/CheckpointIndex.h @@ -3,7 +3,6 @@ #pragma once #include "SQLiteWrapper.h" #include "Microsoft/Schema/ICheckpointIndex.h" -#include "Microsoft/Schema/Checkpoint_1_0/CheckpointArgumentsTable.h" #include "Microsoft/SQLiteStorageBase.h" #include @@ -41,29 +40,17 @@ namespace AppInstaller::Repository::Microsoft std::string GetClientVersion(); - void AddContext(int contextId); + IdType SetCommandName(std::string_view commandName); - void RemoveContext(int contextId); + std::string GetCommandName(); - IdType SetCommandName(int contextId, std::string_view commandName); + IdType SetCommandArguments(std::string_view commandArguments); - std::string GetCommandName(int contextId); + std::string GetCommandArguments(); - bool ContainsArgument(int contextId, std::string_view name); + IdType AddContextData(std::string_view checkpointName, int contextData, std::string_view name, std::string_view value); - bool UpdateArgument(int contextId, std::string_view name, std::string_view value); - - bool UpdateArgument(int contextId, std::string_view name, bool value); - - std::string GetStringArgument(int contextId, std::string_view name); - - bool GetBoolArgument(int contextId, std::string_view name); - - int GetFirstContextId(); - - bool SetLastCheckpoint(int contextId, int checkpointFlag); - - int GetLastCheckpoint(int contextId); + void RemoveContextData(std::string_view checkpointName, int contextData); private: // Constructor used to open an existing index. diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointArgumentsTable.cpp b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointArgumentsTable.cpp deleted file mode 100644 index 4a26575963..0000000000 --- a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointArgumentsTable.cpp +++ /dev/null @@ -1,253 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. -#include "pch.h" -#include "CheckpointArgumentsTable.h" -#include "SQLiteStatementBuilder.h" -#include "Microsoft/Schema/ICheckpointIndex.h" - -namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 -{ - using namespace SQLite; - using namespace std::string_view_literals; - - static constexpr std::string_view s_CheckpointArgumentsTable_Table_Name = "CheckpointArguments"sv; - static constexpr std::string_view s_CheckpointArgumentsTable_ContextId_Column = "contextId"sv; - static constexpr std::string_view s_CheckpointArgumentsTable_CommandName_Column = "commandName"sv; - static constexpr std::string_view s_CheckpointArgumentsTable_Query_Column = "query"sv; - static constexpr std::string_view s_CheckpointArgumentsTable_MultiQuery_Column = "multiQuery"sv; - static constexpr std::string_view s_CheckpointArgumentsTable_Manifest_Column = "manifest"sv; - static constexpr std::string_view s_CheckpointArgumentsTable_Id_Column = "id"sv; - static constexpr std::string_view s_CheckpointArgumentsTable_Name_Column = "name"sv; - static constexpr std::string_view s_CheckpointArgumentsTable_Moniker_Column = "moniker"sv; - static constexpr std::string_view s_CheckpointArgumentsTable_Version_Column = "version"sv; - static constexpr std::string_view s_CheckpointArgumentsTable_Channel_Column = "channel"sv; - static constexpr std::string_view s_CheckpointArgumentsTable_Source_Column = "source"sv; - static constexpr std::string_view s_CheckpointArgumentsTable_InstallScope_Column = "scope"sv; - static constexpr std::string_view s_CheckpointArgumentsTable_InstallArchitecture_Column = "architecture"sv; - static constexpr std::string_view s_CheckpointArgumentsTable_Exact_Column = "exact"sv; - static constexpr std::string_view s_CheckpointArgumentsTable_Interactive_Column = "interactive"sv; - static constexpr std::string_view s_CheckpointArgumentsTable_Silent_Column = "silent"sv; - static constexpr std::string_view s_CheckpointArgumentsTable_Locale_Column = "locale"sv; - static constexpr std::string_view s_CheckpointArgumentsTable_Log_Column = "log"sv; - static constexpr std::string_view s_CheckpointArgumentsTable_Custom_Column = "custom"sv; - static constexpr std::string_view s_CheckpointArgumentsTable_Override_Column = "override"sv; - static constexpr std::string_view s_CheckpointArgumentsTable_InstallLocation_Column = "location"sv; - static constexpr std::string_view s_CheckpointArgumentsTable_HashOverride_Column = "ignore-security-hash"sv; - static constexpr std::string_view s_CheckpointArgumentsTable_SkipDependencies_Column = "skip-dependencies"sv; - static constexpr std::string_view s_CheckpointArgumentsTable_IgnoreLocalArchiveMalwareScan_Column = "ignore-local-archive-malware-scan"sv; - static constexpr std::string_view s_CheckpointArgumentsTable_DependencySource_Column = "dependency-source"sv; - static constexpr std::string_view s_CheckpointArgumentsTable_AcceptPackageAgreements_Column = "accept-package-agreements"sv; - static constexpr std::string_view s_CheckpointArgumentsTable_NoUpgrade_Column = "no-upgrade"sv; - static constexpr std::string_view s_CheckpointArgumentsTable_CustomHeader_Column = "header"sv; - static constexpr std::string_view s_CheckpointArgumentsTable_AcceptSourceAgreements_Column = "accept-source-agreements"sv; - static constexpr std::string_view s_CheckpointArgumentsTable_Rename_Column = "rename"sv; - static constexpr std::string_view s_CheckpointArgumentsTable_UninstallPrevious_Column = "uninstall-previous"sv; - static constexpr std::string_view s_CheckpointArgumentsTable_Force_Column = "force"sv; - - std::string_view CheckpointArgumentsTable::TableName() - { - return s_CheckpointArgumentsTable_Table_Name; - } - - void CheckpointArgumentsTable::Create(SQLite::Connection& connection) - { - using namespace SQLite::Builder; - - SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "createCheckpointArgumentsTable_v1_0"); - - StatementBuilder createTableBuilder; - createTableBuilder.CreateTable(s_CheckpointArgumentsTable_Table_Name).BeginColumns(); - createTableBuilder.Column(ColumnBuilder(s_CheckpointArgumentsTable_ContextId_Column, Type::Int).Unique().NotNull()); - createTableBuilder.Column(ColumnBuilder(s_CheckpointArgumentsTable_CommandName_Column, Type::Text)); - createTableBuilder.Column(ColumnBuilder(s_CheckpointArgumentsTable_Query_Column, Type::Text)); - createTableBuilder.Column(ColumnBuilder(s_CheckpointArgumentsTable_MultiQuery_Column, Type::Text)); - createTableBuilder.Column(ColumnBuilder(s_CheckpointArgumentsTable_Manifest_Column, Type::Text)); - createTableBuilder.Column(ColumnBuilder(s_CheckpointArgumentsTable_Id_Column, Type::Text)); - createTableBuilder.Column(ColumnBuilder(s_CheckpointArgumentsTable_Name_Column, Type::Text)); - createTableBuilder.Column(ColumnBuilder(s_CheckpointArgumentsTable_Moniker_Column, Type::Text)); - createTableBuilder.Column(ColumnBuilder(s_CheckpointArgumentsTable_Version_Column, Type::Text)); - createTableBuilder.Column(ColumnBuilder(s_CheckpointArgumentsTable_Channel_Column, Type::Text)); - createTableBuilder.Column(ColumnBuilder(s_CheckpointArgumentsTable_Source_Column, Type::Text)); - createTableBuilder.Column(ColumnBuilder(s_CheckpointArgumentsTable_InstallScope_Column, Type::Text)); - createTableBuilder.Column(ColumnBuilder(s_CheckpointArgumentsTable_InstallArchitecture_Column, Type::Text)); - createTableBuilder.Column(ColumnBuilder(s_CheckpointArgumentsTable_Exact_Column, Type::Bool)); - createTableBuilder.Column(ColumnBuilder(s_CheckpointArgumentsTable_Interactive_Column, Type::Bool)); - createTableBuilder.Column(ColumnBuilder(s_CheckpointArgumentsTable_Silent_Column, Type::Bool)); - createTableBuilder.Column(ColumnBuilder(s_CheckpointArgumentsTable_Locale_Column, Type::Text)); - createTableBuilder.Column(ColumnBuilder(s_CheckpointArgumentsTable_Log_Column, Type::Text)); - createTableBuilder.Column(ColumnBuilder(s_CheckpointArgumentsTable_Custom_Column, Type::Text)); - createTableBuilder.Column(ColumnBuilder(s_CheckpointArgumentsTable_Override_Column, Type::Text)); - createTableBuilder.Column(ColumnBuilder(s_CheckpointArgumentsTable_InstallLocation_Column, Type::Text)); - createTableBuilder.Column(ColumnBuilder(s_CheckpointArgumentsTable_HashOverride_Column, Type::Bool)); - createTableBuilder.Column(ColumnBuilder(s_CheckpointArgumentsTable_SkipDependencies_Column, Type::Bool)); - createTableBuilder.Column(ColumnBuilder(s_CheckpointArgumentsTable_IgnoreLocalArchiveMalwareScan_Column, Type::Bool)); - createTableBuilder.Column(ColumnBuilder(s_CheckpointArgumentsTable_DependencySource_Column, Type::Text)); - createTableBuilder.Column(ColumnBuilder(s_CheckpointArgumentsTable_AcceptPackageAgreements_Column, Type::Bool)); - createTableBuilder.Column(ColumnBuilder(s_CheckpointArgumentsTable_NoUpgrade_Column, Type::Bool)); - createTableBuilder.Column(ColumnBuilder(s_CheckpointArgumentsTable_CustomHeader_Column, Type::Text)); - createTableBuilder.Column(ColumnBuilder(s_CheckpointArgumentsTable_AcceptSourceAgreements_Column, Type::Bool)); - createTableBuilder.Column(ColumnBuilder(s_CheckpointArgumentsTable_Rename_Column, Type::Text)); - createTableBuilder.Column(ColumnBuilder(s_CheckpointArgumentsTable_UninstallPrevious_Column, Type::Bool)); - createTableBuilder.Column(ColumnBuilder(s_CheckpointArgumentsTable_Force_Column, Type::Bool)); - createTableBuilder.EndColumns(); - createTableBuilder.Execute(connection); - savepoint.Commit(); - } - - std::optional CheckpointArgumentsTable::SelectByContextId(const SQLite::Connection& connection, int contextId) - { - SQLite::Builder::StatementBuilder builder; - builder.Select(SQLite::RowIDName).From(s_CheckpointArgumentsTable_Table_Name).Where(s_CheckpointArgumentsTable_ContextId_Column); - builder.Equals(contextId); - - SQLite::Statement select = builder.Prepare(connection); - - if (select.Step()) - { - return select.GetColumn(0); - } - else - { - return {}; - } - } - - bool CheckpointArgumentsTable::ExistsById(const SQLite::Connection& connection, SQLite::rowid_t id) - { - SQLite::Builder::StatementBuilder builder; - builder.Select(SQLite::Builder::RowCount).From(s_CheckpointArgumentsTable_Table_Name).Where(SQLite::RowIDName).Equals(id); - - SQLite::Statement countStatement = builder.Prepare(connection); - - THROW_HR_IF(E_UNEXPECTED, !countStatement.Step()); - - return (countStatement.GetColumn(0) != 0); - } - - void CheckpointArgumentsTable::DeleteById(SQLite::Connection& connection, SQLite::rowid_t id) - { - SQLite::Builder::StatementBuilder builder; - builder.DeleteFrom(s_CheckpointArgumentsTable_Table_Name).Where(SQLite::RowIDName).Equals(id); - - builder.Execute(connection); - } - - bool CheckpointArgumentsTable::IsEmpty(SQLite::Connection& connection) - { - SQLite::Builder::StatementBuilder builder; - builder.Select(SQLite::Builder::RowCount).From(s_CheckpointArgumentsTable_Table_Name); - - SQLite::Statement countStatement = builder.Prepare(connection); - - THROW_HR_IF(E_UNEXPECTED, !countStatement.Step()); - - return (countStatement.GetColumn(0) == 0); - } - - SQLite::rowid_t CheckpointArgumentsTable::AddContext(SQLite::Connection& connection, int contextId) - { - SQLite::Builder::StatementBuilder builder; - builder.InsertInto(s_CheckpointArgumentsTable_Table_Name) - .Columns({ s_CheckpointArgumentsTable_ContextId_Column }) - .Values(contextId); - - builder.Execute(connection); - return connection.GetLastInsertRowID(); - } - - void CheckpointArgumentsTable::RemoveContextById(SQLite::Connection& connection, SQLite::rowid_t id) - { - SQLite::Builder::StatementBuilder builder; - builder.DeleteFrom(s_CheckpointArgumentsTable_Table_Name).Where(SQLite::RowIDName).Equals(id); - builder.Execute(connection); - } - - bool CheckpointArgumentsTable::UpdateArgumentById(SQLite::Connection& connection, SQLite::rowid_t id, std::string_view name, std::string_view value) - { - SQLite::Builder::StatementBuilder builder; - builder.Update(s_CheckpointArgumentsTable_Table_Name).Set() - .Column(name).Equals(value) - .Where(SQLite::RowIDName).Equals(id); - - builder.Execute(connection); - return connection.GetChanges() != 0; - } - - bool CheckpointArgumentsTable::UpdateArgumentById(SQLite::Connection& connection, SQLite::rowid_t id, std::string_view name, bool value) - { - SQLite::Builder::StatementBuilder builder; - builder.Update(s_CheckpointArgumentsTable_Table_Name).Set() - .Column(name).Equals(value) - .Where(SQLite::RowIDName).Equals(id); - - builder.Execute(connection); - return connection.GetChanges() != 0; - } - - - bool CheckpointArgumentsTable::ContainsArgument(SQLite::Connection& connection, SQLite::rowid_t id, std::string_view name) - { - SQLite::Builder::StatementBuilder builder; - builder.Select(SQLite::Builder::RowCount).From(s_CheckpointArgumentsTable_Table_Name) - .Where(SQLite::RowIDName).Equals(id).And(name).IsNotNull(); - - SQLite::Statement statement = builder.Prepare(connection); - THROW_HR_IF(E_UNEXPECTED, !statement.Step()); - return statement.GetColumn(0) != 0; - } - - std::string CheckpointArgumentsTable::GetStringArgumentById(SQLite::Connection& connection, SQLite::rowid_t id, std::string_view name) - { - SQLite::Builder::StatementBuilder builder; - builder.Select(name).From(s_CheckpointArgumentsTable_Table_Name). - Where(SQLite::RowIDName).Equals(id); - - SQLite::Statement statement = builder.Prepare(connection); - if (statement.Step()) - { - return statement.GetColumn(0); - } - else - { - return {}; - } - } - - bool CheckpointArgumentsTable::GetBoolArgumentById(SQLite::Connection& connection, SQLite::rowid_t id, std::string_view name) - { - SQLite::Builder::StatementBuilder builder; - builder.Select(name).From(s_CheckpointArgumentsTable_Table_Name). - Where(SQLite::RowIDName).Equals(id); - - SQLite::Statement statement = builder.Prepare(connection); - if (statement.Step()) - { - return statement.GetColumn(0); - } - else - { - return {}; - } - } - - int CheckpointArgumentsTable::GetFirstContextId(SQLite::Connection& connection) - { - SQLite::Builder::StatementBuilder builder; - builder.Select(s_CheckpointArgumentsTable_ContextId_Column).From(s_CheckpointArgumentsTable_Table_Name) - .OrderBy(s_CheckpointArgumentsTable_ContextId_Column); - - SQLite::Statement statement = builder.Prepare(connection); - THROW_HR_IF(E_UNEXPECTED, !statement.Step()); - return statement.GetColumn(0); - } - - bool CheckpointArgumentsTable::SetCommandNameById(SQLite::Connection& connection, SQLite::rowid_t id, std::string_view commandName) - { - return UpdateArgumentById(connection, id, s_CheckpointArgumentsTable_CommandName_Column, commandName); - } - - std::string CheckpointArgumentsTable::GetCommandNameById(SQLite::Connection& connection, SQLite::rowid_t id) - { - return GetStringArgumentById(connection, id, s_CheckpointArgumentsTable_CommandName_Column); - } -} \ No newline at end of file diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointArgumentsTable.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointArgumentsTable.h deleted file mode 100644 index cc770732aa..0000000000 --- a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointArgumentsTable.h +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. -#pragma once -#include "SQLiteWrapper.h" -#include "SQLiteStatementBuilder.h" -#include "Microsoft/Schema/ICheckpointIndex.h" -#include - -namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 -{ - struct CheckpointArgumentsTable - { - // Get the table name. - static std::string_view TableName(); - - // Creates the table with named indices. - static void Create(SQLite::Connection& connection); - - // Gets a value indicating whether a context with the given id exists. - static bool ExistsById(const SQLite::Connection& connection, SQLite::rowid_t id); - - // Deletes the context with the given id - static void DeleteById(SQLite::Connection& connection, SQLite::rowid_t id); - - // Gets a value indicating whether the table is empty. - static bool IsEmpty(SQLite::Connection& connection); - - // Selects the context arguments by context id from the table, returning the rowid if it exists. - static std::optional SelectByContextId(const SQLite::Connection& connection, int contextId); - - // Adds a context with the given context id to the table. - static SQLite::rowid_t AddContext(SQLite::Connection& connection, int contextId); - - // Removes a context from the table by id. - static void RemoveContextById(SQLite::Connection& connection, SQLite::rowid_t id); - - // Gets a value indicating whether a context argument exists. - static bool ContainsArgument(SQLite::Connection& connection, SQLite::rowid_t id, std::string_view name); - - // Updates a string argument by id. - static bool UpdateArgumentById(SQLite::Connection& connection, SQLite::rowid_t id, std::string_view name, std::string_view value); - - // Gets a string value for an argument by id. - static std::string GetStringArgumentById(SQLite::Connection& connection, SQLite::rowid_t id, std::string_view name); - - // Updates a bool argument by id. - static bool UpdateArgumentById(SQLite::Connection& connection, SQLite::rowid_t id, std::string_view name, bool value); - - // Gets a boolean value for an argument by id. - static bool GetBoolArgumentById(SQLite::Connection& connection, SQLite::rowid_t id, std::string_view name); - - // Sets the command name by id. - static bool SetCommandNameById(SQLite::Connection& connection, SQLite::rowid_t id, std::string_view commandName); - - // Gets the command name by id. - static std::string GetCommandNameById(SQLite::Connection& connection, SQLite::rowid_t id); - - // Gets the first context id from the table. - static int GetFirstContextId(SQLite::Connection& connection); - }; -} \ No newline at end of file diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointContextTable.cpp b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointContextTable.cpp index c54dfb6184..fc98eda78f 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointContextTable.cpp +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointContextTable.cpp @@ -15,14 +15,33 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 static constexpr std::string_view s_CheckpointContextTable_Name_Column = "Name"sv; static constexpr std::string_view s_CheckpointContextTable_Value_Column = "Value"sv; - static constexpr std::string_view s_CheckpointMetadataTable_CheckpointName = "CheckpointName"sv; - static constexpr std::string_view s_CheckpointMetadataTable_ClientVersion = "ClientVersion"sv; - static constexpr std::string_view s_CheckpointMetadataTable_CommandName = "CommandName"sv; - static constexpr std::string_view s_CheckpointMetadataTable_CommandArguments = "CommandArguments"sv; - static constexpr std::string_view s_CheckpointMetadataTable_CommitTime = "CommandArguments"sv; + namespace + { + SQLite::rowid_t SetNamedValue(SQLite::Connection& connection, std::string_view name, std::string_view value) + { + SQLite::Builder::StatementBuilder builder; + builder.InsertInto(s_CheckpointContextTable_Table_Name) + .Columns({ s_CheckpointContextTable_CheckpointName_Column, + s_CheckpointContextTable_ContextData_Column, + s_CheckpointContextTable_Name_Column, + s_CheckpointContextTable_Value_Column }) + .Values(name, value); + + builder.Execute(connection); + return connection.GetLastInsertRowID(); + } - // Everytime we commit to the table we write here: + std::string GetNamedValue(SQLite::Connection& connection, std::string_view name) + { + SQLite::Builder::StatementBuilder builder; + builder.Select({ s_CheckpointContextTable_Value_Column }) + .From(s_CheckpointContextTable_Table_Name).Where(s_CheckpointContextTable_Name_Column).Equals(name); + SQLite::Statement statement = builder.Prepare(connection); + THROW_HR_IF(E_NOT_SET, !statement.Step()); + return statement.GetColumn(0); + } + } std::string_view CheckpointContextTable::TableName() { @@ -46,11 +65,11 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 savepoint.Commit(); } - std::optional CheckpointContextTable::SelectByCheckpointName(const SQLite::Connection& connection, std::string checkpointName) + std::optional CheckpointContextTable::SelectByCheckpointName(const SQLite::Connection& connection, std::string_view checkpointName) { SQLite::Builder::StatementBuilder builder; - builder.Select(SQLite::RowIDName).From(s_CheckpointContextTable_Table_Name).Where(s_CheckpointContextTable_ContextId_Column); - builder.Equals(contextId); + builder.Select(SQLite::RowIDName).From(s_CheckpointContextTable_Table_Name).Where(s_CheckpointContextTable_CheckpointName_Column); + builder.Equals(checkpointName); SQLite::Statement select = builder.Prepare(connection); @@ -80,7 +99,6 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 { SQLite::Builder::StatementBuilder builder; builder.DeleteFrom(s_CheckpointContextTable_Table_Name).Where(SQLite::RowIDName).Equals(id); - builder.Execute(connection); } @@ -96,49 +114,41 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 return (countStatement.GetColumn(0) == 0); } - SQLite::rowid_t CheckpointContextTable::AddContext(SQLite::Connection& connection, int contextId) + SQLite::rowid_t CheckpointContextTable::AddCheckpoint(SQLite::Connection& connection, std::string_view checkpointName) { SQLite::Builder::StatementBuilder builder; builder.InsertInto(s_CheckpointContextTable_Table_Name) - .Columns({ s_CheckpointContextTable_ContextId_Column }) - .Values(contextId); + .Columns({ s_CheckpointContextTable_CheckpointName_Column }) + .Values(checkpointName); builder.Execute(connection); return connection.GetLastInsertRowID(); } - void CheckpointContextTable::RemoveContextById(SQLite::Connection& connection, SQLite::rowid_t id) - { - SQLite::Builder::StatementBuilder builder; - builder.DeleteFrom(s_CheckpointContextTable_Table_Name).Where(SQLite::RowIDName).Equals(id); - builder.Execute(connection); - } - - bool CheckpointContextTable::SetLastCheckpointById(SQLite::Connection& connection, SQLite::rowid_t id, int checkpointFlag) + SQLite::rowid_t CheckpointContextTable::AddContextData(SQLite::Connection& connection, std::string_view checkpointName, int contextData, std::string_view name, std::string_view value) { SQLite::Builder::StatementBuilder builder; - builder.Update(s_CheckpointContextTable_Table_Name).Set() - .Column(s_CheckpointContextTable_LastCheckpoint_Column).Equals(checkpointFlag) - .Where(SQLite::RowIDName).Equals(id); + builder.InsertInto(s_CheckpointContextTable_Table_Name) + .Columns({ s_CheckpointContextTable_CheckpointName_Column, + s_CheckpointContextTable_ContextData_Column, + s_CheckpointContextTable_Name_Column, + s_CheckpointContextTable_Value_Column}) + .Values(checkpointName, contextData, name, value); builder.Execute(connection); - return connection.GetChanges() != 0; + return connection.GetLastInsertRowID(); } - int CheckpointContextTable::GetLastCheckpointById(SQLite::Connection& connection, SQLite::rowid_t id) + void CheckpointContextTable::RemoveContextData(SQLite::Connection& connection, std::string_view checkpointName, int contextData) { SQLite::Builder::StatementBuilder builder; - builder.Select(s_CheckpointContextTable_LastCheckpoint_Column).From(s_CheckpointContextTable_Table_Name). - Where(SQLite::RowIDName).Equals(id); + builder.Select(SQLite::RowIDName).From(s_CheckpointContextTable_Table_Name).Where(s_CheckpointContextTable_CheckpointName_Column).Equals(checkpointName); + builder.Where(s_CheckpointContextTable_ContextData_Column).Equals(contextData); - Statement statement = builder.Prepare(connection); - if (statement.Step()) - { - return statement.GetColumn(0); - } - else + SQLite::Statement select = builder.Prepare(connection); + while (select.Step()) { - return {}; + DeleteById(connection, select.GetColumn(0)); } } } \ No newline at end of file diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointContextTable.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointContextTable.h index 36d5b88730..d079efe2be 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointContextTable.h +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointContextTable.h @@ -25,20 +25,14 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 // Gets a value indicating whether the table is empty. static bool IsEmpty(SQLite::Connection& connection); - // Selects the context data by context id from the table, returning the rowid if it exists. - static std::optional SelectByContextId(const SQLite::Connection& connection, int contextId); + // Selects the context data by checkpoint name from the table, returning the rowid if it exists. + static std::optional SelectByCheckpointName(const SQLite::Connection& connection, std::string_view checkpointName); - // Adds a context row. - static SQLite::rowid_t AddContext(SQLite::Connection& connection, int contextId); - - // Removes a context row. - static void RemoveContextById(SQLite::Connection& connection, SQLite::rowid_t id); - - // Set the last checkpoint for a given context id. - static bool SetLastCheckpointById(SQLite::Connection& connection, SQLite::rowid_t id, int checkpointFlag); - - // Get the last checkpoint for a given context id. - static int GetLastCheckpointById(SQLite::Connection& connection, SQLite::rowid_t id); + // Adds a checkpoint row. + static SQLite::rowid_t AddCheckpoint(SQLite::Connection& connection, std::string_view checkpointName); + static SQLite::rowid_t AddContextData(SQLite::Connection& connection, std::string_view checkpointName, int contextData, std::string_view name, std::string_view value); + + static void RemoveContextData(SQLite::Connection& connection, std::string_view checkpointName, int contextData); }; } \ No newline at end of file diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointIndexInterface.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointIndexInterface.h index e9a03bc60c..511a276c75 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointIndexInterface.h +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointIndexInterface.h @@ -15,19 +15,12 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 bool IsEmpty(SQLite::Connection& connection) override; SQLite::rowid_t SetClientVersion(SQLite::Connection& connection, std::string_view clientVersion) override; std::string GetClientVersion(SQLite::Connection& connection) override; - SQLite::rowid_t SetCommandName(SQLite::Connection& connection, int contextId, std::string_view clientVersion) override; - std::string GetCommandName(SQLite::Connection& connection, int contextId) override; - SQLite::rowid_t AddContextToArgumentTable(SQLite::Connection& connection, int contextId) override; - void RemoveContextFromArgumentTable(SQLite::Connection& connection, int contextId) override; - SQLite::rowid_t AddContextToContextTable(SQLite::Connection& connection, int contextId) override; - void RemoveContextFromContextTable(SQLite::Connection& connection, int contextId) override; - bool ContainsArgument(SQLite::Connection& connection, int contextId, std::string_view name) override; - bool UpdateArgumentByContextId(SQLite::Connection& connection, int contextId, std::string_view name, std::string_view value) override; - bool UpdateArgumentByContextId(SQLite::Connection& connection, int contextId, std::string_view name, bool value) override; - std::string GetStringArgumentByContextId(SQLite::Connection& connection, int contextId, std::string_view name) override; - bool GetBoolArgumentByContextId(SQLite::Connection& connection, int contextId, std::string_view name) override; - int GetFirstContextId(SQLite::Connection& connection) override; - bool SetLastCheckpointByContextId(SQLite::Connection& connection, int contextId, int checkpointFlag) override; - int GetLastCheckpointByContextId(SQLite::Connection& connection, int contextId) override; + SQLite::rowid_t SetCommandName(SQLite::Connection& connection, std::string_view commandName) override; + std::string GetCommandName(SQLite::Connection& connection) override; + SQLite::rowid_t SetCommandArguments(SQLite::Connection& connection, std::string_view commandArguments) override; + std::string GetCommandArguments(SQLite::Connection& connection) override; + SQLite::rowid_t AddContextData(SQLite::Connection& connection, std::string_view checkpointName, int contextData, std::string_view name, std::string_view value) override; + void RemoveContextData(SQLite::Connection& connection, std::string_view checkpointName, int contextData) override; + }; } \ No newline at end of file diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointIndexInterface_1_0.cpp b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointIndexInterface_1_0.cpp index 8d35e64cf0..b4b3f629ed 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointIndexInterface_1_0.cpp +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointIndexInterface_1_0.cpp @@ -2,39 +2,11 @@ // Licensed under the MIT License. #include "pch.h" #include "Microsoft/Schema/Checkpoint_1_0/CheckpointIndexInterface.h" -#include "Microsoft/Schema/Checkpoint_1_0/CheckpointArgumentsTable.h" #include "Microsoft/Schema/Checkpoint_1_0/CheckpointContextTable.h" #include "Microsoft/Schema/Checkpoint_1_0/CheckpointMetadataTable.h" namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 { - namespace - { - std::optional GetExistingContextRowIdFromArgumentTable(const SQLite::Connection& connection, int contextId) - { - auto result = CheckpointArgumentsTable::SelectByContextId(connection, contextId); - - if (!result) - { - AICLI_LOG(Repo, Verbose, << "Did not find a context with the contextId { " << contextId << " }"); - } - - return result; - } - - std::optional GetExistingContextRowIdFromContextTable(const SQLite::Connection& connection, int contextId) - { - auto result = CheckpointContextTable::SelectByContextId(connection, contextId); - - if (!result) - { - AICLI_LOG(Repo, Verbose, << "Did not find a context with the contextId { " << contextId << " }"); - } - - return result; - } - } - Schema::Version CheckpointIndexInterface::GetVersion() const { return { 1, 0 }; @@ -43,7 +15,6 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 void CheckpointIndexInterface::CreateTables(SQLite::Connection& connection) { SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "createCheckpointTables_v1_0"); - Checkpoint_V1_0::CheckpointArgumentsTable::Create(connection); Checkpoint_V1_0::CheckpointContextTable::Create(connection); Checkpoint_V1_0::CheckpointMetadataTable::Create(connection); savepoint.Commit(); @@ -62,138 +33,49 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 return CheckpointMetadataTable::GetClientVersion(connection); } - SQLite::rowid_t CheckpointIndexInterface::SetCommandName(SQLite::Connection& connection, int contextId, std::string_view commandName) + SQLite::rowid_t CheckpointIndexInterface::SetCommandName(SQLite::Connection& connection, std::string_view commandName) { - auto contextResult = GetExistingContextRowIdFromArgumentTable(connection, contextId); - THROW_HR_IF(E_NOT_SET, !contextResult); - SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "setCommandName_v1_0"); - SQLite::rowid_t argumentId = CheckpointArgumentsTable::SetCommandNameById(connection, contextResult.value(), commandName); + SQLite::rowid_t id = CheckpointMetadataTable::SetCommandName(connection, commandName); savepoint.Commit(); - return argumentId; - } - - std::string CheckpointIndexInterface::GetCommandName(SQLite::Connection& connection, int contextId) - { - auto contextResult = GetExistingContextRowIdFromArgumentTable(connection, contextId); - THROW_HR_IF(E_NOT_SET, !contextResult); - return CheckpointArgumentsTable::GetCommandNameById(connection, contextResult.value()); - } - - bool CheckpointIndexInterface::IsEmpty(SQLite::Connection& connection) - { - return CheckpointArgumentsTable::IsEmpty(connection) && CheckpointContextTable::IsEmpty(connection); + return id; } - SQLite::rowid_t CheckpointIndexInterface::AddContextToArgumentTable(SQLite::Connection& connection, int contextId) + std::string CheckpointIndexInterface::GetCommandName(SQLite::Connection& connection) { - auto contextResult = GetExistingContextRowIdFromArgumentTable(connection, contextId); - THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS), contextResult.has_value()); - - SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "addContextToArgTable_v1_0"); - SQLite::rowid_t rowId = CheckpointArgumentsTable::AddContext(connection, contextId); - savepoint.Commit(); - return rowId; + return CheckpointMetadataTable::GetCommandName(connection); } - void CheckpointIndexInterface::RemoveContextFromArgumentTable(SQLite::Connection& connection, int contextId) + SQLite::rowid_t CheckpointIndexInterface::SetCommandArguments(SQLite::Connection& connection, std::string_view commandArguments) { - auto contextResult = GetExistingContextRowIdFromArgumentTable(connection, contextId); - THROW_HR_IF(E_NOT_SET, !contextResult); - - SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "removeContextFromArgTable_v1_0"); - CheckpointArgumentsTable::RemoveContextById(connection, contextResult.value()); + SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "setCommandName_v1_0"); + SQLite::rowid_t id = CheckpointMetadataTable::SetCommandArguments(connection, commandArguments); savepoint.Commit(); + return id; } - SQLite::rowid_t CheckpointIndexInterface::AddContextToContextTable(SQLite::Connection& connection, int contextId) + std::string CheckpointIndexInterface::GetCommandArguments(SQLite::Connection& connection) { - auto contextResult = GetExistingContextRowIdFromContextTable(connection, contextId); - THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS), contextResult.has_value()); - - SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "addContextToContextTable_v1_0"); - SQLite::rowid_t rowId = CheckpointContextTable::AddContext(connection, contextId); - savepoint.Commit(); - return rowId; + return CheckpointMetadataTable::GetCommandArguments(connection); } - void CheckpointIndexInterface::RemoveContextFromContextTable(SQLite::Connection& connection, int contextId) - { - auto contextResult = GetExistingContextRowIdFromContextTable(connection, contextId); - THROW_HR_IF(E_NOT_SET, !contextResult); - - SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "removeContextFromContextTable_v1_0"); - CheckpointContextTable::RemoveContextById(connection, contextResult.value()); - savepoint.Commit(); - } - - bool CheckpointIndexInterface::UpdateArgumentByContextId(SQLite::Connection& connection, int contextId, std::string_view name, std::string_view value) + bool CheckpointIndexInterface::IsEmpty(SQLite::Connection& connection) { - auto contextResult = GetExistingContextRowIdFromArgumentTable(connection, contextId); - THROW_HR_IF(E_NOT_SET, !contextResult); - - SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "updateContextArgument_v1_0"); - bool status = CheckpointArgumentsTable::UpdateArgumentById(connection, contextResult.value(), name, value); - savepoint.Commit(); - return status; + return CheckpointContextTable::IsEmpty(connection); } - bool CheckpointIndexInterface::UpdateArgumentByContextId(SQLite::Connection& connection, int contextId, std::string_view name, bool value) + SQLite::rowid_t CheckpointIndexInterface::AddContextData(SQLite::Connection& connection, std::string_view checkpointName, int contextData, std::string_view name, std::string_view value) { - auto contextResult = GetExistingContextRowIdFromArgumentTable(connection, contextId); - THROW_HR_IF(E_NOT_SET, !contextResult); - - SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "updateContextArgument_v1_0"); - bool status = CheckpointArgumentsTable::UpdateArgumentById(connection, contextResult.value(), name, value); + SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "addContextData_v1_0"); + SQLite::rowid_t rowId = CheckpointContextTable::AddContextData(connection, checkpointName, contextData, name, value); savepoint.Commit(); - return status; - } - - bool CheckpointIndexInterface::ContainsArgument(SQLite::Connection& connection, int contextId, std::string_view name) - { - auto contextResult = GetExistingContextRowIdFromArgumentTable(connection, contextId); - THROW_HR_IF(E_NOT_SET, !contextResult); - - return CheckpointArgumentsTable::ContainsArgument(connection, contextResult.value(), name); - } - - std::string CheckpointIndexInterface::GetStringArgumentByContextId(SQLite::Connection& connection, int contextId, std::string_view name) - { - auto contextResult = GetExistingContextRowIdFromArgumentTable(connection, contextId); - THROW_HR_IF(E_NOT_SET, !contextResult); - - return CheckpointArgumentsTable::GetStringArgumentById(connection, contextResult.value(), name); - } - - bool CheckpointIndexInterface::GetBoolArgumentByContextId(SQLite::Connection& connection, int contextId, std::string_view name) - { - auto contextResult = GetExistingContextRowIdFromArgumentTable(connection, contextId); - THROW_HR_IF(E_NOT_SET, !contextResult); - - return CheckpointArgumentsTable::GetBoolArgumentById(connection, contextResult.value(), name); - } - - int CheckpointIndexInterface::GetFirstContextId(SQLite::Connection& connection) - { - return CheckpointArgumentsTable::GetFirstContextId(connection); + return rowId; } - bool CheckpointIndexInterface::SetLastCheckpointByContextId(SQLite::Connection& connection, int contextId, int checkpointFlag) + void CheckpointIndexInterface::RemoveContextData(SQLite::Connection& connection, std::string_view checkpointName, int contextData) { - auto contextResult = GetExistingContextRowIdFromContextTable(connection, contextId); - THROW_HR_IF(E_NOT_SET, !contextResult); - - SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "updateLastCheckpointFlag_v1_0"); - bool status = CheckpointContextTable::SetLastCheckpointById(connection, contextResult.value(), checkpointFlag); + SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "removeContextData_v1_0"); + CheckpointContextTable::RemoveContextData(connection, checkpointName, contextData); savepoint.Commit(); - return status; - } - - int CheckpointIndexInterface::GetLastCheckpointByContextId(SQLite::Connection& connection, int contextId) - { - auto contextResult = GetExistingContextRowIdFromContextTable(connection, contextId); - THROW_HR_IF(E_NOT_SET, !contextResult); - - return CheckpointContextTable::GetLastCheckpointById(connection, contextResult.value()); } } \ No newline at end of file diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointMetadataTable.cpp b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointMetadataTable.cpp index 498db9b556..7af76e101c 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointMetadataTable.cpp +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointMetadataTable.cpp @@ -9,30 +9,22 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 { using namespace std::string_view_literals; static constexpr std::string_view s_CheckpointMetadataTable_Table_Name = "CheckpointMetadata"sv; - static constexpr std::string_view s_CheckpointMetadataTable_Name_Column = "Name"sv; - static constexpr std::string_view s_CheckpointMetadataTable_Value_Column = "Value"sv; + static constexpr std::string_view s_CheckpointMetadataTable_Name_Column = "Name"; + static constexpr std::string_view s_CheckpointMetadataTable_Value_Column = "Value"; - static constexpr std::string_view s_CheckpointMetadataTable_CheckpointName = "CheckpointName"sv; static constexpr std::string_view s_CheckpointMetadataTable_ClientVersion = "ClientVersion"sv; static constexpr std::string_view s_CheckpointMetadataTable_CommandName = "CommandName"sv; static constexpr std::string_view s_CheckpointMetadataTable_CommandArguments = "CommandArguments"sv; - static constexpr std::string_view s_CheckpointMetadataTable_CommitTime = "CommandArguments"sv;c - - static constexpr std::string_view s_CheckpointContextTable_CheckpointName_Column = "CheckpointName"sv; - static constexpr std::string_view s_CheckpointContextTable_ContextData_Column = "ContextData"sv; - static constexpr std::string_view s_CheckpointContextTable_Name_Column = "Name"sv; - static constexpr std::string_view s_CheckpointContextTable_Value_Column = "Value"sv; namespace { - SQLite::rowid_t SetNamedValue(SQLite::Connection& connection, std::string_view checkpoint, std::string_view name, std::string_view value) + SQLite::rowid_t SetNamedValue(SQLite::Connection& connection, std::string_view name, std::string_view value) { SQLite::Builder::StatementBuilder builder; builder.InsertInto(s_CheckpointMetadataTable_Table_Name) - .Columns({ s_CheckpointContextTable_CheckpointName_Column, - s_CheckpointMetadataTable_Name_Column, + .Columns({ s_CheckpointMetadataTable_Name_Column, s_CheckpointMetadataTable_Value_Column }) - .Values(checkpoint, name, value); + .Values(name, value); builder.Execute(connection); return connection.GetLastInsertRowID(); @@ -62,11 +54,10 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "createCheckpointMetadataTable_v1_0"); StatementBuilder createTableBuilder; - createTableBuilder.CreateTable(s_CheckpointMetadataTable_Table_Name).Columns({ - ColumnBuilder(s_CheckpointMetadataTable_Name_Column, Type::Text).NotNull(), - ColumnBuilder(s_CheckpointMetadataTable_Value_Column, Type::Text).NotNull() - }); - + createTableBuilder.CreateTable(s_CheckpointMetadataTable_Table_Name).BeginColumns(); + createTableBuilder.Column(ColumnBuilder(s_CheckpointMetadataTable_Name_Column, Type::Text)); + createTableBuilder.Column(ColumnBuilder(s_CheckpointMetadataTable_Value_Column, Type::Text)); + createTableBuilder.EndColumns(); createTableBuilder.Execute(connection); savepoint.Commit(); @@ -81,4 +72,24 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 { return GetNamedValue(connection, s_CheckpointMetadataTable_ClientVersion); } + + SQLite::rowid_t CheckpointMetadataTable::SetCommandName(SQLite::Connection& connection, std::string_view commandName) + { + return SetNamedValue(connection, s_CheckpointMetadataTable_CommandName, commandName); + } + + std::string CheckpointMetadataTable::GetCommandName(SQLite::Connection& connection) + { + return GetNamedValue(connection, s_CheckpointMetadataTable_CommandName); + } + + SQLite::rowid_t CheckpointMetadataTable::SetCommandArguments(SQLite::Connection& connection, std::string_view commandArguments) + { + return SetNamedValue(connection, s_CheckpointMetadataTable_CommandArguments, commandArguments); + } + + std::string CheckpointMetadataTable::GetCommandArguments(SQLite::Connection& connection) + { + return GetNamedValue(connection, s_CheckpointMetadataTable_CommandArguments); + } } \ No newline at end of file diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointMetadataTable.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointMetadataTable.h index 7617bf333c..88040d100c 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointMetadataTable.h +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointMetadataTable.h @@ -21,5 +21,17 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 // Sets the client version of the saved state. static SQLite::rowid_t SetClientVersion(SQLite::Connection& connection, std::string_view clientVersion); + + // Gets the command name of the saved state. + static std::string GetCommandName(SQLite::Connection& connection); + + // Sets the command name of the saved state. + static SQLite::rowid_t SetCommandName(SQLite::Connection& connection, std::string_view commandName); + + // Gets the command arguments of the saved state. + static std::string GetCommandArguments(SQLite::Connection& connection); + + // Sets the command name of the saved state. + static SQLite::rowid_t SetCommandArguments(SQLite::Connection& connection, std::string_view commandArguments); }; } \ No newline at end of file diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/ICheckpointIndex.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/ICheckpointIndex.h index 1c1dde33f8..1a943af0af 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/Schema/ICheckpointIndex.h +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/ICheckpointIndex.h @@ -26,46 +26,22 @@ namespace AppInstaller::Repository::Microsoft::Schema // Gets the client version associated with this checkpoint index. virtual std::string GetClientVersion(SQLite::Connection& connection) = 0; - // Sets the command name for a given context id. - virtual SQLite::rowid_t SetCommandName(SQLite::Connection& connection, int contextId, std::string_view commandName) = 0; + // Sets the command name. + virtual SQLite::rowid_t SetCommandName(SQLite::Connection& connection, std::string_view commandName) = 0; - // Gets the command name for a given context id. - virtual std::string GetCommandName(SQLite::Connection& connection, int contextId) = 0; + // Gets the command name. + virtual std::string GetCommandName(SQLite::Connection& connection) = 0; - // Adds a new row with the given context id to the checkpoint argument table. - virtual SQLite::rowid_t AddContextToArgumentTable(SQLite::Connection& connection, int contextId) = 0; + // Sets the command arguments. + virtual SQLite::rowid_t SetCommandArguments(SQLite::Connection& connection, std::string_view commandArguments) = 0; - // removes the given context id from the checkpoint argument table. - virtual void RemoveContextFromArgumentTable(SQLite::Connection& connection, int contextId) = 0; + // Gets the command arguments. + virtual std::string GetCommandArguments(SQLite::Connection& connection) = 0; - // Adds a new row with the given context id to the checkpoint context table. - virtual SQLite::rowid_t AddContextToContextTable(SQLite::Connection& connection, int contextId) = 0; + // Adds the context data property for a given checkpoint. + virtual SQLite::rowid_t AddContextData(SQLite::Connection& connection, std::string_view checkpointName, int contextData, std::string_view name, std::string_view value) = 0; - // Removes the given context id from the checkpoint context table. - virtual void RemoveContextFromContextTable(SQLite::Connection& connection, int contextId) = 0; - - // Returns a boolean value indicating whether an argument exists for a given context id. - virtual bool ContainsArgument(SQLite::Connection& connection, int contextId, std::string_view name) = 0; - - // Updates the boolean value of an argument for a given context id. - virtual bool UpdateArgumentByContextId(SQLite::Connection& connection, int contextId, std::string_view name, std::string_view value) = 0; - - // Updates the string value of an argument for a given context id. - virtual bool UpdateArgumentByContextId(SQLite::Connection& connection, int contextId, std::string_view name, bool value) = 0; - - // Gets the string value of an argument for a given context id. - virtual std::string GetStringArgumentByContextId(SQLite::Connection& connection, int contextId, std::string_view name) = 0; - - // Gets the boolean value of an argument for a given context id. - virtual bool GetBoolArgumentByContextId(SQLite::Connection& connection, int contextId, std::string_view name) = 0; - - // Gets the first context id in the checkpoint table. - virtual int GetFirstContextId(SQLite::Connection& connection) = 0; - - // Sets the last checkpoint for a given context id. - virtual bool SetLastCheckpointByContextId(SQLite::Connection& connection, int contextId, int checkpointFlag) = 0; - - // Gets the last checkpoint for a given context id. - virtual int GetLastCheckpointByContextId(SQLite::Connection& connection, int contextId) = 0; + // Removes the context data for a given checkpoint. + virtual void RemoveContextData(SQLite::Connection& connection, std::string_view checkpointName, int contextData) = 0; }; } \ No newline at end of file From 35b772f957201467951519a35ed3b0a8f44f39f6 Mon Sep 17 00:00:00 2001 From: Ryan Fu Date: Mon, 14 Aug 2023 09:47:13 -0700 Subject: [PATCH 15/43] it builds --- src/AppInstallerCLICore/CheckpointManager.h | 1 - src/AppInstallerCLICore/ExecutionContext.cpp | 10 ++++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/AppInstallerCLICore/CheckpointManager.h b/src/AppInstallerCLICore/CheckpointManager.h index 65604cf986..b853fc8a00 100644 --- a/src/AppInstallerCLICore/CheckpointManager.h +++ b/src/AppInstallerCLICore/CheckpointManager.h @@ -1,7 +1,6 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once -#include "Microsoft/CheckpointIndex.h" #include namespace AppInstaller::Repository::Microsoft diff --git a/src/AppInstallerCLICore/ExecutionContext.cpp b/src/AppInstallerCLICore/ExecutionContext.cpp index fadb214a54..4de11ff76d 100644 --- a/src/AppInstallerCLICore/ExecutionContext.cpp +++ b/src/AppInstallerCLICore/ExecutionContext.cpp @@ -5,7 +5,6 @@ #include "COMContext.h" #include "Argument.h" #include "winget/UserSettings.h" -#include "Command.h" namespace AppInstaller::CLI::Execution { @@ -424,12 +423,13 @@ namespace AppInstaller::CLI::Execution // Initialized the checkpoint manager if it does not exist, then captures the automatic metadata as well as the context data. void Context::Checkpoint(std::string_view checkpointName, std::vector contextData) { + UNREFERENCED_PARAMETER(checkpointName); for (auto data : contextData) { switch (data) { case Execution::Data::Installer: - m_checkpointManager->RecordContextData(checkpointName, Get()); + break; default: THROW_HR(HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED)); } @@ -455,11 +455,13 @@ namespace AppInstaller::CLI::Execution void Context::InitializeCheckpointManager(GUID id) { - m_checkpointManager = std::make_unique(id); + auto checkpointManager = std::make_unique(id); + m_checkpointManager = std::move(checkpointManager); } void Context::InitializeCheckpointManager(std::string_view commandName, std::string_view commandArguments, std::string_view clientVersion) { - m_checkpointManager = std::make_unique(commandName, commandArguments, clientVersion); + auto checkpointManager = std::make_unique(commandName, commandArguments, clientVersion); + m_checkpointManager = std::move(checkpointManager); } } From 9f7f3f51feb4a98687cce757f440b982c7d22986 Mon Sep 17 00:00:00 2001 From: Ryan Fu Date: Mon, 14 Aug 2023 16:00:24 -0700 Subject: [PATCH 16/43] capture context data --- src/AppInstallerCLICore/CheckpointManager.cpp | 21 +++++-- src/AppInstallerCLICore/CheckpointManager.h | 10 ++-- src/AppInstallerCLICore/Command.cpp | 3 +- src/AppInstallerCLICore/Command.h | 1 + .../Commands/InstallCommand.cpp | 6 +- .../Commands/ResumeCommand.cpp | 8 ++- src/AppInstallerCLICore/Core.cpp | 2 - src/AppInstallerCLICore/ExecutionContext.cpp | 57 ++++++++++++------- src/AppInstallerCLICore/ExecutionContext.h | 10 ++-- .../Microsoft/CheckpointIndex.cpp | 7 ++- .../Microsoft/CheckpointIndex.h | 2 + .../Checkpoint_1_0/CheckpointContextTable.cpp | 16 +++--- .../Checkpoint_1_0/CheckpointContextTable.h | 2 +- .../Checkpoint_1_0/CheckpointIndexInterface.h | 1 + .../CheckpointIndexInterface_1_0.cpp | 5 ++ .../Microsoft/Schema/ICheckpointIndex.h | 2 + 16 files changed, 99 insertions(+), 54 deletions(-) diff --git a/src/AppInstallerCLICore/CheckpointManager.cpp b/src/AppInstallerCLICore/CheckpointManager.cpp index 36fb7757fc..badd41de30 100644 --- a/src/AppInstallerCLICore/CheckpointManager.cpp +++ b/src/AppInstallerCLICore/CheckpointManager.cpp @@ -43,11 +43,24 @@ namespace AppInstaller::CLI::Checkpoint m_checkpointIndex->SetClientVersion(clientVersion); } - template<> - void CheckpointManager::RecordContextData(std::string_view checkpointName, Manifest::ManifestInstaller installer) + void CheckpointManager::LoadContextData(std::string_view checkpointName, AppInstaller::Manifest::ManifestInstaller& installer) { - m_checkpointIndex->AddContextData(checkpointName, 1, "installerUrl"sv, installer.Url); - }; + int contextData = static_cast(Execution::Data::Installer); + const auto& map = m_checkpointIndex->GetContextData(checkpointName, contextData); + installer.Url = map.at("url"); + installer.Arch = Utility::ConvertToArchitectureEnum(map.at("arch")); + } + + void CheckpointManager::RecordContextData(std::string_view checkpointName, const AppInstaller::Manifest::ManifestInstaller& installer) + { + int contextData = static_cast(Execution::Data::Installer); + m_checkpointIndex->AddContextData(checkpointName, contextData, "url"sv, installer.Url); + m_checkpointIndex->AddContextData(checkpointName, contextData, "arch"sv, ToString(installer.Arch)); + m_checkpointIndex->AddContextData(checkpointName, contextData, "scope"sv, ScopeToString(installer.Scope)); + m_checkpointIndex->AddContextData(checkpointName, contextData, "installerType"sv, InstallerTypeToString(installer.BaseInstallerType)); + m_checkpointIndex->AddContextData(checkpointName, contextData, "sha256"sv, Utility::SHA256::ConvertToString(installer.Sha256)); + m_checkpointIndex->AddContextData(checkpointName, contextData, "locale"sv, installer.Locale); + } std::string CheckpointManager::GetClientVersion() { diff --git a/src/AppInstallerCLICore/CheckpointManager.h b/src/AppInstallerCLICore/CheckpointManager.h index b853fc8a00..75b42e92e4 100644 --- a/src/AppInstallerCLICore/CheckpointManager.h +++ b/src/AppInstallerCLICore/CheckpointManager.h @@ -2,6 +2,7 @@ // Licensed under the MIT License. #pragma once #include +#include "winget/ManifestCommon.h" namespace AppInstaller::Repository::Microsoft { @@ -25,12 +26,9 @@ namespace AppInstaller::CLI::Checkpoint std::string GetArguments(); - template - void RecordContextData(std::string_view checkpointName, T data) - { - UNREFERENCED_PARAMETER(checkpointName); - UNREFERENCED_PARAMETER(data); - } + void LoadContextData(std::string_view checkpointName, AppInstaller::Manifest::ManifestInstaller& installer); + + void RecordContextData(std::string_view checkpointName, const AppInstaller::Manifest::ManifestInstaller& installer); private: GUID m_checkpointId = {}; diff --git a/src/AppInstallerCLICore/Command.cpp b/src/AppInstallerCLICore/Command.cpp index eed93f956c..d2330085dd 100644 --- a/src/AppInstallerCLICore/Command.cpp +++ b/src/AppInstallerCLICore/Command.cpp @@ -855,10 +855,9 @@ namespace AppInstaller::CLI if (Settings::ExperimentalFeature::IsEnabled(ExperimentalFeature::Feature::Resume)) { - // Only the resume command requires a resume id. If not present, create checkpoint index and capture initial arguments. if (!context.Args.Contains(Execution::Args::Type::ResumeId)) { - context.InitializeCheckpointManager(Name(), "install Microsoft.PowerToys"sv, AppInstaller::Runtime::GetClientVersion()); + context.InitializeCheckpointManager(Name()); } } diff --git a/src/AppInstallerCLICore/Command.h b/src/AppInstallerCLICore/Command.h index 30326c24e4..15804ab86f 100644 --- a/src/AppInstallerCLICore/Command.h +++ b/src/AppInstallerCLICore/Command.h @@ -129,6 +129,7 @@ namespace AppInstaller::CLI Settings::TogglePolicy::Policy m_groupPolicy; CommandOutputFlags m_outputFlags; bool m_selectCurrentCommandIfUnrecognizedSubcommandFound = false; + std::string m_commandArguments; }; template diff --git a/src/AppInstallerCLICore/Commands/InstallCommand.cpp b/src/AppInstallerCLICore/Commands/InstallCommand.cpp index bb9b56fe2e..da51e79c22 100644 --- a/src/AppInstallerCLICore/Commands/InstallCommand.cpp +++ b/src/AppInstallerCLICore/Commands/InstallCommand.cpp @@ -106,12 +106,10 @@ namespace AppInstaller::CLI void InstallCommand::Resume(Context& context) const { - // Load arguments from index. - auto commandArguments = context.GetCommandArguments(); - Invocation invocation{ std::move(commandArguments) }; + auto commandLineArgs = context.GetArgsFromCheckpointIndex(); + Invocation invocation{ std::move(commandLineArgs) }; ParseArguments(invocation, context.Args); - // Load checkpoints from index. context.LoadCheckpoints(); // Set flags and disable workflow execution. diff --git a/src/AppInstallerCLICore/Commands/ResumeCommand.cpp b/src/AppInstallerCLICore/Commands/ResumeCommand.cpp index 9f7c2064de..aa5b8a4cfd 100644 --- a/src/AppInstallerCLICore/Commands/ResumeCommand.cpp +++ b/src/AppInstallerCLICore/Commands/ResumeCommand.cpp @@ -52,11 +52,13 @@ namespace AppInstaller::CLI AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_RESUME_GUID_NOT_FOUND); } + Execution::Context resumeContext{ std::cout, std::cin }; - auto previousThreadGlobals = context.SetForCurrentThread(); - context.EnableSignalTerminationHandler(); + auto previousThreadGlobals = resumeContext.SetForCurrentThread(); + resumeContext.EnableSignalTerminationHandler(); + resumeContext.InitializeCheckpointManager(checkpointId); - std::string commandName = context.GetCheckpointCommand(); + std::string commandName = resumeContext.GetCheckpointCommand(); std::unique_ptr commandToResume; // Find the command using the root command. diff --git a/src/AppInstallerCLICore/Core.cpp b/src/AppInstallerCLICore/Core.cpp index aca5556879..8c887cc1d1 100644 --- a/src/AppInstallerCLICore/Core.cpp +++ b/src/AppInstallerCLICore/Core.cpp @@ -104,8 +104,6 @@ namespace AppInstaller::CLI return strstr.str(); }()); - - context.SetCommandArguments(utf8Args); Invocation invocation{ std::move(utf8Args) }; // The root command is our fallback in the event of very bad or very little input diff --git a/src/AppInstallerCLICore/ExecutionContext.cpp b/src/AppInstallerCLICore/ExecutionContext.cpp index 4de11ff76d..73c6a7c22b 100644 --- a/src/AppInstallerCLICore/ExecutionContext.cpp +++ b/src/AppInstallerCLICore/ExecutionContext.cpp @@ -5,6 +5,7 @@ #include "COMContext.h" #include "Argument.h" #include "winget/UserSettings.h" +#include "AppInstallerRuntime.h" namespace AppInstaller::CLI::Execution { @@ -416,41 +417,39 @@ namespace AppInstaller::CLI::Execution void Context::LoadCheckpoints() { - // Call a function here that retrieves the checkpoint in reverse order so that the entire state is completely populated. - // Retreives all metadata from the stored info in the checkpoint index and applies the state change to the context. + // Hard code retrieve the installer + AppInstaller::Manifest::ManifestInstaller installer; + m_checkpointManager->LoadContextData("InstallerSelected"sv, installer); + Add(installer); } - // Initialized the checkpoint manager if it does not exist, then captures the automatic metadata as well as the context data. void Context::Checkpoint(std::string_view checkpointName, std::vector contextData) { - UNREFERENCED_PARAMETER(checkpointName); for (auto data : contextData) { switch (data) { case Execution::Data::Installer: + { + const auto& installer = Get().value(); + m_checkpointManager->RecordContextData(checkpointName, installer); break; + } default: THROW_HR(HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED)); } } } - void Context::SetCommandArguments(std::vector args) + std::string Context::GetCommandLineString() { - m_commandLineArgs = args; - //std::stringstream strstr; - //for (const auto& arg : args) - //{ - // strstr << arg << ' '; - //} - - //const std::string& commandLine = strstr.str(); - } + std::stringstream strstr; + for (const auto& arg : m_commandLineArgs) + { + strstr << arg << ' '; + } - std::vector Context::GetCommandArguments() - { - return m_commandLineArgs; + return strstr.str(); } void Context::InitializeCheckpointManager(GUID id) @@ -459,9 +458,29 @@ namespace AppInstaller::CLI::Execution m_checkpointManager = std::move(checkpointManager); } - void Context::InitializeCheckpointManager(std::string_view commandName, std::string_view commandArguments, std::string_view clientVersion) + void Context::InitializeCheckpointManager(std::string_view commandName) { - auto checkpointManager = std::make_unique(commandName, commandArguments, clientVersion); + const auto& clientVersion = AppInstaller::Runtime::GetClientVersion(); + const auto& commandLineString = GetCommandLineFromArgs(); + auto checkpointManager = std::make_unique(commandName, commandLineString, clientVersion); m_checkpointManager = std::move(checkpointManager); } + + std::string Context::GetCommandLineFromArgs() + { + std::string commandLine; + for (auto type : Args.GetTypes()) + { + const auto& argument = Argument::ForType(type); + commandLine += "--" + std::string{ argument.Name() } + " " + std::string{ Args.GetArg(type) }; + } + + return commandLine; + } + + std::vector Context::GetArgsFromCheckpointIndex() + { + const auto& commandLineString = m_checkpointManager->GetArguments(); + return Utility::Split(commandLineString, ' '); + } } diff --git a/src/AppInstallerCLICore/ExecutionContext.h b/src/AppInstallerCLICore/ExecutionContext.h index 5987e41d73..850052d474 100644 --- a/src/AppInstallerCLICore/ExecutionContext.h +++ b/src/AppInstallerCLICore/ExecutionContext.h @@ -174,13 +174,15 @@ namespace AppInstaller::CLI::Execution // Gets the target checkpoint of the resume state. std::string_view GetCheckpoint() { return m_checkpoint; }; + std::string GetCommandLineFromArgs(); + std::string GetCheckpointCommand(); - std::vector GetCommandLineArgs() { return m_commandLineArgs; }; + void SetCommandLineArgs(std::vector args) { m_commandLineArgs = args; }; - std::vector GetCommandArguments(); + std::vector GetArgsFromCheckpointIndex(); - void SetCommandArguments(std::vector args); + std::string GetCommandLineString(); void DisableWorkflowExecution(bool state) { m_disableWorkflowExecution = state; }; @@ -188,7 +190,7 @@ namespace AppInstaller::CLI::Execution void InitializeCheckpointManager(GUID id); - void InitializeCheckpointManager(std::string_view commandName, std::string_view commandArguments, std::string_view clientVersion); + void InitializeCheckpointManager(std::string_view commandName); protected: // Copies the args that are also needed in a sub-context. E.g., silent diff --git a/src/AppInstallerRepositoryCore/Microsoft/CheckpointIndex.cpp b/src/AppInstallerRepositoryCore/Microsoft/CheckpointIndex.cpp index 5cbd731d8c..b0dc6f0d1f 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/CheckpointIndex.cpp +++ b/src/AppInstallerRepositoryCore/Microsoft/CheckpointIndex.cpp @@ -146,7 +146,7 @@ namespace AppInstaller::Repository::Microsoft SQLite::Savepoint savepoint = SQLite::Savepoint::Create(m_dbconn, "checkpointindex_setcommandarguments"); - IdType result = m_interface->SetClientVersion(m_dbconn, commandArguments); + IdType result = m_interface->SetCommandArguments(m_dbconn, commandArguments); SetLastWriteTime(); savepoint.Commit(); @@ -169,6 +169,11 @@ namespace AppInstaller::Repository::Microsoft return rowId; } + std::map CheckpointIndex::GetContextData(std::string_view checkpointName, int contextData) + { + return m_interface->GetContextData(m_dbconn, checkpointName, contextData); + } + void CheckpointIndex::RemoveContextData(std::string_view checkpointName, int contextData) { std::lock_guard lockInterface{ *m_interfaceLock }; diff --git a/src/AppInstallerRepositoryCore/Microsoft/CheckpointIndex.h b/src/AppInstallerRepositoryCore/Microsoft/CheckpointIndex.h index 129522502a..dc31f92646 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/CheckpointIndex.h +++ b/src/AppInstallerRepositoryCore/Microsoft/CheckpointIndex.h @@ -50,6 +50,8 @@ namespace AppInstaller::Repository::Microsoft IdType AddContextData(std::string_view checkpointName, int contextData, std::string_view name, std::string_view value); + std::map GetContextData(std::string_view checkpointName, int contextData); + void RemoveContextData(std::string_view checkpointName, int contextData); private: diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointContextTable.cpp b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointContextTable.cpp index fc98eda78f..8f0177fbfc 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointContextTable.cpp +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointContextTable.cpp @@ -65,22 +65,22 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 savepoint.Commit(); } - std::optional CheckpointContextTable::SelectByCheckpointName(const SQLite::Connection& connection, std::string_view checkpointName) + std::map CheckpointContextTable::GetContextData(const SQLite::Connection& connection, std::string_view checkpointName, int contextData) { SQLite::Builder::StatementBuilder builder; builder.Select(SQLite::RowIDName).From(s_CheckpointContextTable_Table_Name).Where(s_CheckpointContextTable_CheckpointName_Column); - builder.Equals(checkpointName); + builder.Equals(checkpointName).And(s_CheckpointContextTable_ContextData_Column).Equals(contextData); SQLite::Statement select = builder.Prepare(connection); - if (select.Step()) - { - return select.GetColumn(0); - } - else + std::map contextDataMap; + while (select.Step()) { - return {}; + auto [rowCheckpointName, rowContextData, name, value] = select.GetRow(); + contextDataMap.emplace(name, value); } + + return contextDataMap; } bool CheckpointContextTable::ExistsById(const SQLite::Connection& connection, SQLite::rowid_t id) diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointContextTable.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointContextTable.h index d079efe2be..5b441397b9 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointContextTable.h +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointContextTable.h @@ -26,7 +26,7 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 static bool IsEmpty(SQLite::Connection& connection); // Selects the context data by checkpoint name from the table, returning the rowid if it exists. - static std::optional SelectByCheckpointName(const SQLite::Connection& connection, std::string_view checkpointName); + static std::map GetContextData(const SQLite::Connection& connection, std::string_view checkpointName, int contextData); // Adds a checkpoint row. static SQLite::rowid_t AddCheckpoint(SQLite::Connection& connection, std::string_view checkpointName); diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointIndexInterface.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointIndexInterface.h index 511a276c75..53d6cea2a5 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointIndexInterface.h +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointIndexInterface.h @@ -20,6 +20,7 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 SQLite::rowid_t SetCommandArguments(SQLite::Connection& connection, std::string_view commandArguments) override; std::string GetCommandArguments(SQLite::Connection& connection) override; SQLite::rowid_t AddContextData(SQLite::Connection& connection, std::string_view checkpointName, int contextData, std::string_view name, std::string_view value) override; + std::map GetContextData(SQLite::Connection& connection, std::string_view checkpointName, int contextData) override; void RemoveContextData(SQLite::Connection& connection, std::string_view checkpointName, int contextData) override; }; diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointIndexInterface_1_0.cpp b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointIndexInterface_1_0.cpp index b4b3f629ed..0fb4794032 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointIndexInterface_1_0.cpp +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointIndexInterface_1_0.cpp @@ -72,6 +72,11 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 return rowId; } + std::map CheckpointIndexInterface::GetContextData(SQLite::Connection& connection, std::string_view checkpointName, int contextData) + { + return CheckpointContextTable::GetContextData(connection, checkpointName, contextData); + } + void CheckpointIndexInterface::RemoveContextData(SQLite::Connection& connection, std::string_view checkpointName, int contextData) { SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "removeContextData_v1_0"); diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/ICheckpointIndex.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/ICheckpointIndex.h index 1a943af0af..36c987f7f6 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/Schema/ICheckpointIndex.h +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/ICheckpointIndex.h @@ -41,6 +41,8 @@ namespace AppInstaller::Repository::Microsoft::Schema // Adds the context data property for a given checkpoint. virtual SQLite::rowid_t AddContextData(SQLite::Connection& connection, std::string_view checkpointName, int contextData, std::string_view name, std::string_view value) = 0; + virtual std::map GetContextData(SQLite::Connection& connection, std::string_view checkpointName, int contextData) = 0; + // Removes the context data for a given checkpoint. virtual void RemoveContextData(SQLite::Connection& connection, std::string_view checkpointName, int contextData) = 0; }; From e939e90f761ed54be56bb0555fa9bfa4bcca80f4 Mon Sep 17 00:00:00 2001 From: Ryan Fu Date: Tue, 15 Aug 2023 13:55:36 -0700 Subject: [PATCH 17/43] simplify work and update tests --- src/AppInstallerCLICore/Argument.cpp | 2 +- src/AppInstallerCLICore/CheckpointManager.cpp | 64 ++---- src/AppInstallerCLICore/CheckpointManager.h | 17 +- src/AppInstallerCLICore/Command.cpp | 4 +- .../Commands/InstallCommand.cpp | 6 +- .../Commands/ResumeCommand.cpp | 17 +- src/AppInstallerCLICore/ExecutionContext.cpp | 71 ++---- src/AppInstallerCLICore/ExecutionContext.h | 37 +--- src/AppInstallerCLICore/Resources.h | 6 +- .../Workflows/ResumeFlow.cpp | 50 +---- .../Workflows/WorkflowBase.cpp | 8 - src/AppInstallerCLIE2ETests/Constants.cs | 5 +- .../Shared/Strings/en-us/winget.resw | 13 +- src/AppInstallerCLITests/CheckpointIndex.cpp | 206 ++---------------- src/AppInstallerCLITests/ResumeFlow.cpp | 109 ++++----- src/AppInstallerCLITests/TestHooks.h | 18 -- .../Public/AppInstallerRuntime.h | 2 +- .../Microsoft/CheckpointIndex.cpp | 4 +- .../Microsoft/CheckpointIndex.h | 2 +- .../Checkpoint_1_0/CheckpointContextTable.cpp | 40 ++-- .../Checkpoint_1_0/CheckpointContextTable.h | 5 +- .../Checkpoint_1_0/CheckpointIndexInterface.h | 2 +- .../CheckpointIndexInterface_1_0.cpp | 4 +- .../Microsoft/Schema/ICheckpointIndex.h | 3 +- src/AppInstallerSharedLib/Errors.cpp | 6 +- .../Public/AppInstallerErrors.h | 5 +- 26 files changed, 187 insertions(+), 519 deletions(-) diff --git a/src/AppInstallerCLICore/Argument.cpp b/src/AppInstallerCLICore/Argument.cpp index 4d88b35222..4de77d3a31 100644 --- a/src/AppInstallerCLICore/Argument.cpp +++ b/src/AppInstallerCLICore/Argument.cpp @@ -350,7 +350,7 @@ namespace AppInstaller::CLI case Args::Type::InstallerType: return Argument{ type, Resource::String::InstallerTypeArgumentDescription, ArgumentType::Standard, Argument::Visibility::Help, false }; case Args::Type::ResumeId: - return Argument{ type, Resource::String::ResumeGuidArgumentDescription, ArgumentType::Standard, true }; + return Argument{ type, Resource::String::ResumeIdArgumentDescription, ArgumentType::Standard, true }; default: THROW_HR(E_UNEXPECTED); } diff --git a/src/AppInstallerCLICore/CheckpointManager.cpp b/src/AppInstallerCLICore/CheckpointManager.cpp index badd41de30..88eb0cea81 100644 --- a/src/AppInstallerCLICore/CheckpointManager.cpp +++ b/src/AppInstallerCLICore/CheckpointManager.cpp @@ -4,21 +4,22 @@ #include "CheckpointManager.h" #include "Microsoft/CheckpointIndex.h" #include "Microsoft/SQLiteStorageBase.h" -#include -#include +#include namespace AppInstaller::CLI::Checkpoint { + using namespace AppInstaller::Repository::Microsoft; + CheckpointManager::CheckpointManager(GUID id) { m_checkpointId = id; AICLI_LOG(CLI, Info, << "Opening checkpoint index with id: " << m_checkpointId); - auto openDisposition = AppInstaller::Repository::Microsoft::SQLiteStorageBase::OpenDisposition::ReadWrite; - auto checkpointIndex = AppInstaller::Repository::Microsoft::CheckpointIndex::OpenOrCreateDefault(m_checkpointId, openDisposition); + auto openDisposition = SQLiteStorageBase::OpenDisposition::ReadWrite; + auto checkpointIndex = CheckpointIndex::OpenOrCreateDefault(m_checkpointId, openDisposition); if (!checkpointIndex) { AICLI_LOG(CLI, Error, << "Unable to open checkpoint index."); - // TODO: Handle failure to open index gracefully. + THROW_HR_MSG(APPINSTALLER_CLI_ERROR_CANNOT_OPEN_CHECKPOINT_INDEX, "The checkpoint index could not be opened."); } m_checkpointIndex = std::move(checkpointIndex); @@ -29,12 +30,12 @@ namespace AppInstaller::CLI::Checkpoint std::ignore = CoCreateGuid(&m_checkpointId); AICLI_LOG(CLI, Info, << "Creating checkpoint index with id: " << m_checkpointId); - auto openDisposition = AppInstaller::Repository::Microsoft::SQLiteStorageBase::OpenDisposition::ReadWrite; - auto checkpointIndex = AppInstaller::Repository::Microsoft::CheckpointIndex::OpenOrCreateDefault(m_checkpointId, openDisposition); + auto openDisposition = SQLiteStorageBase::OpenDisposition::ReadWrite; + auto checkpointIndex = CheckpointIndex::OpenOrCreateDefault(m_checkpointId, openDisposition); if (!checkpointIndex) { AICLI_LOG(CLI, Error, << "Unable to open checkpoint index."); - // TODO: Handle failure to open index gracefully. + return; } m_checkpointIndex = std::move(checkpointIndex); @@ -43,25 +44,6 @@ namespace AppInstaller::CLI::Checkpoint m_checkpointIndex->SetClientVersion(clientVersion); } - void CheckpointManager::LoadContextData(std::string_view checkpointName, AppInstaller::Manifest::ManifestInstaller& installer) - { - int contextData = static_cast(Execution::Data::Installer); - const auto& map = m_checkpointIndex->GetContextData(checkpointName, contextData); - installer.Url = map.at("url"); - installer.Arch = Utility::ConvertToArchitectureEnum(map.at("arch")); - } - - void CheckpointManager::RecordContextData(std::string_view checkpointName, const AppInstaller::Manifest::ManifestInstaller& installer) - { - int contextData = static_cast(Execution::Data::Installer); - m_checkpointIndex->AddContextData(checkpointName, contextData, "url"sv, installer.Url); - m_checkpointIndex->AddContextData(checkpointName, contextData, "arch"sv, ToString(installer.Arch)); - m_checkpointIndex->AddContextData(checkpointName, contextData, "scope"sv, ScopeToString(installer.Scope)); - m_checkpointIndex->AddContextData(checkpointName, contextData, "installerType"sv, InstallerTypeToString(installer.BaseInstallerType)); - m_checkpointIndex->AddContextData(checkpointName, contextData, "sha256"sv, Utility::SHA256::ConvertToString(installer.Sha256)); - m_checkpointIndex->AddContextData(checkpointName, contextData, "locale"sv, installer.Locale); - } - std::string CheckpointManager::GetClientVersion() { return m_checkpointIndex->GetClientVersion(); @@ -72,29 +54,23 @@ namespace AppInstaller::CLI::Checkpoint return m_checkpointIndex->GetCommandName(); } - void CheckpointManager::CleanUpIndex() + std::string CheckpointManager::GetArguments() { - bool isIndexEmpty = m_checkpointIndex->IsEmpty(); - - m_checkpointIndex.reset(); + return m_checkpointIndex->GetCommandArguments(); + } - if (isIndexEmpty) + void CheckpointManager::CleanUpIndex() + { + if (m_checkpointIndex != nullptr) { - const auto& checkpointIndexPath = AppInstaller::Repository::Microsoft::CheckpointIndex::GetCheckpointIndexPath(m_checkpointId); - if (std::filesystem::remove(checkpointIndexPath)) + m_checkpointIndex.reset(); + const auto& checkpointIndexPath = CheckpointIndex::GetCheckpointIndexPath(m_checkpointId); + + std::error_code error; + if (std::filesystem::remove(checkpointIndexPath, error)) { AICLI_LOG(CLI, Info, << "Checkpoint index deleted: " << checkpointIndexPath); } } } - - CheckpointManager::~CheckpointManager() - { - CleanUpIndex(); - } - - std::string CheckpointManager::GetArguments() - { - return m_checkpointIndex->GetCommandArguments(); - } } diff --git a/src/AppInstallerCLICore/CheckpointManager.h b/src/AppInstallerCLICore/CheckpointManager.h index 75b42e92e4..df368fd0b7 100644 --- a/src/AppInstallerCLICore/CheckpointManager.h +++ b/src/AppInstallerCLICore/CheckpointManager.h @@ -15,25 +15,24 @@ namespace AppInstaller::CLI::Checkpoint { // Constructor for an existing resume id. CheckpointManager(GUID id); + // Constructor for a new resume save state. CheckpointManager(std::string_view commandName, std::string_view commandArguments, std::string_view clientVersion); - ~CheckpointManager(); - + // Gets the client version from the checkpoint index. std::string GetClientVersion(); + // Gets the command name from the checkpoint index. std::string GetCommandName(); + // Gets the command arguments from the checkpoint index. std::string GetArguments(); - void LoadContextData(std::string_view checkpointName, AppInstaller::Manifest::ManifestInstaller& installer); - - void RecordContextData(std::string_view checkpointName, const AppInstaller::Manifest::ManifestInstaller& installer); + // Releases and deletes the checkpoint index. + void CleanUpIndex(); private: - GUID m_checkpointId = {}; - std::shared_ptr m_checkpointIndex = nullptr; - - void CleanUpIndex(); + GUID m_checkpointId; + std::shared_ptr m_checkpointIndex; }; } diff --git a/src/AppInstallerCLICore/Command.cpp b/src/AppInstallerCLICore/Command.cpp index d2330085dd..5383a01862 100644 --- a/src/AppInstallerCLICore/Command.cpp +++ b/src/AppInstallerCLICore/Command.cpp @@ -857,7 +857,9 @@ namespace AppInstaller::CLI { if (!context.Args.Contains(Execution::Args::Type::ResumeId)) { - context.InitializeCheckpointManager(Name()); + const auto& clientVersion = AppInstaller::Runtime::GetClientVersion().get(); + const auto& commandLineString = context.GetCommandLineFromArgs(); + context.InitializeCheckpointManager(Name(), commandLineString, clientVersion); } } diff --git a/src/AppInstallerCLICore/Commands/InstallCommand.cpp b/src/AppInstallerCLICore/Commands/InstallCommand.cpp index da51e79c22..d67b973afa 100644 --- a/src/AppInstallerCLICore/Commands/InstallCommand.cpp +++ b/src/AppInstallerCLICore/Commands/InstallCommand.cpp @@ -106,15 +106,11 @@ namespace AppInstaller::CLI void InstallCommand::Resume(Context& context) const { - auto commandLineArgs = context.GetArgsFromCheckpointIndex(); + auto commandLineArgs = context.GetArgsFromCheckpoint(); Invocation invocation{ std::move(commandLineArgs) }; ParseArguments(invocation, context.Args); - context.LoadCheckpoints(); - - // Set flags and disable workflow execution. context.SetFlags(Execution::ContextFlag::Resume); - context.DisableWorkflowExecution(true); ExecuteInternal(context); } diff --git a/src/AppInstallerCLICore/Commands/ResumeCommand.cpp b/src/AppInstallerCLICore/Commands/ResumeCommand.cpp index aa5b8a4cfd..0b6d531fb5 100644 --- a/src/AppInstallerCLICore/Commands/ResumeCommand.cpp +++ b/src/AppInstallerCLICore/Commands/ResumeCommand.cpp @@ -40,16 +40,16 @@ namespace AppInstaller::CLI std::string resumeGuidString { context.Args.GetArg(Execution::Args::Type::ResumeId) }; if (!Utility::IsValidGuidString(resumeGuidString)) { - context.Reporter.Error() << Resource::String::InvalidResumeGuidError(Utility::LocIndView{ resumeGuidString }) << std::endl; - AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_INVALID_RESUME_GUID); + context.Reporter.Error() << Resource::String::InvalidResumeIdError(Utility::LocIndView{ resumeGuidString }) << std::endl; + AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_INVALID_RESUME_ID); } GUID checkpointId = Utility::ConvertToGuid(resumeGuidString); if (!std::filesystem::exists(AppInstaller::Repository::Microsoft::CheckpointIndex::GetCheckpointIndexPath(checkpointId))) { - context.Reporter.Error() << Resource::String::ResumeGuidNotFoundError(Utility::LocIndView{ resumeGuidString }) << std::endl; - AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_RESUME_GUID_NOT_FOUND); + context.Reporter.Error() << Resource::String::ResumeIdNotFoundError(Utility::LocIndView{ resumeGuidString }) << std::endl; + AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_RESUME_ID_NOT_FOUND); } @@ -58,7 +58,14 @@ namespace AppInstaller::CLI resumeContext.EnableSignalTerminationHandler(); resumeContext.InitializeCheckpointManager(checkpointId); - std::string commandName = resumeContext.GetCheckpointCommand(); + const auto& checkpointClientVersion = resumeContext.GetClientVersionFromCheckpoint(); + if (checkpointClientVersion != AppInstaller::Runtime::GetClientVersion().get()) + { + context.Reporter.Error() << Resource::String::ClientVersionMismatchError(Utility::LocIndView{ checkpointClientVersion }) << std::endl; + AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_CLIENT_VERSION_MISMATCH); + } + + std::string commandName = resumeContext.GetCommandNameFromCheckpoint(); std::unique_ptr commandToResume; // Find the command using the root command. diff --git a/src/AppInstallerCLICore/ExecutionContext.cpp b/src/AppInstallerCLICore/ExecutionContext.cpp index 73c6a7c22b..1c5a8aeb20 100644 --- a/src/AppInstallerCLICore/ExecutionContext.cpp +++ b/src/AppInstallerCLICore/ExecutionContext.cpp @@ -252,6 +252,12 @@ namespace AppInstaller::CLI::Execution Context::~Context() { + // TODO: Update so that only certain terminated HRs allow the checkpoint index to persist. + if (!m_isTerminated) + { + m_checkpointManager->CleanUpIndex(); + } + if (m_disableSignalTerminationHandlerOnExit) { EnableSignalTerminationHandler(false); @@ -410,60 +416,21 @@ namespace AppInstaller::CLI::Execution } #endif - std::string Context::GetCheckpointCommand() + void Context::InitializeCheckpointManager(GUID id) { - return m_checkpointManager->GetCommandName(); + m_checkpointManager = std::make_unique(id); } - void Context::LoadCheckpoints() + void Context::InitializeCheckpointManager(std::string_view commandName, std::string_view commandArguments, std::string_view clientVersion) { - // Hard code retrieve the installer - AppInstaller::Manifest::ManifestInstaller installer; - m_checkpointManager->LoadContextData("InstallerSelected"sv, installer); - Add(installer); + m_checkpointManager = std::make_unique(commandName, commandArguments, clientVersion); } void Context::Checkpoint(std::string_view checkpointName, std::vector contextData) { - for (auto data : contextData) - { - switch (data) - { - case Execution::Data::Installer: - { - const auto& installer = Get().value(); - m_checkpointManager->RecordContextData(checkpointName, installer); - break; - } - default: - THROW_HR(HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED)); - } - } - } - - std::string Context::GetCommandLineString() - { - std::stringstream strstr; - for (const auto& arg : m_commandLineArgs) - { - strstr << arg << ' '; - } - - return strstr.str(); - } - - void Context::InitializeCheckpointManager(GUID id) - { - auto checkpointManager = std::make_unique(id); - m_checkpointManager = std::move(checkpointManager); - } - - void Context::InitializeCheckpointManager(std::string_view commandName) - { - const auto& clientVersion = AppInstaller::Runtime::GetClientVersion(); - const auto& commandLineString = GetCommandLineFromArgs(); - auto checkpointManager = std::make_unique(commandName, commandLineString, clientVersion); - m_checkpointManager = std::move(checkpointManager); + // TODO: Implementation for capturing each context data specified in the provided vector. + UNREFERENCED_PARAMETER(checkpointName); + UNREFERENCED_PARAMETER(contextData); } std::string Context::GetCommandLineFromArgs() @@ -478,9 +445,19 @@ namespace AppInstaller::CLI::Execution return commandLine; } - std::vector Context::GetArgsFromCheckpointIndex() + std::vector Context::GetArgsFromCheckpoint() { const auto& commandLineString = m_checkpointManager->GetArguments(); return Utility::Split(commandLineString, ' '); } + + std::string Context::GetCommandNameFromCheckpoint() + { + return m_checkpointManager->GetCommandName(); + } + + std::string Context::GetClientVersionFromCheckpoint() + { + return m_checkpointManager->GetClientVersion(); + } } diff --git a/src/AppInstallerCLICore/ExecutionContext.h b/src/AppInstallerCLICore/ExecutionContext.h index 850052d474..d882cdd4f7 100644 --- a/src/AppInstallerCLICore/ExecutionContext.h +++ b/src/AppInstallerCLICore/ExecutionContext.h @@ -162,35 +162,26 @@ namespace AppInstaller::CLI::Execution // Enable tests to override behavior bool ShouldExecuteWorkflowTask(const Workflow::WorkflowTask& task); #endif - // Sets the current checkpoint flag of the context. - void SetCurrentCheckpoint(std::string_view checkpointName) { m_checkpoint = checkpointName; } + // Initialize an existing checkpoint manager with a resume id. + void InitializeCheckpointManager(GUID id); - // Retrieves the checkpoints stored in the checkpoint index and populates the context state. - void LoadCheckpoints(); + // Initialize a new checkpoint manager. + void InitializeCheckpointManager(std::string_view commandName, std::string_view commandArguments, std::string_view clientVersion); // Records the provided context data to the checkpoint index. If it already exists, loads the context instead. void Checkpoint(std::string_view checkpointName, std::vector contextData); - // Gets the target checkpoint of the resume state. - std::string_view GetCheckpoint() { return m_checkpoint; }; - + // Gets the command line string from the store context arguments. std::string GetCommandLineFromArgs(); - std::string GetCheckpointCommand(); - - void SetCommandLineArgs(std::vector args) { m_commandLineArgs = args; }; - - std::vector GetArgsFromCheckpointIndex(); - - std::string GetCommandLineString(); + // Gets the arguments from the checkpoint index. + std::vector GetArgsFromCheckpoint(); - void DisableWorkflowExecution(bool state) { m_disableWorkflowExecution = state; }; + // Gets the command name from the checkpoint index. + std::string GetCommandNameFromCheckpoint(); - bool ShouldExecuteWorkflow() { return !m_disableWorkflowExecution; }; - - void InitializeCheckpointManager(GUID id); - - void InitializeCheckpointManager(std::string_view commandName); + // Gets the client version from the checkpoint index. + std::string GetClientVersionFromCheckpoint(); protected: // Copies the args that are also needed in a sub-context. E.g., silent @@ -210,12 +201,6 @@ namespace AppInstaller::CLI::Execution Workflow::ExecutionStage m_executionStage = Workflow::ExecutionStage::Initial; AppInstaller::ThreadLocalStorage::WingetThreadGlobals m_threadGlobals; AppInstaller::CLI::Command* m_executingCommand = nullptr; - - // This store the entire command line string that is used to execute a command. std::unique_ptr m_checkpointManager; - bool m_disableWorkflowExecution = false; - std::string_view m_checkpoint = {}; - std::string m_commandLineString = {}; - std::vector m_commandLineArgs = {}; }; } diff --git a/src/AppInstallerCLICore/Resources.h b/src/AppInstallerCLICore/Resources.h index bc3e21a21e..b28a68386d 100644 --- a/src/AppInstallerCLICore/Resources.h +++ b/src/AppInstallerCLICore/Resources.h @@ -282,7 +282,7 @@ namespace AppInstaller::CLI::Resource WINGET_DEFINE_RESOURCE_STRINGID(InvalidJsonFile); WINGET_DEFINE_RESOURCE_STRINGID(InvalidNameError); WINGET_DEFINE_RESOURCE_STRINGID(InvalidPathToNestedInstaller); - WINGET_DEFINE_RESOURCE_STRINGID(InvalidResumeGuidError); + WINGET_DEFINE_RESOURCE_STRINGID(InvalidResumeIdError); WINGET_DEFINE_RESOURCE_STRINGID(KeyDirectoriesHeader); WINGET_DEFINE_RESOURCE_STRINGID(LicenseAgreement); WINGET_DEFINE_RESOURCE_STRINGID(Links); @@ -408,8 +408,8 @@ namespace AppInstaller::CLI::Resource WINGET_DEFINE_RESOURCE_STRINGID(ReservedFilenameError); WINGET_DEFINE_RESOURCE_STRINGID(ResumeCommandLongDescription); WINGET_DEFINE_RESOURCE_STRINGID(ResumeCommandShortDescription); - WINGET_DEFINE_RESOURCE_STRINGID(ResumeGuidArgumentDescription); - WINGET_DEFINE_RESOURCE_STRINGID(ResumeGuidNotFoundError); + WINGET_DEFINE_RESOURCE_STRINGID(ResumeIdArgumentDescription); + WINGET_DEFINE_RESOURCE_STRINGID(ResumeIdNotFoundError); WINGET_DEFINE_RESOURCE_STRINGID(ResumeStateDataNotFoundError); WINGET_DEFINE_RESOURCE_STRINGID(RetroArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(SearchCommandLongDescription); diff --git a/src/AppInstallerCLICore/Workflows/ResumeFlow.cpp b/src/AppInstallerCLICore/Workflows/ResumeFlow.cpp index 981b62b5b0..bcd8067ed2 100644 --- a/src/AppInstallerCLICore/Workflows/ResumeFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/ResumeFlow.cpp @@ -1,52 +1,10 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" -#include "AppInstallerRuntime.h" -#include "CheckpointManager.h" -#include "Microsoft/CheckpointIndex.h" #include "ResumeFlow.h" -using namespace AppInstaller::CLI::Execution; -using namespace AppInstaller::CLI::Checkpoint; -using namespace AppInstaller::Manifest; -using namespace AppInstaller::Repository; -using namespace AppInstaller::Settings; -using namespace AppInstaller::Utility; -using namespace AppInstaller::Utility::literals; - namespace AppInstaller::CLI::Workflow { - void EnsureSupportForResume(Execution::Context& context) - { - UNREFERENCED_PARAMETER(context); - //std::string resumeGuidString { context.Args.GetArg(Execution::Args::Type::ResumeId) }; - //if (!Utility::IsValidGuidString(resumeGuidString)) - //{ - // context.Reporter.Error() << Resource::String::InvalidResumeGuidError(Utility::LocIndView{ resumeGuidString }) << std::endl; - // AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_INVALID_RESUME_GUID); - //} - - //GUID checkpointId = Utility::ConvertToGuid(resumeGuidString); - - //if (!std::filesystem::exists(AppInstaller::Repository::Microsoft::CheckpointIndex::GetCheckpointIndexPath(checkpointId))) - //{ - // context.Reporter.Error() << Resource::String::ResumeGuidNotFoundError(Utility::LocIndView{ resumeGuidString }) << std::endl; - // AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_RESUME_GUID_NOT_FOUND); - //} - - //if (AppInstaller::Runtime::GetClientVersion().get() != resumeStateClientVersion) - //{ - // context.Reporter.Error() << Resource::String::ClientVersionMismatchError(Utility::LocIndView{ resumeStateClientVersion }) << std::endl; - // AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_CLIENT_VERSION_MISMATCH); - //} - - //if (!checkpointManager.HasContext()) - //{ - // context.Reporter.Error() << Resource::String::ResumeStateDataNotFoundError << std::endl; - // AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_INVALID_RESUME_STATE); - //} - } - void Checkpoint::operator()(Execution::Context& context) const { if (!Settings::ExperimentalFeature::IsEnabled(Settings::ExperimentalFeature::Feature::Resume)) @@ -56,15 +14,11 @@ namespace AppInstaller::CLI::Workflow if (WI_IsFlagSet(context.GetFlags(), Execution::ContextFlag::Resume)) { - if (context.GetCheckpoint() == m_checkpointName) - { - // We have reached the checkpoint, begin enabling workflows. - context.DisableWorkflowExecution(false); - } + // TODO: If the resume flag is set, resume workflow behavior. } else { - // If it is not a resume, simply capture the data in the index. + // If this is not a resume, simply capture the context data. context.Checkpoint(m_checkpointName, m_contextData); } } diff --git a/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp b/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp index 56cf862d32..cdd114a516 100644 --- a/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp +++ b/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp @@ -1275,14 +1275,6 @@ AppInstaller::CLI::Execution::Context& operator<<(AppInstaller::CLI::Execution:: AppInstaller::CLI::Execution::Context& operator<<(AppInstaller::CLI::Execution::Context& context, const AppInstaller::CLI::Workflow::WorkflowTask& task) { - if (AppInstaller::Settings::ExperimentalFeature::IsEnabled(AppInstaller::Settings::ExperimentalFeature::Feature::Resume)) - { - if (WI_IsFlagSet(context.GetFlags(), AppInstaller::CLI::Execution::ContextFlag::Resume) && !context.ShouldExecuteWorkflow()) - { - return context; - } - } - if (!context.IsTerminated()) { #ifndef AICLI_DISABLE_TEST_HOOKS diff --git a/src/AppInstallerCLIE2ETests/Constants.cs b/src/AppInstallerCLIE2ETests/Constants.cs index 084e2847cb..a7fb163492 100644 --- a/src/AppInstallerCLIE2ETests/Constants.cs +++ b/src/AppInstallerCLIE2ETests/Constants.cs @@ -250,10 +250,11 @@ public class ErrorCode public const int ERROR_APPTERMINATION_RECEIVED = unchecked((int)0x8A15006A); public const int ERROR_DOWNLOAD_DEPENDENCIES = unchecked((int)0x8A15006B); public const int ERROR_DOWNLOAD_COMMAND_PROHIBITED = unchecked((int)0x8A15006C); - public const int ERROR_INVALID_RESUME_GUID = unchecked((int)0x8A15006D); - public const int ERROR_RESUME_GUID_NOT_FOUND = unchecked((int)0x8A15006E); + public const int ERROR_INVALID_RESUME_ID = unchecked((int)0x8A15006D); + public const int ERROR_RESUME_ID_NOT_FOUND = unchecked((int)0x8A15006E); public const int ERROR_CLIENT_VERSION_MISMATCH = unchecked((int)0x8A15006F); public const int ERROR_INVALID_RESUME_STATE = unchecked((int)0x8A150070); + public const int ERROR_CANNOT_OPEN_CHECKPOINT_INDEX = unchecked((int)0x8A150071); public const int ERROR_INSTALL_PACKAGE_IN_USE = unchecked((int)0x8A150101); public const int ERROR_INSTALL_INSTALL_IN_PROGRESS = unchecked((int)0x8A150102); diff --git a/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw b/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw index a78da2456e..d3dee10a74 100644 --- a/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw +++ b/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw @@ -2065,20 +2065,17 @@ Please specify one of them using the --source option to proceed. Resumes execution of a previously saved command. - - The unique guid identifier of the saved state to resume + + The unique identifier of the saved state to resume - - The resume guid string provided is invalid: {0} - {Locked="{0}", "guid"} -{0} represents the guid string that the user provided. -{1} 'guid' is the short name form of a globally unique identifier. + + The resume id string provided is invalid: {0} Resuming the state from a different client version is not supported: {0} {Locked= "{0}"} Message displayed to inform the user that the client version of the resume state does not match the current client version. {0} is a placeholder for the client version that created the resume state. - + The resume state does not exist: {0} {Locked="{0}" Error message displayed when the user provides a guid that does not correspond to a valid saved state. {0} is a placeholder replaced by the provided guid string. diff --git a/src/AppInstallerCLITests/CheckpointIndex.cpp b/src/AppInstallerCLITests/CheckpointIndex.cpp index 27b9e61058..d9625d623b 100644 --- a/src/AppInstallerCLITests/CheckpointIndex.cpp +++ b/src/AppInstallerCLITests/CheckpointIndex.cpp @@ -3,20 +3,13 @@ #include "pch.h" #include "TestCommon.h" #include -#include using namespace std::string_literals; using namespace TestCommon; -using namespace AppInstaller::CLI; using namespace AppInstaller::Repository::Microsoft; using namespace AppInstaller::Repository::SQLite; using namespace AppInstaller::Repository::Microsoft::Schema; -constexpr std::string_view s_ArgumentName_Id = "id"sv; -constexpr std::string_view s_ArgumentName_Version = "version"sv; -constexpr std::string_view s_ArgumentName_AcceptPackageAgreements = "accept-package-agreements"sv; -constexpr std::string_view s_ArgumentName_HashOverride = "ignore-security-hash"sv; - TEST_CASE("CheckpointIndexCreateLatestAndReopen", "[checkpointIndex]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; @@ -39,222 +32,59 @@ TEST_CASE("CheckpointIndexCreateLatestAndReopen", "[checkpointIndex]") } } -TEST_CASE("CheckpointIndexAddContext", "[checkpointIndex]") +TEST_CASE("CheckpointIndex_WriteMetadata", "[checkpointIndex]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); - int testContextId = 0; std::string_view testCommand = "install"sv; std::string_view testClientVersion = "1.20.1234"sv; + std::string_view testArguments = "install --id Microsoft.PowerToys"; { CheckpointIndex index = CheckpointIndex::CreateNew(tempFile, { 1, 0 }); - REQUIRE(index.IsEmpty()); - index.AddContext(testContextId); - index.SetCommandName(testContextId, testCommand); + index.SetCommandName(testCommand); index.SetClientVersion(testClientVersion); + index.SetCommandArguments(testArguments); } { CheckpointIndex index = CheckpointIndex::Open(tempFile, SQLiteStorageBase::OpenDisposition::ReadWrite); - int firstContextId = index.GetFirstContextId(); - REQUIRE(testContextId == firstContextId); - REQUIRE(testCommand == index.GetCommandName(firstContextId)); + REQUIRE(testCommand == index.GetCommandName()); REQUIRE(testClientVersion == index.GetClientVersion()); - - index.RemoveContext(firstContextId); - REQUIRE(index.IsEmpty()); + REQUIRE(testArguments == index.GetCommandArguments()); } } -TEST_CASE("CheckpointIndex_ArgumentsTable_AddRemoveArguments", "[checkpointIndex]") +TEST_CASE("CheckpointIndex_WriteContextData", "[checkpointIndex]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); - int testContextId = 0; - std::string_view testPackageIdentifier = "AppInstallerTest.TestExeInstaller"sv; - std::string_view testPackageVersion = "1.2.3.4"sv; - bool testAcceptPackageAgreements = true; - bool testHashOverride = true; - - { - CheckpointIndex index = CheckpointIndex::CreateNew(tempFile, { 1, 0 }); - REQUIRE(index.IsEmpty()); - index.AddContext(testContextId); - - // Verify arguments do not exist. - REQUIRE_FALSE(index.ContainsArgument(testContextId, s_ArgumentName_Id)); - REQUIRE_FALSE(index.ContainsArgument(testContextId, s_ArgumentName_Version)); - REQUIRE_FALSE(index.ContainsArgument(testContextId, s_ArgumentName_AcceptPackageAgreements)); - REQUIRE_FALSE(index.ContainsArgument(testContextId, s_ArgumentName_HashOverride)); - - // Add valid context arguments; - REQUIRE(index.UpdateArgument(testContextId, s_ArgumentName_Id, testPackageIdentifier)); - REQUIRE(index.UpdateArgument(testContextId, s_ArgumentName_Version, testPackageVersion)); - REQUIRE(index.UpdateArgument(testContextId, s_ArgumentName_AcceptPackageAgreements, testAcceptPackageAgreements)); - REQUIRE(index.UpdateArgument(testContextId, s_ArgumentName_HashOverride, testHashOverride)); - } - - { - // Open index and verify values. - CheckpointIndex index = CheckpointIndex::Open(tempFile, SQLiteStorageBase::OpenDisposition::ReadWrite); - int firstContextId = index.GetFirstContextId(); - - // Verify arguments exist. - REQUIRE(index.ContainsArgument(testContextId, s_ArgumentName_Id)); - REQUIRE(index.ContainsArgument(testContextId, s_ArgumentName_Version)); - REQUIRE(index.ContainsArgument(testContextId, s_ArgumentName_AcceptPackageAgreements)); - REQUIRE(index.ContainsArgument(testContextId, s_ArgumentName_HashOverride)); - - // Verify context arguments were added. - REQUIRE(testPackageIdentifier == index.GetStringArgument(testContextId, "id"sv)); - REQUIRE(testPackageVersion == index.GetStringArgument(testContextId, "version"sv)); - REQUIRE(testAcceptPackageAgreements == index.GetBoolArgument(testContextId, "accept-package-agreements"sv)); - REQUIRE(testHashOverride == index.GetBoolArgument(testContextId, "ignore-security-hash"sv)); - - index.RemoveContext(firstContextId); - REQUIRE(index.IsEmpty()); - } -} - -TEST_CASE("CheckpointIndex_ContextTable_AddRemoveContext", "[checkpointIndex]") -{ - TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; - INFO("Using temporary file named: " << tempFile.GetPath()); + std::string_view testCheckpoint = "testCheckpoint"sv; + int testContextData = 3; + std::string_view testName = "testName"; + std::string_view testValue = "testValue"; - int testContextId = 0; + std::string_view testName2 = "exampleName"; + std::string_view testValue2 = "exampleValue"; { CheckpointIndex index = CheckpointIndex::CreateNew(tempFile, { 1, 0 }); - REQUIRE(index.IsEmpty()); - index.AddContext(testContextId); - index.SetLastCheckpoint(testContextId, 0); + index.AddContextData(testCheckpoint, testContextData, testName, testValue); + index.AddContextData(testCheckpoint, testContextData, testName2, testValue2); } { CheckpointIndex index = CheckpointIndex::Open(tempFile, SQLiteStorageBase::OpenDisposition::ReadWrite); - REQUIRE(index.GetLastCheckpoint(testContextId) == 0); - index.SetLastCheckpoint(testContextId, 1); - REQUIRE(index.GetLastCheckpoint(testContextId) == 1); - - index.RemoveContext(testContextId); - REQUIRE(index.IsEmpty()); - } -} + REQUIRE(testValue == index.GetContextData(testCheckpoint, testContextData, testName)); + REQUIRE(testValue2 == index.GetContextData(testCheckpoint, testContextData, testName2)); -TEST_CASE("CheckpointIndex_VerifyContextOrder", "[checkpointIndex]") -{ - // Verify that the context id order is retrieved in order regardless of what sequence they are added. - TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; - INFO("Using temporary file named: " << tempFile.GetPath()); - - { - CheckpointIndex index = CheckpointIndex::CreateNew(tempFile, { 1, 0 }); - REQUIRE(index.IsEmpty()); - index.AddContext(3); - index.AddContext(1); - index.AddContext(0); - index.AddContext(2); + index.RemoveContextData(testCheckpoint, testContextData); } { CheckpointIndex index = CheckpointIndex::Open(tempFile, SQLiteStorageBase::OpenDisposition::ReadWrite); - REQUIRE(index.GetFirstContextId() == 0); - index.RemoveContext(0); - REQUIRE(index.GetFirstContextId() == 1); - index.RemoveContext(1); - REQUIRE(index.GetFirstContextId() == 2); - - // The value should persist if no context was removed. - REQUIRE(index.GetFirstContextId() == 2); - index.RemoveContext(2); - REQUIRE(index.GetFirstContextId() == 3); - index.RemoveContext(3); - - REQUIRE(index.IsEmpty()); - } -} - -std::vector GetBoolArgsFromCommand(Command command) -{ - const auto& commandArguments = command.GetArguments(); - std::vector boolArguments; - - for (const auto& argument : commandArguments) - { - if (argument.Type() == ArgumentType::Flag) - { - boolArguments.emplace_back(argument.Name()); - } - } - - return boolArguments; -} - -std::vector GetStringArgsFromCommand(Command command) -{ - const auto& commandArguments = command.GetArguments(); - std::vector stringArguments; - - for (const auto& argument : commandArguments) - { - if (argument.Type() != ArgumentType::Flag) - { - stringArguments.emplace_back(argument.Name()); - } - } - - return stringArguments; -} - -TEST_CASE("CheckpointIndex_ArgumentTable_VerifyAllArguments_InstallCommand", "[checkpointIndex]") -{ - TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; - INFO("Using temporary file named: " << tempFile.GetPath()); - - int testContextId = 0; - - InstallCommand installCommand({}); - std::vector boolArguments = GetBoolArgsFromCommand(installCommand); - std::vector stringArguments = GetStringArgsFromCommand(installCommand); - - { - CheckpointIndex index = CheckpointIndex::CreateNew(tempFile, { 1, 0 }); - index.AddContext(testContextId); - - for (const auto& argName : boolArguments) - { - REQUIRE_FALSE(index.ContainsArgument(testContextId, argName)); - REQUIRE(index.UpdateArgument(testContextId, argName, true)); - } - - std::string_view testValue = "testValue"sv; - for (const auto& argName : stringArguments) - { - REQUIRE_FALSE(index.ContainsArgument(testContextId, argName)); - REQUIRE(index.UpdateArgument(testContextId, argName, testValue)); - } - } - - { - CheckpointIndex index = CheckpointIndex::Open(tempFile, SQLiteStorageBase::OpenDisposition::ReadWrite); - - for (const auto& argName : boolArguments) - { - REQUIRE(index.ContainsArgument(testContextId, argName)); - REQUIRE(index.GetBoolArgument(testContextId, argName)); - } - - std::string_view testValue = "testValue"sv; - for (const auto& argName : stringArguments) - { - REQUIRE(index.ContainsArgument(testContextId, argName)); - REQUIRE(testValue == index.GetStringArgument(testContextId, argName)); - } - - index.RemoveContext(testContextId); REQUIRE(index.IsEmpty()); } } diff --git a/src/AppInstallerCLITests/ResumeFlow.cpp b/src/AppInstallerCLITests/ResumeFlow.cpp index 896f177687..92c209d081 100644 --- a/src/AppInstallerCLITests/ResumeFlow.cpp +++ b/src/AppInstallerCLITests/ResumeFlow.cpp @@ -17,16 +17,6 @@ using namespace AppInstaller::Repository::Microsoft; using namespace AppInstaller::Settings; using namespace TestCommon; -namespace -{ - // IMPORTANT: - // Since checkpoint manager is a static singleton class, the deconstructor is not called until after the tests are done. - // This will cause issues with the teardown process, as the temp directory will try to remove itself while the checkpoint manager still holds - // the handle to the index file. To resolve this, clean up for the Checkpoint Manager has been disabled and any tests that involve an index must call - // 'Checkpoint::CheckpointManager::Instance().ManualReset()'. - auto mockCheckpointManagerCleanUp = TestHook::MockCheckpointManagerCleanUp_Override(); -} - TEST_CASE("ResumeFlow_InvalidGuid", "[Resume]") { std::ostringstream resumeOutput; @@ -40,8 +30,8 @@ TEST_CASE("ResumeFlow_InvalidGuid", "[Resume]") resume.Execute(context); INFO(resumeOutput.str()); - REQUIRE(context.GetTerminationHR() == APPINSTALLER_CLI_ERROR_INVALID_RESUME_GUID); - auto expectedMessage = Resource::String::InvalidResumeGuidError(AppInstaller::Utility::LocIndString{ "badGuid"s }); + REQUIRE(context.GetTerminationHR() == APPINSTALLER_CLI_ERROR_INVALID_RESUME_ID); + auto expectedMessage = Resource::String::InvalidResumeIdError(AppInstaller::Utility::LocIndString{ "badGuid"s }); REQUIRE(resumeOutput.str().find(Resource::LocString(expectedMessage).get()) != std::string::npos); } @@ -59,8 +49,8 @@ TEST_CASE("ResumeFlow_IndexNotFound", "[Resume]") resume.Execute(context); INFO(resumeOutput.str()); - REQUIRE_TERMINATED_WITH(context, APPINSTALLER_CLI_ERROR_RESUME_GUID_NOT_FOUND); - auto expectedMessage = Resource::String::ResumeGuidNotFoundError(AppInstaller::Utility::LocIndString(tempGuidString)); + REQUIRE_TERMINATED_WITH(context, APPINSTALLER_CLI_ERROR_RESUME_ID_NOT_FOUND); + auto expectedMessage = Resource::String::ResumeIdNotFoundError(AppInstaller::Utility::LocIndString(tempGuidString)); REQUIRE(resumeOutput.str().find(Resource::LocString(expectedMessage).get()) != std::string::npos); } @@ -75,7 +65,7 @@ TEST_CASE("ResumeFlow_InvalidClientVersion", "[Resume]") std::string tempGuidString = "{b157d11f-4487-4e03-9447-9f9d50d66d8e}"; std::string tempFileName = tempGuidString + ".db"; auto tempIndexPath = tempCheckpointIndexDirectoryPath / tempFileName; - std::string invalidClientVersion{ "1.2.3.4 "}; + std::string_view invalidClientVersion = "1.2.3.4"sv; INFO("Using temporary file named: " << tempIndexPath); @@ -97,9 +87,6 @@ TEST_CASE("ResumeFlow_InvalidClientVersion", "[Resume]") REQUIRE_TERMINATED_WITH(context, APPINSTALLER_CLI_ERROR_CLIENT_VERSION_MISMATCH); auto expectedMessage = Resource::String::ClientVersionMismatchError(AppInstaller::Utility::LocIndString(invalidClientVersion)); REQUIRE(resumeOutput.str().find(Resource::LocString(expectedMessage).get()) != std::string::npos); - - // Manually reset index to allow for proper clean up. - Checkpoint::CheckpointManager::Instance().ManualReset(); } TEST_CASE("ResumeFlow_EmptyIndex", "Resume") @@ -133,12 +120,9 @@ TEST_CASE("ResumeFlow_EmptyIndex", "Resume") REQUIRE_TERMINATED_WITH(context, APPINSTALLER_CLI_ERROR_INVALID_RESUME_STATE); REQUIRE(resumeOutput.str().find(Resource::LocString(Resource::String::ResumeStateDataNotFoundError).get()) != std::string::npos); - - // Manually reset index to allow for proper clean up. - Checkpoint::CheckpointManager::Instance().ManualReset(); } -TEST_CASE("ResumeFlow_VerifyContextStateRemovedForInstallSuccess", "[Resume]") +TEST_CASE("ResumeFlow_InstallSuccess", "[Resume]") { TestCommon::TempDirectory tempCheckpointIndexDirectory("TempCheckpointIndexDirectory", false); @@ -150,18 +134,21 @@ TEST_CASE("ResumeFlow_VerifyContextStateRemovedForInstallSuccess", "[Resume]") TestCommon::TempFile installResultPath("TestExeInstalled.txt"); - std::ostringstream installOutput; - TestContext context{ installOutput, std::cin }; - auto previousThreadGlobals = context.SetForCurrentThread(); - OverrideForShellExecute(context); - - const auto& testManifestPath = TestDataFile("InstallFlowTest_Exe.yaml").GetPath().u8string(); - context.Args.AddArg(Execution::Args::Type::Manifest, testManifestPath); - - InstallCommand install({}); - context.SetExecutingCommand(&install); - install.Execute(context); - + { + std::ostringstream installOutput; + TestContext context{ installOutput, std::cin }; + auto previousThreadGlobals = context.SetForCurrentThread(); + OverrideForShellExecute(context); + + const auto& testManifestPath = TestDataFile("InstallFlowTest_Exe.yaml").GetPath().u8string(); + context.Args.AddArg(Execution::Args::Type::Manifest, testManifestPath); + + InstallCommand install({}); + context.SetExecutingCommand(&install); + install.Execute(context); + INFO(installOutput.str()); + } + // Verify Installer is called and parameters are passed in. REQUIRE(std::filesystem::exists(installResultPath.GetPath())); std::ifstream installResultFile(installResultPath.GetPath()); @@ -171,29 +158,18 @@ TEST_CASE("ResumeFlow_VerifyContextStateRemovedForInstallSuccess", "[Resume]") REQUIRE(installResultStr.find("/custom") != std::string::npos); REQUIRE(installResultStr.find("/silentwithprogress") != std::string::npos); - // Only one checkpoint file should be created. + // The checkpoint index should not exist if the context succeeded. std::vector checkpointFiles; for (const auto& entry : std::filesystem::directory_iterator(tempCheckpointIndexDirectoryPath)) { checkpointFiles.emplace_back(entry.path()); } - REQUIRE(checkpointFiles.size() == 1); - - std::filesystem::path checkpointIndexPath = checkpointFiles[0]; - REQUIRE(std::filesystem::exists(checkpointIndexPath)); - - { - CheckpointIndex index = CheckpointIndex::Open(checkpointIndexPath.u8string(), SQLiteStorageBase::OpenDisposition::ReadWrite); - REQUIRE(index.IsEmpty()); - } - - // Manually reset index to allow for proper index clean up. - Checkpoint::CheckpointManager::Instance().ManualReset(); + REQUIRE(checkpointFiles.size() == 0); } // TODO: This test will need to be updated once saving the resume state is restricted to certain HRs. -TEST_CASE("ResumeFlow_VerifyContextStateSavedForInstallFailure", "[Resume]") +TEST_CASE("ResumeFlow_InstallFailure", "[Resume]") { TestCommon::TempDirectory tempCheckpointIndexDirectory("TempCheckpointIndexDirectory", false); @@ -203,21 +179,23 @@ TEST_CASE("ResumeFlow_VerifyContextStateSavedForInstallFailure", "[Resume]") TestCommon::TestUserSettings testSettings; testSettings.Set(true); - std::ostringstream installOutput; - TestContext context{ installOutput, std::cin }; - auto previousThreadGlobals = context.SetForCurrentThread(); + { + std::ostringstream installOutput; + TestContext context{ installOutput, std::cin }; + auto previousThreadGlobals = context.SetForCurrentThread(); - const auto& testManifestPath = TestDataFile("InstallFlowTest_UnsupportedArguments.yaml").GetPath().u8string(); - context.Args.AddArg(Execution::Args::Type::Manifest, testManifestPath); - context.Args.AddArg(Execution::Args::Type::InstallLocation, "installLocation"sv); + const auto& testManifestPath = TestDataFile("InstallFlowTest_UnsupportedArguments.yaml").GetPath().u8string(); + context.Args.AddArg(Execution::Args::Type::Manifest, testManifestPath); + context.Args.AddArg(Execution::Args::Type::InstallLocation, "installLocation"sv); - InstallCommand install({}); - context.SetExecutingCommand(&install); - install.Execute(context); - INFO(installOutput.str()); + InstallCommand install({}); + context.SetExecutingCommand(&install); + install.Execute(context); + INFO(installOutput.str()); - // Verify unsupported arguments error message is shown - REQUIRE(context.GetTerminationHR() == APPINSTALLER_CLI_ERROR_UNSUPPORTED_ARGUMENT); + // Verify unsupported arguments error message is shown + REQUIRE(context.GetTerminationHR() == APPINSTALLER_CLI_ERROR_UNSUPPORTED_ARGUMENT); + } // Only one checkpoint file should be created. std::vector checkpointFiles; @@ -230,15 +208,4 @@ TEST_CASE("ResumeFlow_VerifyContextStateSavedForInstallFailure", "[Resume]") std::filesystem::path checkpointIndexPath = checkpointFiles[0]; REQUIRE(std::filesystem::exists(checkpointIndexPath)); - - { - CheckpointIndex index = CheckpointIndex::Open(checkpointIndexPath.u8string(), SQLiteStorageBase::OpenDisposition::ReadWrite); - REQUIRE_FALSE(index.IsEmpty()); - int contextId = index.GetFirstContextId(); - REQUIRE(index.GetStringArgument(contextId, "manifest"sv) == testManifestPath); - REQUIRE(index.GetStringArgument(contextId, "location"sv) == "installLocation"sv); - } - - // Manually reset index to allow for proper index clean up. - Checkpoint::CheckpointManager::Instance().ManualReset(); } diff --git a/src/AppInstallerCLITests/TestHooks.h b/src/AppInstallerCLITests/TestHooks.h index 82e611f430..16bf9698cc 100644 --- a/src/AppInstallerCLITests/TestHooks.h +++ b/src/AppInstallerCLITests/TestHooks.h @@ -73,11 +73,6 @@ namespace AppInstaller void TestHook_SetWindowsFeatureGetDisplayNameResult_Override(Utility::LocIndString* displayName); void TestHook_SetWindowsFeatureGetRestartStatusResult_Override(AppInstaller::WindowsFeature::DismRestartType* restartType); } - - namespace CLI::Checkpoint - { - void TestHook_MockCheckpointManagerCleanUp_Override(bool status); - } } namespace TestHook @@ -248,17 +243,4 @@ namespace TestHook private: std::vector m_extractedIcons; }; - - struct MockCheckpointManagerCleanUp_Override - { - MockCheckpointManagerCleanUp_Override() - { - AppInstaller::CLI::Checkpoint::TestHook_MockCheckpointManagerCleanUp_Override(true); - } - - ~MockCheckpointManagerCleanUp_Override() - { - AppInstaller::CLI::Checkpoint::TestHook_MockCheckpointManagerCleanUp_Override(false); - } - }; } \ No newline at end of file diff --git a/src/AppInstallerCommonCore/Public/AppInstallerRuntime.h b/src/AppInstallerCommonCore/Public/AppInstallerRuntime.h index 4d2a00ab39..3ce763fa28 100644 --- a/src/AppInstallerCommonCore/Public/AppInstallerRuntime.h +++ b/src/AppInstallerCommonCore/Public/AppInstallerRuntime.h @@ -49,7 +49,7 @@ namespace AppInstaller::Runtime SelfPackageRoot, // The location where user downloads are stored. UserProfileDownloads, - // The location where checkpoint database files are stored. + // Always one more than the last path; for being able to iterate paths in tests. Max }; diff --git a/src/AppInstallerRepositoryCore/Microsoft/CheckpointIndex.cpp b/src/AppInstallerRepositoryCore/Microsoft/CheckpointIndex.cpp index b0dc6f0d1f..629b6c9636 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/CheckpointIndex.cpp +++ b/src/AppInstallerRepositoryCore/Microsoft/CheckpointIndex.cpp @@ -169,9 +169,9 @@ namespace AppInstaller::Repository::Microsoft return rowId; } - std::map CheckpointIndex::GetContextData(std::string_view checkpointName, int contextData) + std::string CheckpointIndex::GetContextData(std::string_view checkpointName, int contextData, std::string_view name) { - return m_interface->GetContextData(m_dbconn, checkpointName, contextData); + return m_interface->GetContextData(m_dbconn, checkpointName, contextData, name); } void CheckpointIndex::RemoveContextData(std::string_view checkpointName, int contextData) diff --git a/src/AppInstallerRepositoryCore/Microsoft/CheckpointIndex.h b/src/AppInstallerRepositoryCore/Microsoft/CheckpointIndex.h index dc31f92646..7dea30ad4c 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/CheckpointIndex.h +++ b/src/AppInstallerRepositoryCore/Microsoft/CheckpointIndex.h @@ -50,7 +50,7 @@ namespace AppInstaller::Repository::Microsoft IdType AddContextData(std::string_view checkpointName, int contextData, std::string_view name, std::string_view value); - std::map GetContextData(std::string_view checkpointName, int contextData); + std::string GetContextData(std::string_view checkpointName, int contextData, std::string_view name); void RemoveContextData(std::string_view checkpointName, int contextData); diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointContextTable.cpp b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointContextTable.cpp index 8f0177fbfc..c8f01e4043 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointContextTable.cpp +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointContextTable.cpp @@ -65,24 +65,6 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 savepoint.Commit(); } - std::map CheckpointContextTable::GetContextData(const SQLite::Connection& connection, std::string_view checkpointName, int contextData) - { - SQLite::Builder::StatementBuilder builder; - builder.Select(SQLite::RowIDName).From(s_CheckpointContextTable_Table_Name).Where(s_CheckpointContextTable_CheckpointName_Column); - builder.Equals(checkpointName).And(s_CheckpointContextTable_ContextData_Column).Equals(contextData); - - SQLite::Statement select = builder.Prepare(connection); - - std::map contextDataMap; - while (select.Step()) - { - auto [rowCheckpointName, rowContextData, name, value] = select.GetRow(); - contextDataMap.emplace(name, value); - } - - return contextDataMap; - } - bool CheckpointContextTable::ExistsById(const SQLite::Connection& connection, SQLite::rowid_t id) { SQLite::Builder::StatementBuilder builder; @@ -142,8 +124,8 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 void CheckpointContextTable::RemoveContextData(SQLite::Connection& connection, std::string_view checkpointName, int contextData) { SQLite::Builder::StatementBuilder builder; - builder.Select(SQLite::RowIDName).From(s_CheckpointContextTable_Table_Name).Where(s_CheckpointContextTable_CheckpointName_Column).Equals(checkpointName); - builder.Where(s_CheckpointContextTable_ContextData_Column).Equals(contextData); + builder.Select(SQLite::RowIDName).From(s_CheckpointContextTable_Table_Name).Where(s_CheckpointContextTable_CheckpointName_Column).Equals(checkpointName) + .And(s_CheckpointContextTable_ContextData_Column).Equals(contextData); SQLite::Statement select = builder.Prepare(connection); while (select.Step()) @@ -151,4 +133,22 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 DeleteById(connection, select.GetColumn(0)); } } + + std::string CheckpointContextTable::GetContextData(SQLite::Connection& connection, std::string_view checkpointName, int contextData, std::string_view name) + { + SQLite::Builder::StatementBuilder builder; + builder.Select(s_CheckpointContextTable_Value_Column).From(s_CheckpointContextTable_Table_Name).Where(s_CheckpointContextTable_CheckpointName_Column); + builder.Equals(checkpointName).And(s_CheckpointContextTable_ContextData_Column).Equals(contextData).And(s_CheckpointContextTable_Name_Column).Equals(name); + + SQLite::Statement select = builder.Prepare(connection); + + if (select.Step()) + { + return select.GetColumn(0); + } + else + { + return {}; + } + } } \ No newline at end of file diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointContextTable.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointContextTable.h index 5b441397b9..146977ee46 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointContextTable.h +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointContextTable.h @@ -25,14 +25,13 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 // Gets a value indicating whether the table is empty. static bool IsEmpty(SQLite::Connection& connection); - // Selects the context data by checkpoint name from the table, returning the rowid if it exists. - static std::map GetContextData(const SQLite::Connection& connection, std::string_view checkpointName, int contextData); - // Adds a checkpoint row. static SQLite::rowid_t AddCheckpoint(SQLite::Connection& connection, std::string_view checkpointName); static SQLite::rowid_t AddContextData(SQLite::Connection& connection, std::string_view checkpointName, int contextData, std::string_view name, std::string_view value); + static std::string GetContextData(SQLite::Connection& connection, std::string_view checkpointName, int contextData, std::string_view name); + static void RemoveContextData(SQLite::Connection& connection, std::string_view checkpointName, int contextData); }; } \ No newline at end of file diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointIndexInterface.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointIndexInterface.h index 53d6cea2a5..c35ebf701a 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointIndexInterface.h +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointIndexInterface.h @@ -20,7 +20,7 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 SQLite::rowid_t SetCommandArguments(SQLite::Connection& connection, std::string_view commandArguments) override; std::string GetCommandArguments(SQLite::Connection& connection) override; SQLite::rowid_t AddContextData(SQLite::Connection& connection, std::string_view checkpointName, int contextData, std::string_view name, std::string_view value) override; - std::map GetContextData(SQLite::Connection& connection, std::string_view checkpointName, int contextData) override; + std::string GetContextData(SQLite::Connection& connection, std::string_view checkpointName, int contextData, std::string_view name) override; void RemoveContextData(SQLite::Connection& connection, std::string_view checkpointName, int contextData) override; }; diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointIndexInterface_1_0.cpp b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointIndexInterface_1_0.cpp index 0fb4794032..842865aa7e 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointIndexInterface_1_0.cpp +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointIndexInterface_1_0.cpp @@ -72,9 +72,9 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 return rowId; } - std::map CheckpointIndexInterface::GetContextData(SQLite::Connection& connection, std::string_view checkpointName, int contextData) + std::string CheckpointIndexInterface::GetContextData(SQLite::Connection& connection, std::string_view checkpointName, int contextData, std::string_view name) { - return CheckpointContextTable::GetContextData(connection, checkpointName, contextData); + return CheckpointContextTable::GetContextData(connection, checkpointName, contextData, name); } void CheckpointIndexInterface::RemoveContextData(SQLite::Connection& connection, std::string_view checkpointName, int contextData) diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/ICheckpointIndex.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/ICheckpointIndex.h index 36c987f7f6..5e4755ffc0 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/Schema/ICheckpointIndex.h +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/ICheckpointIndex.h @@ -41,7 +41,8 @@ namespace AppInstaller::Repository::Microsoft::Schema // Adds the context data property for a given checkpoint. virtual SQLite::rowid_t AddContextData(SQLite::Connection& connection, std::string_view checkpointName, int contextData, std::string_view name, std::string_view value) = 0; - virtual std::map GetContextData(SQLite::Connection& connection, std::string_view checkpointName, int contextData) = 0; + // Gets the context data property for a given checkpoint. + virtual std::string GetContextData(SQLite::Connection& connection, std::string_view checkpointName, int contextData, std::string_view name) = 0; // Removes the context data for a given checkpoint. virtual void RemoveContextData(SQLite::Connection& connection, std::string_view checkpointName, int contextData) = 0; diff --git a/src/AppInstallerSharedLib/Errors.cpp b/src/AppInstallerSharedLib/Errors.cpp index 29aa7422df..7b5172f5e8 100644 --- a/src/AppInstallerSharedLib/Errors.cpp +++ b/src/AppInstallerSharedLib/Errors.cpp @@ -230,14 +230,16 @@ namespace AppInstaller return "Failed to download package dependencies."; case APPINSTALLER_CLI_ERROR_DOWNLOAD_COMMAND_PROHIBITED: return "Failed to download package. Download for offline installation is prohibited."; - case APPINSTALLER_CLI_ERROR_INVALID_RESUME_GUID: + case APPINSTALLER_CLI_ERROR_INVALID_RESUME_ID: return "Argument for resume guid is invalid."; - case APPINSTALLER_CLI_ERROR_RESUME_GUID_NOT_FOUND: + case APPINSTALLER_CLI_ERROR_RESUME_ID_NOT_FOUND: return "The guid provided does not correspond to a valid resume state."; case APPINSTALLER_CLI_ERROR_CLIENT_VERSION_MISMATCH: return "The current client version did not match the client version of the saved state."; case APPINSTALLER_CLI_ERROR_INVALID_RESUME_STATE: return "The resume state data is invalid."; + case APPINSTALLER_CLI_ERROR_CANNOT_OPEN_CHECKPOINT_INDEX: + return "Unable to open the checkpoint database."; // Install errors case APPINSTALLER_CLI_ERROR_INSTALL_PACKAGE_IN_USE: diff --git a/src/AppInstallerSharedLib/Public/AppInstallerErrors.h b/src/AppInstallerSharedLib/Public/AppInstallerErrors.h index 5b5c877b57..67ddbd0dd5 100644 --- a/src/AppInstallerSharedLib/Public/AppInstallerErrors.h +++ b/src/AppInstallerSharedLib/Public/AppInstallerErrors.h @@ -121,10 +121,11 @@ #define APPINSTALLER_CLI_ERROR_APPTERMINATION_RECEIVED ((HRESULT)0x8A15006A) #define APPINSTALLER_CLI_ERROR_DOWNLOAD_DEPENDENCIES ((HRESULT)0x8A15006B) #define APPINSTALLER_CLI_ERROR_DOWNLOAD_COMMAND_PROHIBITED ((HRESULT)0x8A15006C) -#define APPINSTALLER_CLI_ERROR_INVALID_RESUME_GUID ((HRESULT)0x8A15006D) -#define APPINSTALLER_CLI_ERROR_RESUME_GUID_NOT_FOUND ((HRESULT)0x8A15006E) +#define APPINSTALLER_CLI_ERROR_INVALID_RESUME_ID ((HRESULT)0x8A15006D) +#define APPINSTALLER_CLI_ERROR_RESUME_ID_NOT_FOUND ((HRESULT)0x8A15006E) #define APPINSTALLER_CLI_ERROR_CLIENT_VERSION_MISMATCH ((HRESULT)0x8A15006F) #define APPINSTALLER_CLI_ERROR_INVALID_RESUME_STATE ((HRESULT)0x8A150070) +#define APPINSTALLER_CLI_ERROR_CANNOT_OPEN_CHECKPOINT_INDEX ((HRESULT)0x8A150071) // Install errors. #define APPINSTALLER_CLI_ERROR_INSTALL_PACKAGE_IN_USE ((HRESULT)0x8A150101) From a757d8c79254650e4e1482a93ba09b62d75d7d46 Mon Sep 17 00:00:00 2001 From: Ryan Fu Date: Tue, 15 Aug 2023 15:39:34 -0700 Subject: [PATCH 18/43] fix spelling --- .github/actions/spelling/allow.txt | 7 ++----- src/AppInstallerCLICore/ExecutionContext.cpp | 9 ++++++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/actions/spelling/allow.txt b/.github/actions/spelling/allow.txt index 76c69be7e9..44b42524e3 100644 --- a/.github/actions/spelling/allow.txt +++ b/.github/actions/spelling/allow.txt @@ -3,7 +3,7 @@ ACCESSDENIED ACTIONDATA ACTIONSTART activatable -addcontext +addcontextdata addfile addmanifest addpin @@ -117,7 +117,6 @@ debugbreak declspec decltype declval -deconstructor defaultlocale delstore Demitrius @@ -496,6 +495,7 @@ selectany SERVICEPACKMAJOR SERVICEPACKMINOR setclientversion +setcommandarguments setcommandname setfill setschemaversion @@ -641,13 +641,10 @@ Unmarshal Unregister Unregisters untimes -updateboolargument updatefile -updatelastcheckpointflag updatemanifest updatepin updateportablefile -updatestringargument UPLEVEL upvote uregex diff --git a/src/AppInstallerCLICore/ExecutionContext.cpp b/src/AppInstallerCLICore/ExecutionContext.cpp index 1c5a8aeb20..ecc011d2a2 100644 --- a/src/AppInstallerCLICore/ExecutionContext.cpp +++ b/src/AppInstallerCLICore/ExecutionContext.cpp @@ -252,10 +252,13 @@ namespace AppInstaller::CLI::Execution Context::~Context() { - // TODO: Update so that only certain terminated HRs allow the checkpoint index to persist. - if (!m_isTerminated) + if (Settings::ExperimentalFeature::IsEnabled(ExperimentalFeature::Feature::Resume)) { - m_checkpointManager->CleanUpIndex(); + // TODO: Update so that only certain terminated HRs allow the checkpoint index to persist. + if (!m_isTerminated && m_checkpointManager != nullptr) + { + m_checkpointManager->CleanUpIndex(); + } } if (m_disableSignalTerminationHandlerOnExit) From 3cf3fb7ef9a1032b72846a9bf1e73c0b0d4feebb Mon Sep 17 00:00:00 2001 From: Ryan Fu Date: Wed, 16 Aug 2023 09:25:57 -0700 Subject: [PATCH 19/43] remove EF check --- src/AppInstallerCLICore/ExecutionContext.cpp | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/AppInstallerCLICore/ExecutionContext.cpp b/src/AppInstallerCLICore/ExecutionContext.cpp index ecc011d2a2..2f91170e1c 100644 --- a/src/AppInstallerCLICore/ExecutionContext.cpp +++ b/src/AppInstallerCLICore/ExecutionContext.cpp @@ -252,13 +252,10 @@ namespace AppInstaller::CLI::Execution Context::~Context() { - if (Settings::ExperimentalFeature::IsEnabled(ExperimentalFeature::Feature::Resume)) + // TODO: Update so that only certain terminated HRs allow the checkpoint index to persist. + if (!m_isTerminated && m_checkpointManager != nullptr) { - // TODO: Update so that only certain terminated HRs allow the checkpoint index to persist. - if (!m_isTerminated && m_checkpointManager != nullptr) - { - m_checkpointManager->CleanUpIndex(); - } + m_checkpointManager->CleanUpIndex(); } if (m_disableSignalTerminationHandlerOnExit) From ed47dd16f0b7541fc91a5fa4df278e99431ea3fd Mon Sep 17 00:00:00 2001 From: Ryan Fu Date: Wed, 16 Aug 2023 11:20:32 -0700 Subject: [PATCH 20/43] try again --- src/AppInstallerCLICore/Argument.cpp | 2 +- src/AppInstallerCLICore/CheckpointManager.cpp | 15 +++++++++++---- src/AppInstallerCLICore/Command.cpp | 8 +++++++- .../Commands/ResumeCommand.cpp | 2 +- src/AppInstallerCLICore/ExecutionContext.cpp | 14 ++++++++------ src/AppInstallerCLICore/ExecutionContext.h | 3 +++ 6 files changed, 31 insertions(+), 13 deletions(-) diff --git a/src/AppInstallerCLICore/Argument.cpp b/src/AppInstallerCLICore/Argument.cpp index 4de77d3a31..13122141cb 100644 --- a/src/AppInstallerCLICore/Argument.cpp +++ b/src/AppInstallerCLICore/Argument.cpp @@ -176,7 +176,7 @@ namespace AppInstaller::CLI // Resume command case Execution::Args::Type::ResumeId: - return { type, "resume-guid"_liv, 'g', ArgTypeCategory::None }; + return { type, "resume-id"_liv, 'g', ArgTypeCategory::None }; // Configuration commands case Execution::Args::Type::ConfigurationFile: diff --git a/src/AppInstallerCLICore/CheckpointManager.cpp b/src/AppInstallerCLICore/CheckpointManager.cpp index 88eb0cea81..d88e1bbe51 100644 --- a/src/AppInstallerCLICore/CheckpointManager.cpp +++ b/src/AppInstallerCLICore/CheckpointManager.cpp @@ -61,15 +61,22 @@ namespace AppInstaller::CLI::Checkpoint void CheckpointManager::CleanUpIndex() { - if (m_checkpointIndex != nullptr) + if (m_checkpointIndex) { m_checkpointIndex.reset(); + } + + if (m_checkpointId != GUID_NULL) + { const auto& checkpointIndexPath = CheckpointIndex::GetCheckpointIndexPath(m_checkpointId); - std::error_code error; - if (std::filesystem::remove(checkpointIndexPath, error)) + if (std::filesystem::exists(checkpointIndexPath)) { - AICLI_LOG(CLI, Info, << "Checkpoint index deleted: " << checkpointIndexPath); + std::error_code error; + if (std::filesystem::remove(checkpointIndexPath, error)) + { + AICLI_LOG(CLI, Info, << "Checkpoint index deleted: " << checkpointIndexPath); + } } } } diff --git a/src/AppInstallerCLICore/Command.cpp b/src/AppInstallerCLICore/Command.cpp index 5383a01862..b26e8b3ecd 100644 --- a/src/AppInstallerCLICore/Command.cpp +++ b/src/AppInstallerCLICore/Command.cpp @@ -853,7 +853,8 @@ namespace AppInstaller::CLI throw GroupPolicyException(Settings::TogglePolicy::Policy::WinGet); } - if (Settings::ExperimentalFeature::IsEnabled(ExperimentalFeature::Feature::Resume)) + bool isResumeFeatureEnabled = Settings::ExperimentalFeature::IsEnabled(ExperimentalFeature::Feature::Resume); + if (isResumeFeatureEnabled) { if (!context.Args.Contains(Execution::Args::Type::ResumeId)) { @@ -873,6 +874,11 @@ namespace AppInstaller::CLI ExecuteInternal(context); } + if (isResumeFeatureEnabled && !context.IsTerminated()) + { + context.CleanUpCheckpoints(); + } + if (context.Args.Contains(Execution::Args::Type::OpenLogs)) { // TODO: Consider possibly adding functionality that if the context contains 'Execution::Args::Type::Log' to open the path provided for the log diff --git a/src/AppInstallerCLICore/Commands/ResumeCommand.cpp b/src/AppInstallerCLICore/Commands/ResumeCommand.cpp index 0b6d531fb5..c01b95815f 100644 --- a/src/AppInstallerCLICore/Commands/ResumeCommand.cpp +++ b/src/AppInstallerCLICore/Commands/ResumeCommand.cpp @@ -52,7 +52,6 @@ namespace AppInstaller::CLI AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_RESUME_ID_NOT_FOUND); } - Execution::Context resumeContext{ std::cout, std::cin }; auto previousThreadGlobals = resumeContext.SetForCurrentThread(); resumeContext.EnableSignalTerminationHandler(); @@ -87,5 +86,6 @@ namespace AppInstaller::CLI resumeContext.SetExecutingCommand(commandToResume.get()); resumeContext.SetFlags(Execution::ContextFlag::Resume); commandToResume->Resume(resumeContext); + context.SetTerminationHR(resumeContext.GetTerminationHR()); } } diff --git a/src/AppInstallerCLICore/ExecutionContext.cpp b/src/AppInstallerCLICore/ExecutionContext.cpp index 2f91170e1c..4ad49e28a2 100644 --- a/src/AppInstallerCLICore/ExecutionContext.cpp +++ b/src/AppInstallerCLICore/ExecutionContext.cpp @@ -252,12 +252,6 @@ namespace AppInstaller::CLI::Execution Context::~Context() { - // TODO: Update so that only certain terminated HRs allow the checkpoint index to persist. - if (!m_isTerminated && m_checkpointManager != nullptr) - { - m_checkpointManager->CleanUpIndex(); - } - if (m_disableSignalTerminationHandlerOnExit) { EnableSignalTerminationHandler(false); @@ -460,4 +454,12 @@ namespace AppInstaller::CLI::Execution { return m_checkpointManager->GetClientVersion(); } + + void Context::CleanUpCheckpoints() + { + if (m_checkpointManager) + { + m_checkpointManager->CleanUpIndex(); + } + } } diff --git a/src/AppInstallerCLICore/ExecutionContext.h b/src/AppInstallerCLICore/ExecutionContext.h index d882cdd4f7..b16994fdfa 100644 --- a/src/AppInstallerCLICore/ExecutionContext.h +++ b/src/AppInstallerCLICore/ExecutionContext.h @@ -183,6 +183,9 @@ namespace AppInstaller::CLI::Execution // Gets the client version from the checkpoint index. std::string GetClientVersionFromCheckpoint(); + // Removes the checkpoint index file. + void CleanUpCheckpoints(); + protected: // Copies the args that are also needed in a sub-context. E.g., silent void CopyArgsToSubContext(Context* subContext); From 3bca5d449759c8b632ca435a5d5948cb6c400390 Mon Sep 17 00:00:00 2001 From: Ryan Fu Date: Wed, 16 Aug 2023 14:40:49 -0700 Subject: [PATCH 21/43] fix client Version --- src/AppInstallerCLICore/Command.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/AppInstallerCLICore/Command.cpp b/src/AppInstallerCLICore/Command.cpp index b26e8b3ecd..7fe537c43a 100644 --- a/src/AppInstallerCLICore/Command.cpp +++ b/src/AppInstallerCLICore/Command.cpp @@ -858,7 +858,7 @@ namespace AppInstaller::CLI { if (!context.Args.Contains(Execution::Args::Type::ResumeId)) { - const auto& clientVersion = AppInstaller::Runtime::GetClientVersion().get(); + const auto& clientVersion = AppInstaller::Runtime::GetClientVersion(); const auto& commandLineString = context.GetCommandLineFromArgs(); context.InitializeCheckpointManager(Name(), commandLineString, clientVersion); } From 525bf1c6577a01f1b886829e342ff9e948aefca0 Mon Sep 17 00:00:00 2001 From: Ryan Fu Date: Fri, 18 Aug 2023 13:51:31 -0700 Subject: [PATCH 22/43] add table --- src/AppInstallerCLICore/CheckpointManager.cpp | 84 +++---- src/AppInstallerCLICore/CheckpointManager.h | 26 ++- src/AppInstallerCLICore/Command.cpp | 14 +- .../Commands/InstallCommand.cpp | 7 +- .../Commands/ResumeCommand.cpp | 46 ++-- .../Commands/ResumeCommand.h | 1 + src/AppInstallerCLICore/ExecutionContext.cpp | 71 +++--- src/AppInstallerCLICore/ExecutionContext.h | 24 +- src/AppInstallerCLICore/Resources.h | 1 + .../Workflows/ResumeFlow.h | 10 +- src/AppInstallerCLIE2ETests/Constants.cs | 9 +- .../Shared/Strings/en-us/winget.resw | 3 + src/AppInstallerCLITests/ResumeFlow.cpp | 4 +- .../Public/AppInstallerRuntime.h | 2 + src/AppInstallerCommonCore/Runtime.cpp | 5 + .../AppInstallerRepositoryCore.vcxproj | 12 +- ...AppInstallerRepositoryCore.vcxproj.filters | 16 +- .../Microsoft/CheckpointIndex.cpp | 212 ------------------ .../Microsoft/CheckpointIndex.h | 69 ------ .../Microsoft/CheckpointRecord.cpp | 196 ++++++++++++++++ .../Microsoft/CheckpointRecord.h | 72 ++++++ .../Checkpoint_1_0/CheckpointContextTable.cpp | 82 +++---- .../Checkpoint_1_0/CheckpointContextTable.h | 19 +- .../Checkpoint_1_0/CheckpointIndexInterface.h | 27 --- .../CheckpointIndexInterface_1_0.cpp | 86 ------- .../CheckpointMetadataTable.cpp | 68 ++---- .../Checkpoint_1_0/CheckpointMetadataTable.h | 22 +- .../CheckpointRecordInterface.h | 26 +++ .../CheckpointRecordInterface_1_0.cpp | 76 +++++++ .../Schema/Checkpoint_1_0/CheckpointTable.cpp | 63 ++++++ .../Schema/Checkpoint_1_0/CheckpointTable.h | 25 +++ .../Microsoft/Schema/ICheckpointIndex.h | 50 ----- .../Microsoft/Schema/ICheckpointRecord.h | 42 ++++ src/AppInstallerSharedLib/Errors.cpp | 2 - .../Public/AppInstallerErrors.h | 9 +- 35 files changed, 726 insertions(+), 755 deletions(-) delete mode 100644 src/AppInstallerRepositoryCore/Microsoft/CheckpointIndex.cpp delete mode 100644 src/AppInstallerRepositoryCore/Microsoft/CheckpointIndex.h create mode 100644 src/AppInstallerRepositoryCore/Microsoft/CheckpointRecord.cpp create mode 100644 src/AppInstallerRepositoryCore/Microsoft/CheckpointRecord.h delete mode 100644 src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointIndexInterface.h delete mode 100644 src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointIndexInterface_1_0.cpp create mode 100644 src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointRecordInterface.h create mode 100644 src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointRecordInterface_1_0.cpp create mode 100644 src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointTable.cpp create mode 100644 src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointTable.h delete mode 100644 src/AppInstallerRepositoryCore/Microsoft/Schema/ICheckpointIndex.h create mode 100644 src/AppInstallerRepositoryCore/Microsoft/Schema/ICheckpointRecord.h diff --git a/src/AppInstallerCLICore/CheckpointManager.cpp b/src/AppInstallerCLICore/CheckpointManager.cpp index d88e1bbe51..931ceff8cb 100644 --- a/src/AppInstallerCLICore/CheckpointManager.cpp +++ b/src/AppInstallerCLICore/CheckpointManager.cpp @@ -2,80 +2,86 @@ // Licensed under the MIT License. #include "pch.h" #include "CheckpointManager.h" -#include "Microsoft/CheckpointIndex.h" -#include "Microsoft/SQLiteStorageBase.h" -#include +#include "Microsoft/CheckpointRecord.h" namespace AppInstaller::CLI::Checkpoint { using namespace AppInstaller::Repository::Microsoft; - CheckpointManager::CheckpointManager(GUID id) + void CheckpointManager::CreateRecord() { - m_checkpointId = id; - AICLI_LOG(CLI, Info, << "Opening checkpoint index with id: " << m_checkpointId); - auto openDisposition = SQLiteStorageBase::OpenDisposition::ReadWrite; - auto checkpointIndex = CheckpointIndex::OpenOrCreateDefault(m_checkpointId, openDisposition); - if (!checkpointIndex) - { - AICLI_LOG(CLI, Error, << "Unable to open checkpoint index."); - THROW_HR_MSG(APPINSTALLER_CLI_ERROR_CANNOT_OPEN_CHECKPOINT_INDEX, "The checkpoint index could not be opened."); - } - - m_checkpointIndex = std::move(checkpointIndex); + THROW_HR_IF(E_UNEXPECTED, IsLoaded()); + std::ignore = CoCreateGuid(&m_checkpointId); + AICLI_LOG(CLI, Info, << "Creating checkpoint index with id: " << m_checkpointId); + m_checkpointRecord = CheckpointRecord::CreateDefault(m_checkpointId); } - CheckpointManager::CheckpointManager(std::string_view commandName, std::string_view commandArguments, std::string_view clientVersion) + CheckpointManager::CheckpointManager() { - std::ignore = CoCreateGuid(&m_checkpointId); - - AICLI_LOG(CLI, Info, << "Creating checkpoint index with id: " << m_checkpointId); - auto openDisposition = SQLiteStorageBase::OpenDisposition::ReadWrite; - auto checkpointIndex = CheckpointIndex::OpenOrCreateDefault(m_checkpointId, openDisposition); - if (!checkpointIndex) - { - AICLI_LOG(CLI, Error, << "Unable to open checkpoint index."); - return; - } + m_checkpointId = {}; + } - m_checkpointIndex = std::move(checkpointIndex); - m_checkpointIndex->SetCommandName(commandName); - m_checkpointIndex->SetCommandArguments(commandArguments); - m_checkpointIndex->SetClientVersion(clientVersion); + void CheckpointManager::LoadExistingRecord(GUID id) + { + m_checkpointId = id; + AICLI_LOG(CLI, Info, << "Opening checkpoint index with id: " << m_checkpointId); + m_checkpointRecord = CheckpointRecord::OpenDefault(m_checkpointId); } std::string CheckpointManager::GetClientVersion() { - return m_checkpointIndex->GetClientVersion(); + THROW_HR_IF(E_UNEXPECTED, !IsLoaded()); + return m_checkpointRecord->GetMetadata(CheckpointMetadata::ClientVersion); } std::string CheckpointManager::GetCommandName() { - return m_checkpointIndex->GetCommandName(); + THROW_HR_IF(E_UNEXPECTED, !IsLoaded()); + return m_checkpointRecord->GetMetadata(CheckpointMetadata::CommandName); } std::string CheckpointManager::GetArguments() { - return m_checkpointIndex->GetCommandArguments(); + return {}; + } + + void CheckpointManager::SetClientVersion(std::string_view value) + { + m_checkpointRecord->SetMetadata(CheckpointMetadata::ClientVersion, value); + } + + void CheckpointManager::SetCommandName(std::string_view value) + { + m_checkpointRecord->SetMetadata(CheckpointMetadata::CommandName, value); + } + + void CheckpointManager::AddContextData(std::string_view checkpointName, int contextData, std::string_view name, std::string_view value, int index) + { + const auto& checkpointId = m_checkpointRecord->GetCheckpointId(checkpointName); + + if (checkpointId) + { + m_checkpointRecord->AddContextData(checkpointId.value(), contextData, name, value, index); + } } void CheckpointManager::CleanUpIndex() { - if (m_checkpointIndex) + if (m_checkpointRecord) { - m_checkpointIndex.reset(); + m_checkpointRecord.reset(); } if (m_checkpointId != GUID_NULL) { - const auto& checkpointIndexPath = CheckpointIndex::GetCheckpointIndexPath(m_checkpointId); + const auto& checkpointRecordPath = CheckpointRecord::GetCheckpointRecordPath(m_checkpointId); - if (std::filesystem::exists(checkpointIndexPath)) + if (std::filesystem::exists(checkpointRecordPath)) { std::error_code error; - if (std::filesystem::remove(checkpointIndexPath, error)) + if (std::filesystem::remove(checkpointRecordPath, error)) { - AICLI_LOG(CLI, Info, << "Checkpoint index deleted: " << checkpointIndexPath); + AICLI_LOG(CLI, Info, << "Checkpoint index deleted: " << checkpointRecordPath); } } } diff --git a/src/AppInstallerCLICore/CheckpointManager.h b/src/AppInstallerCLICore/CheckpointManager.h index df368fd0b7..650c8469a3 100644 --- a/src/AppInstallerCLICore/CheckpointManager.h +++ b/src/AppInstallerCLICore/CheckpointManager.h @@ -6,19 +6,22 @@ namespace AppInstaller::Repository::Microsoft { - struct CheckpointIndex; + struct CheckpointRecord; } namespace AppInstaller::CLI::Checkpoint { struct CheckpointManager { - // Constructor for an existing resume id. - CheckpointManager(GUID id); + CheckpointManager(); + + bool IsLoaded() { return m_checkpointRecord ? true: false; }; + + void CreateRecord(); + + // Loads an existing record from an id. + void LoadExistingRecord(GUID id); - // Constructor for a new resume save state. - CheckpointManager(std::string_view commandName, std::string_view commandArguments, std::string_view clientVersion); - // Gets the client version from the checkpoint index. std::string GetClientVersion(); @@ -28,11 +31,18 @@ namespace AppInstaller::CLI::Checkpoint // Gets the command arguments from the checkpoint index. std::string GetArguments(); + void SetClientVersion(std::string_view value); + + void SetCommandName(std::string_view value); + + // Adds a context data to the checkpoint record. + void AddContextData(std::string_view checkpointName, int contextData, std::string_view name, std::string_view value, int index); + // Releases and deletes the checkpoint index. void CleanUpIndex(); private: GUID m_checkpointId; - std::shared_ptr m_checkpointIndex; + std::shared_ptr m_checkpointRecord; }; -} +} \ No newline at end of file diff --git a/src/AppInstallerCLICore/Command.cpp b/src/AppInstallerCLICore/Command.cpp index 7fe537c43a..49085470a6 100644 --- a/src/AppInstallerCLICore/Command.cpp +++ b/src/AppInstallerCLICore/Command.cpp @@ -853,14 +853,11 @@ namespace AppInstaller::CLI throw GroupPolicyException(Settings::TogglePolicy::Policy::WinGet); } - bool isResumeFeatureEnabled = Settings::ExperimentalFeature::IsEnabled(ExperimentalFeature::Feature::Resume); - if (isResumeFeatureEnabled) + if (Settings::ExperimentalFeature::IsEnabled(ExperimentalFeature::Feature::Resume)) { if (!context.Args.Contains(Execution::Args::Type::ResumeId)) { - const auto& clientVersion = AppInstaller::Runtime::GetClientVersion(); - const auto& commandLineString = context.GetCommandLineFromArgs(); - context.InitializeCheckpointManager(Name(), commandLineString, clientVersion); + context.Checkpoint("Start"sv, {}); } } @@ -874,11 +871,6 @@ namespace AppInstaller::CLI ExecuteInternal(context); } - if (isResumeFeatureEnabled && !context.IsTerminated()) - { - context.CleanUpCheckpoints(); - } - if (context.Args.Contains(Execution::Args::Type::OpenLogs)) { // TODO: Consider possibly adding functionality that if the context contains 'Execution::Args::Type::Log' to open the path provided for the log @@ -894,7 +886,7 @@ namespace AppInstaller::CLI void Command::Resume(Execution::Context& context) const { - context.Reporter.Error() << Resource::String::PendingWorkError << std::endl; + context.Reporter.Error() << Resource::String::CommandDoesNotSupportResumeMessage << std::endl; THROW_HR(E_NOTIMPL); } diff --git a/src/AppInstallerCLICore/Commands/InstallCommand.cpp b/src/AppInstallerCLICore/Commands/InstallCommand.cpp index d67b973afa..a5e73a94e5 100644 --- a/src/AppInstallerCLICore/Commands/InstallCommand.cpp +++ b/src/AppInstallerCLICore/Commands/InstallCommand.cpp @@ -106,9 +106,10 @@ namespace AppInstaller::CLI void InstallCommand::Resume(Context& context) const { - auto commandLineArgs = context.GetArgsFromCheckpoint(); - Invocation invocation{ std::move(commandLineArgs) }; - ParseArguments(invocation, context.Args); + // Move this to the resume command to load arguments. + //auto commandLine = context.CheckpointManager.GetArguments(); + //Invocation invocation{ std::move(commandLine) }; + //ParseArguments(invocation, context.Args); context.SetFlags(Execution::ContextFlag::Resume); ExecuteInternal(context); diff --git a/src/AppInstallerCLICore/Commands/ResumeCommand.cpp b/src/AppInstallerCLICore/Commands/ResumeCommand.cpp index c01b95815f..70ae997c64 100644 --- a/src/AppInstallerCLICore/Commands/ResumeCommand.cpp +++ b/src/AppInstallerCLICore/Commands/ResumeCommand.cpp @@ -5,13 +5,15 @@ #include "Resources.h" #include "ResumeCommand.h" #include "RootCommand.h" -#include "Microsoft/CheckpointIndex.h" +#include "CheckpointManager.h" +#include "Microsoft/CheckpointRecord.h" #include "Workflows/ResumeFlow.h" namespace AppInstaller::CLI { using namespace std::string_view_literals; using namespace Execution; + using namespace Checkpoint; std::vector ResumeCommand::GetArguments() const { @@ -37,44 +39,36 @@ namespace AppInstaller::CLI void ResumeCommand::ExecuteInternal(Execution::Context& context) const { - std::string resumeGuidString { context.Args.GetArg(Execution::Args::Type::ResumeId) }; - if (!Utility::IsValidGuidString(resumeGuidString)) - { - context.Reporter.Error() << Resource::String::InvalidResumeIdError(Utility::LocIndView{ resumeGuidString }) << std::endl; - AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_INVALID_RESUME_ID); - } - + std::string resumeGuidString{ context.Args.GetArg(Execution::Args::Type::ResumeId) }; GUID checkpointId = Utility::ConvertToGuid(resumeGuidString); - if (!std::filesystem::exists(AppInstaller::Repository::Microsoft::CheckpointIndex::GetCheckpointIndexPath(checkpointId))) + if (!std::filesystem::exists(AppInstaller::Repository::Microsoft::CheckpointRecord::GetCheckpointRecordPath(checkpointId))) { context.Reporter.Error() << Resource::String::ResumeIdNotFoundError(Utility::LocIndView{ resumeGuidString }) << std::endl; AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_RESUME_ID_NOT_FOUND); } - Execution::Context resumeContext{ std::cout, std::cin }; - auto previousThreadGlobals = resumeContext.SetForCurrentThread(); - resumeContext.EnableSignalTerminationHandler(); - resumeContext.InitializeCheckpointManager(checkpointId); + Execution::Context resumeContext = context.CreateEmptyContext(); + + CheckpointManager checkpointManager = resumeContext.CheckpointManager; + checkpointManager.LoadExistingRecord(checkpointId); - const auto& checkpointClientVersion = resumeContext.GetClientVersionFromCheckpoint(); + + const auto& checkpointClientVersion = checkpointManager.GetClientVersion(); if (checkpointClientVersion != AppInstaller::Runtime::GetClientVersion().get()) { context.Reporter.Error() << Resource::String::ClientVersionMismatchError(Utility::LocIndView{ checkpointClientVersion }) << std::endl; AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_CLIENT_VERSION_MISMATCH); } - std::string commandName = resumeContext.GetCommandNameFromCheckpoint(); + std::string commandName = checkpointManager.GetCommandName(); std::unique_ptr commandToResume; // Find the command using the root command. AICLI_LOG(CLI, Info, << "Resuming command: " << commandName); for (auto& command : std::make_unique()->GetCommands()) { - if ( - Utility::CaseInsensitiveEquals(commandName, command->Name()) || - Utility::CaseInsensitiveContains(command->Aliases(), commandName) - ) + if (Utility::CaseInsensitiveEquals(commandName, command->Name())) { commandToResume = std::move(command); break; @@ -85,7 +79,21 @@ namespace AppInstaller::CLI resumeContext.SetExecutingCommand(commandToResume.get()); resumeContext.SetFlags(Execution::ContextFlag::Resume); + + // Load arguments here: + + auto previousThreadGlobals = resumeContext.SetForCurrentThread(); + resumeContext.EnableSignalTerminationHandler(); commandToResume->Resume(resumeContext); context.SetTerminationHR(resumeContext.GetTerminationHR()); } + + void ResumeCommand::ValidateArgumentsInternal(Execution::Args& execArgs) const + { + std::string resumeGuidString{ execArgs.GetArg(Execution::Args::Type::ResumeId) }; + if (!Utility::IsValidGuidString(resumeGuidString)) + { + throw CommandException(Resource::String::InvalidResumeIdError(Utility::LocIndView{ resumeGuidString })); + } + } } diff --git a/src/AppInstallerCLICore/Commands/ResumeCommand.h b/src/AppInstallerCLICore/Commands/ResumeCommand.h index 79c915df6d..497458a644 100644 --- a/src/AppInstallerCLICore/Commands/ResumeCommand.h +++ b/src/AppInstallerCLICore/Commands/ResumeCommand.h @@ -18,5 +18,6 @@ namespace AppInstaller::CLI protected: void ExecuteInternal(Execution::Context& context) const override; + void ValidateArgumentsInternal(Execution::Args& execArgs) const override; }; } diff --git a/src/AppInstallerCLICore/ExecutionContext.cpp b/src/AppInstallerCLICore/ExecutionContext.cpp index 4ad49e28a2..ffd94fce18 100644 --- a/src/AppInstallerCLICore/ExecutionContext.cpp +++ b/src/AppInstallerCLICore/ExecutionContext.cpp @@ -6,6 +6,7 @@ #include "Argument.h" #include "winget/UserSettings.h" #include "AppInstallerRuntime.h" +#include "Command.h" namespace AppInstaller::CLI::Execution { @@ -252,12 +253,22 @@ namespace AppInstaller::CLI::Execution Context::~Context() { + if (Settings::ExperimentalFeature::IsEnabled(ExperimentalFeature::Feature::Resume) && !IsTerminated()) + { + CheckpointManager.CleanUpIndex(); + } + if (m_disableSignalTerminationHandlerOnExit) { EnableSignalTerminationHandler(false); } } + Context Context::CreateEmptyContext() + { + return Context(Reporter, m_threadGlobals); + } + std::unique_ptr Context::CreateSubContext() { auto clone = std::make_unique(Reporter, m_threadGlobals); @@ -410,56 +421,32 @@ namespace AppInstaller::CLI::Execution } #endif - void Context::InitializeCheckpointManager(GUID id) - { - m_checkpointManager = std::make_unique(id); - } - - void Context::InitializeCheckpointManager(std::string_view commandName, std::string_view commandArguments, std::string_view clientVersion) - { - m_checkpointManager = std::make_unique(commandName, commandArguments, clientVersion); - } - void Context::Checkpoint(std::string_view checkpointName, std::vector contextData) { - // TODO: Implementation for capturing each context data specified in the provided vector. - UNREFERENCED_PARAMETER(checkpointName); - UNREFERENCED_PARAMETER(contextData); - } + CheckpointManager.CreateRecord(); - std::string Context::GetCommandLineFromArgs() - { - std::string commandLine; - for (auto type : Args.GetTypes()) + if (contextData.empty()) { - const auto& argument = Argument::ForType(type); - commandLine += "--" + std::string{ argument.Name() } + " " + std::string{ Args.GetArg(type) }; - } + // Capture automatic metadata. + CheckpointManager.SetClientVersion(AppInstaller::Runtime::GetClientVersion()); - return commandLine; - } - - std::vector Context::GetArgsFromCheckpoint() - { - const auto& commandLineString = m_checkpointManager->GetArguments(); - return Utility::Split(commandLineString, ' '); - } - - std::string Context::GetCommandNameFromCheckpoint() - { - return m_checkpointManager->GetCommandName(); - } + const auto& executingCommand = m_executingCommand; + if (executingCommand != nullptr) + { + CheckpointManager.SetCommandName(executingCommand->Name()); + } - std::string Context::GetClientVersionFromCheckpoint() - { - return m_checkpointManager->GetClientVersion(); - } + // Capture arguments - void Context::CleanUpCheckpoints() - { - if (m_checkpointManager) + } + else { - m_checkpointManager->CleanUpIndex(); + // Capture context data. } + + // TODO: Implementation for capturing each context data specified in the provided vector. + UNREFERENCED_PARAMETER(checkpointName); + UNREFERENCED_PARAMETER(contextData); } + } diff --git a/src/AppInstallerCLICore/ExecutionContext.h b/src/AppInstallerCLICore/ExecutionContext.h index b16994fdfa..03834d3027 100644 --- a/src/AppInstallerCLICore/ExecutionContext.h +++ b/src/AppInstallerCLICore/ExecutionContext.h @@ -98,6 +98,9 @@ namespace AppInstaller::CLI::Execution // The arguments given to execute with. Args Args; + // Creates a empty context, inheriting + Context CreateEmptyContext(); + // Creates a child of this context. virtual std::unique_ptr CreateSubContext(); @@ -162,30 +165,12 @@ namespace AppInstaller::CLI::Execution // Enable tests to override behavior bool ShouldExecuteWorkflowTask(const Workflow::WorkflowTask& task); #endif - // Initialize an existing checkpoint manager with a resume id. - void InitializeCheckpointManager(GUID id); - // Initialize a new checkpoint manager. - void InitializeCheckpointManager(std::string_view commandName, std::string_view commandArguments, std::string_view clientVersion); + Checkpoint::CheckpointManager CheckpointManager; // Records the provided context data to the checkpoint index. If it already exists, loads the context instead. void Checkpoint(std::string_view checkpointName, std::vector contextData); - // Gets the command line string from the store context arguments. - std::string GetCommandLineFromArgs(); - - // Gets the arguments from the checkpoint index. - std::vector GetArgsFromCheckpoint(); - - // Gets the command name from the checkpoint index. - std::string GetCommandNameFromCheckpoint(); - - // Gets the client version from the checkpoint index. - std::string GetClientVersionFromCheckpoint(); - - // Removes the checkpoint index file. - void CleanUpCheckpoints(); - protected: // Copies the args that are also needed in a sub-context. E.g., silent void CopyArgsToSubContext(Context* subContext); @@ -204,6 +189,5 @@ namespace AppInstaller::CLI::Execution Workflow::ExecutionStage m_executionStage = Workflow::ExecutionStage::Initial; AppInstaller::ThreadLocalStorage::WingetThreadGlobals m_threadGlobals; AppInstaller::CLI::Command* m_executingCommand = nullptr; - std::unique_ptr m_checkpointManager; }; } diff --git a/src/AppInstallerCLICore/Resources.h b/src/AppInstallerCLICore/Resources.h index b28a68386d..23c75aa902 100644 --- a/src/AppInstallerCLICore/Resources.h +++ b/src/AppInstallerCLICore/Resources.h @@ -46,6 +46,7 @@ namespace AppInstaller::CLI::Resource WINGET_DEFINE_RESOURCE_STRINGID(ClientVersionMismatchError); WINGET_DEFINE_RESOURCE_STRINGID(Command); WINGET_DEFINE_RESOURCE_STRINGID(CommandArgumentDescription); + WINGET_DEFINE_RESOURCE_STRINGID(CommandDoesNotSupportResumeMessage); WINGET_DEFINE_RESOURCE_STRINGID(CommandLineArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(CommandRequiresAdmin); WINGET_DEFINE_RESOURCE_STRINGID(CompleteCommandLongDescription); diff --git a/src/AppInstallerCLICore/Workflows/ResumeFlow.h b/src/AppInstallerCLICore/Workflows/ResumeFlow.h index 2edf839a7d..d4c8307793 100644 --- a/src/AppInstallerCLICore/Workflows/ResumeFlow.h +++ b/src/AppInstallerCLICore/Workflows/ResumeFlow.h @@ -5,19 +5,13 @@ namespace AppInstaller::CLI::Workflow { - // Ensures that the arguments provided supports a resume. - // Required Args: None - // Inputs: ResumeGuid - // Outputs: None - void EnsureSupportForResume(Execution::Context& context); - // Applies a checkpoint to the context workflow. struct Checkpoint : public WorkflowTask { Checkpoint(std::string_view checkpointName, std::vector contextData) : - WorkflowTask("ApplyCheckpoint"), + WorkflowTask("Checkpoint"), m_checkpointName(checkpointName), - m_contextData(contextData) {} + m_contextData(std::move(contextData)) {} void operator()(Execution::Context& context) const override; diff --git a/src/AppInstallerCLIE2ETests/Constants.cs b/src/AppInstallerCLIE2ETests/Constants.cs index a7fb163492..46b0ab577b 100644 --- a/src/AppInstallerCLIE2ETests/Constants.cs +++ b/src/AppInstallerCLIE2ETests/Constants.cs @@ -250,11 +250,10 @@ public class ErrorCode public const int ERROR_APPTERMINATION_RECEIVED = unchecked((int)0x8A15006A); public const int ERROR_DOWNLOAD_DEPENDENCIES = unchecked((int)0x8A15006B); public const int ERROR_DOWNLOAD_COMMAND_PROHIBITED = unchecked((int)0x8A15006C); - public const int ERROR_INVALID_RESUME_ID = unchecked((int)0x8A15006D); - public const int ERROR_RESUME_ID_NOT_FOUND = unchecked((int)0x8A15006E); - public const int ERROR_CLIENT_VERSION_MISMATCH = unchecked((int)0x8A15006F); - public const int ERROR_INVALID_RESUME_STATE = unchecked((int)0x8A150070); - public const int ERROR_CANNOT_OPEN_CHECKPOINT_INDEX = unchecked((int)0x8A150071); + public const int ERROR_RESUME_ID_NOT_FOUND = unchecked((int)0x8A15006D); + public const int ERROR_CLIENT_VERSION_MISMATCH = unchecked((int)0x8A15006E); + public const int ERROR_INVALID_RESUME_STATE = unchecked((int)0x8A15006F); + public const int ERROR_CANNOT_OPEN_CHECKPOINT_INDEX = unchecked((int)0x8A150070); public const int ERROR_INSTALL_PACKAGE_IN_USE = unchecked((int)0x8A150101); public const int ERROR_INSTALL_INSTALL_IN_PROGRESS = unchecked((int)0x8A150102); diff --git a/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw b/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw index d3dee10a74..f055fa524f 100644 --- a/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw +++ b/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw @@ -2082,4 +2082,7 @@ Please specify one of them using the --source option to proceed. No data found in the resume state. + + This command does not support resuming. + \ No newline at end of file diff --git a/src/AppInstallerCLITests/ResumeFlow.cpp b/src/AppInstallerCLITests/ResumeFlow.cpp index 92c209d081..51c6741edc 100644 --- a/src/AppInstallerCLITests/ResumeFlow.cpp +++ b/src/AppInstallerCLITests/ResumeFlow.cpp @@ -70,7 +70,7 @@ TEST_CASE("ResumeFlow_InvalidClientVersion", "[Resume]") INFO("Using temporary file named: " << tempIndexPath); { - CheckpointIndex index = CheckpointIndex::CreateNew(tempIndexPath.u8string()); + CheckpointRecord index = CheckpointRecord::CreateNew(tempIndexPath.u8string()); index.SetClientVersion(invalidClientVersion); } @@ -104,7 +104,7 @@ TEST_CASE("ResumeFlow_EmptyIndex", "Resume") INFO("Using temporary file named: " << tempIndexPath); { - CheckpointIndex index = CheckpointIndex::CreateNew(tempGuidString); + CheckpointRecord index = CheckpointRecord::CreateNew(tempGuidString); index.SetClientVersion(AppInstaller::Runtime::GetClientVersion()); } diff --git a/src/AppInstallerCommonCore/Public/AppInstallerRuntime.h b/src/AppInstallerCommonCore/Public/AppInstallerRuntime.h index 3ce763fa28..5dfd4f2567 100644 --- a/src/AppInstallerCommonCore/Public/AppInstallerRuntime.h +++ b/src/AppInstallerCommonCore/Public/AppInstallerRuntime.h @@ -49,6 +49,8 @@ namespace AppInstaller::Runtime SelfPackageRoot, // The location where user downloads are stored. UserProfileDownloads, + // The location where checkpoints are stored. + CheckpointsLocation, // Always one more than the last path; for being able to iterate paths in tests. Max }; diff --git a/src/AppInstallerCommonCore/Runtime.cpp b/src/AppInstallerCommonCore/Runtime.cpp index 19a2e7e854..734670095c 100644 --- a/src/AppInstallerCommonCore/Runtime.cpp +++ b/src/AppInstallerCommonCore/Runtime.cpp @@ -387,6 +387,10 @@ namespace AppInstaller::Runtime result.Path = GetKnownFolderPath(FOLDERID_Downloads); mayBeInProfilePath = true; break; + case PathName::CheckpointsLocation: + result.Path = GetPathDetailsFor(PathName::LocalState).Path; + result.Path /= s_CheckpointsDirectory; + break; default: THROW_HR(E_UNEXPECTED); } @@ -533,6 +537,7 @@ namespace AppInstaller::Runtime case PathName::PortableLinksUserLocation: case PathName::PortablePackageUserRoot: case PathName::UserProfileDownloads: + case PathName::CheckpointsLocation: result = GetPathDetailsCommon(path, forDisplay); break; case PathName::SelfPackageRoot: diff --git a/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj b/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj index 46699aa8de..27c048d5f3 100644 --- a/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj +++ b/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj @@ -353,7 +353,7 @@ - + @@ -388,7 +388,7 @@ - + @@ -396,8 +396,9 @@ - + + @@ -476,7 +477,7 @@ - + @@ -500,8 +501,9 @@ - + + diff --git a/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj.filters b/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj.filters index 9e8daa72b5..0c73be575d 100644 --- a/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj.filters +++ b/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj.filters @@ -390,21 +390,24 @@ Rest\Schema\1_6 - + Microsoft\Schema - + Microsoft Microsoft\Schema\Checkpoint_1_0 - + Microsoft\Schema\Checkpoint_1_0 Microsoft\Schema\Checkpoint_1_0 + + Microsoft\Schema\Checkpoint_1_0 + @@ -632,18 +635,21 @@ Rest\Schema\1_6 - + Source Files Microsoft\Schema\Checkpoint_1_0 - + Source Files Microsoft\Schema\Checkpoint_1_0 + + Microsoft\Schema\Checkpoint_1_0 + diff --git a/src/AppInstallerRepositoryCore/Microsoft/CheckpointIndex.cpp b/src/AppInstallerRepositoryCore/Microsoft/CheckpointIndex.cpp deleted file mode 100644 index 629b6c9636..0000000000 --- a/src/AppInstallerRepositoryCore/Microsoft/CheckpointIndex.cpp +++ /dev/null @@ -1,212 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. -#include "pch.h" -#include "CheckpointIndex.h" -#include "SQLiteStorageBase.h" -#include "Schema/Checkpoint_1_0/CheckpointIndexInterface.h" - -namespace AppInstaller::Repository::Microsoft -{ - namespace - { - constexpr std::string_view s_Checkpoints = "Checkpoints"sv; - } - - CheckpointIndex CheckpointIndex::CreateNew(const std::string& filePath, Schema::Version version) - { - AICLI_LOG(Repo, Info, << "Creating new Checkpoint Index with version [" << version << "] at '" << filePath << "'"); - CheckpointIndex result{ filePath, version }; - - SQLite::Savepoint savepoint = SQLite::Savepoint::Create(result.m_dbconn, "CheckpointIndex_createnew"); - - // Use calculated version, as incoming version could be 'latest' - result.m_version.SetSchemaVersion(result.m_dbconn); - - result.m_interface->CreateTables(result.m_dbconn); - - result.SetLastWriteTime(); - - savepoint.Commit(); - - return result; - } - - std::shared_ptr CheckpointIndex::OpenOrCreateDefault(GUID guid, OpenDisposition openDisposition) - { - const auto& indexPath = GetCheckpointIndexPath(guid); - AICLI_LOG(Repo, Info, << "Opening checkpoint index"); - - try - { - if (std::filesystem::exists(indexPath)) - { - if (std::filesystem::is_regular_file(indexPath)) - { - try - { - AICLI_LOG(Repo, Info, << "Opening existing checkpoint index"); - return std::make_shared(CheckpointIndex::Open(indexPath.u8string(), openDisposition)); - } - CATCH_LOG(); - } - - AICLI_LOG(Repo, Info, << "Attempting to delete bad checkpoint index file"); - std::filesystem::remove_all(indexPath); - } - - return std::make_shared(CheckpointIndex::CreateNew(indexPath.u8string())); - } - CATCH_LOG(); - - return {}; - } - -#ifndef AICLI_DISABLE_TEST_HOOKS - std::optional s_CheckpointIndexDirectoryOverride{}; - void TestHook_SetCheckpointIndexDirectory_Override(std::optional&& checkpointIndexDirectory) - { - s_CheckpointIndexDirectoryOverride = std::move(checkpointIndexDirectory); - } -#endif - - std::filesystem::path CheckpointIndex::GetCheckpointIndexPath(GUID guid) - { - wchar_t checkpointGuid[256]; - THROW_HR_IF(E_UNEXPECTED, !StringFromGUID2(guid, checkpointGuid, ARRAYSIZE(checkpointGuid))); - - const auto DefaultIndexDirectoryPath = Runtime::GetPathTo(Runtime::PathName::LocalState) / s_Checkpoints; -#ifndef AICLI_DISABLE_TEST_HOOKS - const auto checkpointIndexDirectory = s_CheckpointIndexDirectoryOverride.has_value() ? s_CheckpointIndexDirectoryOverride.value() : DefaultIndexDirectoryPath; -#else - const auto checkpointIndexDirectory = DefaultIndexDirectoryPath; -#endif - - if (!std::filesystem::exists(checkpointIndexDirectory)) - { - std::filesystem::create_directories(checkpointIndexDirectory); - AICLI_LOG(Repo, Info, << "Creating checkpoint index directory: " << checkpointIndexDirectory); - } - else - { - THROW_HR_IF(ERROR_CANNOT_MAKE, !std::filesystem::is_directory(checkpointIndexDirectory)); - } - - auto indexPath = checkpointIndexDirectory / checkpointGuid; - indexPath.replace_extension(".db"); - return indexPath; - } - - bool CheckpointIndex::IsEmpty() - { - return m_interface->IsEmpty(m_dbconn); - } - - CheckpointIndex::IdType CheckpointIndex::SetClientVersion(std::string_view clientVersion) - { - std::lock_guard lockInterface{ *m_interfaceLock }; - AICLI_LOG(Repo, Verbose, << "Setting client version [" << clientVersion << "]"); - - SQLite::Savepoint savepoint = SQLite::Savepoint::Create(m_dbconn, "checkpointindex_setclientversion"); - - IdType result = m_interface->SetClientVersion(m_dbconn, clientVersion); - - SetLastWriteTime(); - savepoint.Commit(); - return result; - } - - std::string CheckpointIndex::GetClientVersion() - { - return m_interface->GetClientVersion(m_dbconn); - } - - CheckpointIndex::IdType CheckpointIndex::SetCommandName(std::string_view commandName) - { - std::lock_guard lockInterface{ *m_interfaceLock }; - AICLI_LOG(Repo, Verbose, << "Setting command name [" << commandName << "]"); - - SQLite::Savepoint savepoint = SQLite::Savepoint::Create(m_dbconn, "checkpointindex_setcommandname"); - - IdType result = m_interface->SetCommandName(m_dbconn, commandName); - - SetLastWriteTime(); - savepoint.Commit(); - return result; - } - - std::string CheckpointIndex::GetCommandName() - { - return m_interface->GetCommandName(m_dbconn); - } - - CheckpointIndex::IdType CheckpointIndex::SetCommandArguments(std::string_view commandArguments) - { - std::lock_guard lockInterface{ *m_interfaceLock }; - AICLI_LOG(Repo, Verbose, << "Setting command arguments [" << commandArguments << "]"); - - SQLite::Savepoint savepoint = SQLite::Savepoint::Create(m_dbconn, "checkpointindex_setcommandarguments"); - - IdType result = m_interface->SetCommandArguments(m_dbconn, commandArguments); - - SetLastWriteTime(); - savepoint.Commit(); - return result; - } - - std::string CheckpointIndex::GetCommandArguments() - { - return m_interface->GetCommandArguments(m_dbconn); - } - - CheckpointIndex::IdType CheckpointIndex::AddContextData(std::string_view checkpointName, int contextData, std::string_view name, std::string_view value) - { - std::lock_guard lockInterface{ *m_interfaceLock }; - AICLI_LOG(Repo, Verbose, << "Setting context data [" << contextData << "] for [" << name << "] with value [" << value << "] value"); - - SQLite::Savepoint savepoint = SQLite::Savepoint::Create(m_dbconn, "checkpointindex_addcontextdata"); - SQLite::rowid_t rowId = m_interface->AddContextData(m_dbconn, checkpointName, contextData, name, value); - savepoint.Commit(); - return rowId; - } - - std::string CheckpointIndex::GetContextData(std::string_view checkpointName, int contextData, std::string_view name) - { - return m_interface->GetContextData(m_dbconn, checkpointName, contextData, name); - } - - void CheckpointIndex::RemoveContextData(std::string_view checkpointName, int contextData) - { - std::lock_guard lockInterface{ *m_interfaceLock }; - AICLI_LOG(Repo, Verbose, << "Removing context data [" << contextData << "]"); - - SQLite::Savepoint savepoint = SQLite::Savepoint::Create(m_dbconn, "checkpointindex_addcontextdata"); - m_interface->RemoveContextData(m_dbconn, checkpointName, contextData); - savepoint.Commit(); - } - - std::unique_ptr CheckpointIndex::CreateICheckpointIndex() const - { - if (m_version == Schema::Version{ 1, 0 } || - m_version.MajorVersion == 1 || - m_version.IsLatest()) - { - return std::make_unique(); - } - - THROW_HR(HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED)); - } - - CheckpointIndex::CheckpointIndex(const std::string& target, SQLiteStorageBase::OpenDisposition disposition, Utility::ManagedFile&& indexFile) : - SQLiteStorageBase(target, disposition, std::move(indexFile)) - { - AICLI_LOG(Repo, Info, << "Opened Checkpoint Index with version [" << m_version << "], last write [" << GetLastWriteTime() << "]"); - m_interface = CreateICheckpointIndex(); - THROW_HR_IF(APPINSTALLER_CLI_ERROR_CANNOT_WRITE_TO_UPLEVEL_INDEX, disposition == SQLiteStorageBase::OpenDisposition::ReadWrite && m_version != m_interface->GetVersion()); - } - - CheckpointIndex::CheckpointIndex(const std::string& target, Schema::Version version) : SQLiteStorageBase(target, version) - { - m_interface = CreateICheckpointIndex(); - m_version = m_interface->GetVersion(); - } -} \ No newline at end of file diff --git a/src/AppInstallerRepositoryCore/Microsoft/CheckpointIndex.h b/src/AppInstallerRepositoryCore/Microsoft/CheckpointIndex.h deleted file mode 100644 index 7dea30ad4c..0000000000 --- a/src/AppInstallerRepositoryCore/Microsoft/CheckpointIndex.h +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. -#pragma once -#include "SQLiteWrapper.h" -#include "Microsoft/Schema/ICheckpointIndex.h" -#include "Microsoft/SQLiteStorageBase.h" -#include - -namespace AppInstaller::Repository::Microsoft -{ - struct CheckpointIndex : SQLiteStorageBase - { - // An id that refers to a specific Checkpoint. - using IdType = SQLite::rowid_t; - - CheckpointIndex(const CheckpointIndex&) = delete; - CheckpointIndex& operator=(const CheckpointIndex&) = delete; - - CheckpointIndex(CheckpointIndex&&) = default; - CheckpointIndex& operator=(CheckpointIndex&&) = default; - - // Creates a new CheckpointIndex database of the given version. - static CheckpointIndex CreateNew(const std::string& filePath, Schema::Version version = Schema::Version::Latest()); - - // Opens an existing CheckpointIndex database. - static CheckpointIndex Open(const std::string& filePath, OpenDisposition disposition, Utility::ManagedFile&& indexFile = {}) - { - return { filePath, disposition, std::move(indexFile) }; - } - - // Opens or creates a CheckpointIndex database on the default path. - static std::shared_ptr OpenOrCreateDefault(GUID guid, OpenDisposition openDisposition = OpenDisposition::ReadWrite); - - // Gets the file path of the CheckpointIndex database. - static std::filesystem::path GetCheckpointIndexPath(GUID guid); - - bool IsEmpty(); - - IdType SetClientVersion(std::string_view clientVersion); - - std::string GetClientVersion(); - - IdType SetCommandName(std::string_view commandName); - - std::string GetCommandName(); - - IdType SetCommandArguments(std::string_view commandArguments); - - std::string GetCommandArguments(); - - IdType AddContextData(std::string_view checkpointName, int contextData, std::string_view name, std::string_view value); - - std::string GetContextData(std::string_view checkpointName, int contextData, std::string_view name); - - void RemoveContextData(std::string_view checkpointName, int contextData); - - private: - // Constructor used to open an existing index. - CheckpointIndex(const std::string& target, SQLiteStorageBase::OpenDisposition disposition, Utility::ManagedFile&& indexFile); - - // Constructor used to create a new index. - CheckpointIndex(const std::string& target, Schema::Version version); - - // Creates the ICheckpointIndex interface object for this version. - std::unique_ptr CreateICheckpointIndex() const; - - std::unique_ptr m_interface; - }; -} \ No newline at end of file diff --git a/src/AppInstallerRepositoryCore/Microsoft/CheckpointRecord.cpp b/src/AppInstallerRepositoryCore/Microsoft/CheckpointRecord.cpp new file mode 100644 index 0000000000..e2a05fc710 --- /dev/null +++ b/src/AppInstallerRepositoryCore/Microsoft/CheckpointRecord.cpp @@ -0,0 +1,196 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#include "pch.h" +#include "CheckpointRecord.h" +#include "SQLiteStorageBase.h" +#include "Schema/Checkpoint_1_0/CheckpointRecordInterface.h" + +namespace AppInstaller::Repository::Microsoft +{ + namespace + { + constexpr std::string_view s_checkpoints_filename = "checkpoints.db"sv; + + constexpr std::string_view s_Checkpoints = "Checkpoints"sv; + constexpr std::string_view s_ClientVersion = "ClientVersion"sv; + constexpr std::string_view s_CommandName = "CommandName"sv; + + std::string_view GetCheckpointMetadataString(CheckpointMetadata checkpointMetadata) + { + switch (checkpointMetadata) + { + case CheckpointMetadata::ClientVersion: + return s_ClientVersion; + case CheckpointMetadata::CommandName: + return s_CommandName; + default: + return "unknown"sv; + } + } + } + + CheckpointRecord CheckpointRecord::CreateNew(const std::string& filePath, Schema::Version version) + { + AICLI_LOG(Repo, Info, << "Creating new Checkpoint Index with version [" << version << "] at '" << filePath << "'"); + CheckpointRecord result{ filePath, version }; + + SQLite::Savepoint savepoint = SQLite::Savepoint::Create(result.m_dbconn, "CheckpointRecord_createnew"); + + // Use calculated version, as incoming version could be 'latest' + result.m_version.SetSchemaVersion(result.m_dbconn); + + result.m_interface->CreateTables(result.m_dbconn); + + result.SetLastWriteTime(); + + savepoint.Commit(); + + return result; + } + + std::shared_ptr CheckpointRecord::CreateDefault(GUID guid) + { + const auto& indexPath = GetCheckpointRecordPath(guid); + AICLI_LOG(Repo, Info, << "Creating checkpoint index"); + + try + { + return std::make_shared(CheckpointRecord::CreateNew(indexPath.u8string())); + } + CATCH_LOG(); + + return {}; + } + + std::shared_ptr CheckpointRecord::OpenDefault(GUID guid) + { + const auto& indexPath = GetCheckpointRecordPath(guid); + AICLI_LOG(Repo, Info, << "Opening existing checkpoint index"); + + try + { + return std::make_shared(CheckpointRecord::Open(indexPath.u8string(), OpenDisposition::ReadWrite)); + } + CATCH_LOG(); + + return {}; + } + + std::filesystem::path CheckpointRecord::GetCheckpointRecordPath(GUID guid) + { + wchar_t checkpointGuid[256]; + THROW_HR_IF(E_UNEXPECTED, !StringFromGUID2(guid, checkpointGuid, ARRAYSIZE(checkpointGuid))); + + const auto checkpointsDirectory = Runtime::GetPathTo(Runtime::PathName::CheckpointsLocation) / checkpointGuid; + + if (!std::filesystem::exists(checkpointsDirectory)) + { + std::filesystem::create_directories(checkpointsDirectory); + AICLI_LOG(Repo, Info, << "Creating checkpoint index directory: " << checkpointsDirectory); + } + else + { + THROW_HR_IF(ERROR_CANNOT_MAKE, !std::filesystem::is_directory(checkpointsDirectory)); + } + + auto indexPath = checkpointsDirectory / s_checkpoints_filename; + return indexPath; + } + + bool CheckpointRecord::IsEmpty() + { + return m_interface->IsEmpty(m_dbconn); + } + + std::string CheckpointRecord::GetMetadata(CheckpointMetadata checkpointMetadata) + { + const auto& metadataName = GetCheckpointMetadataString(checkpointMetadata); + return m_interface->GetMetadata(m_dbconn, metadataName); + } + + CheckpointRecord::IdType CheckpointRecord::SetMetadata(CheckpointMetadata checkpointMetadata, std::string_view value) + { + std::lock_guard lockInterface{ *m_interfaceLock }; + AICLI_LOG(Repo, Verbose, << "Setting client version [" << value << "]"); + + SQLite::Savepoint savepoint = SQLite::Savepoint::Create(m_dbconn, "checkpointindex_setmetadata"); + + const auto& metadataName = GetCheckpointMetadataString(checkpointMetadata); + IdType result = m_interface->SetMetadata(m_dbconn, metadataName, value); + + SetLastWriteTime(); + savepoint.Commit(); + return result; + } + + CheckpointRecord::IdType CheckpointRecord::AddCheckpoint(std::string_view checkpointName) + { + std::lock_guard lockInterface{ *m_interfaceLock }; + AICLI_LOG(Repo, Verbose, << "Adding checkpoint [" << checkpointName << "]"); + + SQLite::Savepoint savepoint = SQLite::Savepoint::Create(m_dbconn, "checkpointrecord_addcheckpoint"); + + IdType result = m_interface->AddCheckpoint(m_dbconn, checkpointName); + + SetLastWriteTime(); + savepoint.Commit(); + return result; + } + + std::optional CheckpointRecord::GetCheckpointId(std::string_view checkpointName) + { + return m_interface->GetCheckpointId(m_dbconn, checkpointName); + } + + CheckpointRecord::IdType CheckpointRecord::AddContextData(SQLite::rowid_t checkpointId, int contextData, std::string_view name, std::string_view value, int index) + { + std::lock_guard lockInterface{ *m_interfaceLock }; + AICLI_LOG(Repo, Verbose, << "Setting context data [" << contextData << "] for [" << name << "] with value [" << value << "] value"); + + SQLite::Savepoint savepoint = SQLite::Savepoint::Create(m_dbconn, "checkpointindex_addcontextdata"); + SQLite::rowid_t rowId = m_interface->AddContextData(m_dbconn, checkpointId, contextData, name, value, index); + savepoint.Commit(); + return rowId; + } + + std::vector CheckpointRecord::GetContextData(SQLite::rowid_t checkpointId, int contextData, std::string_view name) + { + return m_interface->GetContextData(m_dbconn, checkpointId, contextData, name); + } + + void CheckpointRecord::RemoveContextData(SQLite::rowid_t checkpointId, int contextData) + { + std::lock_guard lockInterface{ *m_interfaceLock }; + AICLI_LOG(Repo, Verbose, << "Removing context data [" << contextData << "]"); + + SQLite::Savepoint savepoint = SQLite::Savepoint::Create(m_dbconn, "checkpointindex_addcontextdata"); + m_interface->RemoveContextData(m_dbconn, checkpointId, contextData); + savepoint.Commit(); + } + + std::unique_ptr CheckpointRecord::CreateICheckpointRecord() const + { + if (m_version == Schema::Version{ 1, 0 } || + m_version.MajorVersion == 1 || + m_version.IsLatest()) + { + return std::make_unique(); + } + + THROW_HR(HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED)); + } + + CheckpointRecord::CheckpointRecord(const std::string& target, SQLiteStorageBase::OpenDisposition disposition, Utility::ManagedFile&& indexFile) : + SQLiteStorageBase(target, disposition, std::move(indexFile)) + { + AICLI_LOG(Repo, Info, << "Opened Checkpoint Index with version [" << m_version << "], last write [" << GetLastWriteTime() << "]"); + m_interface = CreateICheckpointRecord(); + THROW_HR_IF(APPINSTALLER_CLI_ERROR_CANNOT_WRITE_TO_UPLEVEL_INDEX, disposition == SQLiteStorageBase::OpenDisposition::ReadWrite && m_version != m_interface->GetVersion()); + } + + CheckpointRecord::CheckpointRecord(const std::string& target, Schema::Version version) : SQLiteStorageBase(target, version) + { + m_interface = CreateICheckpointRecord(); + m_version = m_interface->GetVersion(); + } +} \ No newline at end of file diff --git a/src/AppInstallerRepositoryCore/Microsoft/CheckpointRecord.h b/src/AppInstallerRepositoryCore/Microsoft/CheckpointRecord.h new file mode 100644 index 0000000000..c5067838e6 --- /dev/null +++ b/src/AppInstallerRepositoryCore/Microsoft/CheckpointRecord.h @@ -0,0 +1,72 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once +#include "SQLiteWrapper.h" +#include "Microsoft/Schema/ICheckpointRecord.h" +#include "Microsoft/SQLiteStorageBase.h" +#include + +namespace AppInstaller::Repository::Microsoft +{ + enum CheckpointMetadata + { + ClientVersion, + CommandName + }; + + struct CheckpointRecord : SQLiteStorageBase + { + // An id that refers to a specific Checkpoint. + using IdType = SQLite::rowid_t; + + CheckpointRecord(const CheckpointRecord&) = delete; + CheckpointRecord& operator=(const CheckpointRecord&) = delete; + + CheckpointRecord(CheckpointRecord&&) = default; + CheckpointRecord& operator=(CheckpointRecord&&) = default; + + // Opens an existing CheckpointRecord database. + static CheckpointRecord Open(const std::string& filePath, OpenDisposition disposition, Utility::ManagedFile&& indexFile = {}) + { + return { filePath, disposition, std::move(indexFile) }; + } + + // Create a new CheckpointRecord database. + static CheckpointRecord CreateNew(const std::string& filePath, Schema::Version version = Schema::Version::Latest()); + + static std::shared_ptr OpenDefault(GUID guid); + + static std::shared_ptr CreateDefault(GUID guid); + + // Gets the file path of the CheckpointRecord database. + static std::filesystem::path GetCheckpointRecordPath(GUID guid); + + bool IsEmpty(); + + std::string GetMetadata(CheckpointMetadata checkpointMetadata); + + IdType SetMetadata(CheckpointMetadata checkpointMetadata, std::string_view value); + + IdType AddCheckpoint(std::string_view checkpointName); + + std::optional GetCheckpointId(std::string_view checkpointName); + + IdType AddContextData(SQLite::rowid_t checkpointId, int contextData, std::string_view name, std::string_view value, int index); + + std::vector GetContextData(SQLite::rowid_t checkpointId, int contextData, std::string_view name); + + void RemoveContextData(SQLite::rowid_t checkpointId, int contextData); + + private: + // Constructor used to open an existing index. + CheckpointRecord(const std::string& target, SQLiteStorageBase::OpenDisposition disposition, Utility::ManagedFile&& indexFile); + + // Constructor used to create a new index. + CheckpointRecord(const std::string& target, Schema::Version version); + + // Creates the ICheckpointRecord interface object for this version. + std::unique_ptr CreateICheckpointRecord() const; + + std::unique_ptr m_interface; + }; +} \ No newline at end of file diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointContextTable.cpp b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointContextTable.cpp index c8f01e4043..4b3d69c4fd 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointContextTable.cpp +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointContextTable.cpp @@ -3,17 +3,17 @@ #include "pch.h" #include "CheckpointContextTable.h" #include "SQLiteStatementBuilder.h" -#include "Microsoft/Schema/ICheckpointIndex.h" namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 { using namespace SQLite; using namespace std::string_view_literals; static constexpr std::string_view s_CheckpointContextTable_Table_Name = "CheckpointContext"sv; - static constexpr std::string_view s_CheckpointContextTable_CheckpointName_Column = "CheckpointName"sv; + static constexpr std::string_view s_CheckpointContextTable_CheckpointId_Column = "CheckpointId"sv; static constexpr std::string_view s_CheckpointContextTable_ContextData_Column = "ContextData"sv; static constexpr std::string_view s_CheckpointContextTable_Name_Column = "Name"sv; static constexpr std::string_view s_CheckpointContextTable_Value_Column = "Value"sv; + static constexpr std::string_view s_CheckpointContextTable_Index_Column = "Index"sv; namespace { @@ -21,10 +21,11 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 { SQLite::Builder::StatementBuilder builder; builder.InsertInto(s_CheckpointContextTable_Table_Name) - .Columns({ s_CheckpointContextTable_CheckpointName_Column, + .Columns({ s_CheckpointContextTable_CheckpointId_Column, s_CheckpointContextTable_ContextData_Column, s_CheckpointContextTable_Name_Column, - s_CheckpointContextTable_Value_Column }) + s_CheckpointContextTable_Value_Column, + s_CheckpointContextTable_Index_Column}) .Values(name, value); builder.Execute(connection); @@ -52,38 +53,21 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 { using namespace SQLite::Builder; - SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "createCheckpointArgumentsTable_v1_0"); + SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "createCheckpointContextTable_v1_0"); StatementBuilder createTableBuilder; createTableBuilder.CreateTable(s_CheckpointContextTable_Table_Name).BeginColumns(); - createTableBuilder.Column(ColumnBuilder(s_CheckpointContextTable_CheckpointName_Column, Type::Text).NotNull()); + createTableBuilder.Column(ColumnBuilder(s_CheckpointContextTable_CheckpointId_Column, Type::Int).NotNull()); createTableBuilder.Column(ColumnBuilder(s_CheckpointContextTable_ContextData_Column, Type::Int)); createTableBuilder.Column(ColumnBuilder(s_CheckpointContextTable_Name_Column, Type::Text)); createTableBuilder.Column(ColumnBuilder(s_CheckpointContextTable_Value_Column, Type::Text)); + createTableBuilder.Column(ColumnBuilder(s_CheckpointContextTable_Value_Column, Type::Text)); + createTableBuilder.Column(ColumnBuilder(s_CheckpointContextTable_Index_Column, Type::Int)); createTableBuilder.EndColumns(); createTableBuilder.Execute(connection); savepoint.Commit(); } - bool CheckpointContextTable::ExistsById(const SQLite::Connection& connection, SQLite::rowid_t id) - { - SQLite::Builder::StatementBuilder builder; - builder.Select(SQLite::Builder::RowCount).From(s_CheckpointContextTable_Table_Name).Where(SQLite::RowIDName).Equals(id); - - SQLite::Statement countStatement = builder.Prepare(connection); - - THROW_HR_IF(E_UNEXPECTED, !countStatement.Step()); - - return (countStatement.GetColumn(0) != 0); - } - - void CheckpointContextTable::DeleteById(SQLite::Connection& connection, SQLite::rowid_t id) - { - SQLite::Builder::StatementBuilder builder; - builder.DeleteFrom(s_CheckpointContextTable_Table_Name).Where(SQLite::RowIDName).Equals(id); - builder.Execute(connection); - } - bool CheckpointContextTable::IsEmpty(SQLite::Connection& connection) { SQLite::Builder::StatementBuilder builder; @@ -96,59 +80,49 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 return (countStatement.GetColumn(0) == 0); } - SQLite::rowid_t CheckpointContextTable::AddCheckpoint(SQLite::Connection& connection, std::string_view checkpointName) - { - SQLite::Builder::StatementBuilder builder; - builder.InsertInto(s_CheckpointContextTable_Table_Name) - .Columns({ s_CheckpointContextTable_CheckpointName_Column }) - .Values(checkpointName); - - builder.Execute(connection); - return connection.GetLastInsertRowID(); - } - - SQLite::rowid_t CheckpointContextTable::AddContextData(SQLite::Connection& connection, std::string_view checkpointName, int contextData, std::string_view name, std::string_view value) + SQLite::rowid_t CheckpointContextTable::AddContextData(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int contextData, std::string_view name, std::string_view value, int index) { SQLite::Builder::StatementBuilder builder; builder.InsertInto(s_CheckpointContextTable_Table_Name) - .Columns({ s_CheckpointContextTable_CheckpointName_Column, + .Columns({ s_CheckpointContextTable_CheckpointId_Column, s_CheckpointContextTable_ContextData_Column, s_CheckpointContextTable_Name_Column, - s_CheckpointContextTable_Value_Column}) - .Values(checkpointName, contextData, name, value); + s_CheckpointContextTable_Value_Column, + s_CheckpointContextTable_Index_Column}) + .Values(checkpointId, contextData, name, value, index); builder.Execute(connection); return connection.GetLastInsertRowID(); } - void CheckpointContextTable::RemoveContextData(SQLite::Connection& connection, std::string_view checkpointName, int contextData) + std::vector CheckpointContextTable::GetContextData(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int contextData, std::string_view name) { SQLite::Builder::StatementBuilder builder; - builder.Select(SQLite::RowIDName).From(s_CheckpointContextTable_Table_Name).Where(s_CheckpointContextTable_CheckpointName_Column).Equals(checkpointName) - .And(s_CheckpointContextTable_ContextData_Column).Equals(contextData); + builder.Select(s_CheckpointContextTable_Value_Column).From(s_CheckpointContextTable_Table_Name).Where(s_CheckpointContextTable_CheckpointId_Column); + builder.Equals(checkpointId).And(s_CheckpointContextTable_ContextData_Column).Equals(contextData).And(s_CheckpointContextTable_Name_Column).Equals(name); SQLite::Statement select = builder.Prepare(connection); + + std::vector values; + while (select.Step()) { - DeleteById(connection, select.GetColumn(0)); + values.emplace_back(select.GetColumn(0)); } + + return values; } - std::string CheckpointContextTable::GetContextData(SQLite::Connection& connection, std::string_view checkpointName, int contextData, std::string_view name) + void CheckpointContextTable::RemoveContextData(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int contextData) { SQLite::Builder::StatementBuilder builder; - builder.Select(s_CheckpointContextTable_Value_Column).From(s_CheckpointContextTable_Table_Name).Where(s_CheckpointContextTable_CheckpointName_Column); - builder.Equals(checkpointName).And(s_CheckpointContextTable_ContextData_Column).Equals(contextData).And(s_CheckpointContextTable_Name_Column).Equals(name); + builder.Select(SQLite::RowIDName).From(s_CheckpointContextTable_Table_Name).Where(s_CheckpointContextTable_CheckpointId_Column).Equals(checkpointId) + .And(s_CheckpointContextTable_ContextData_Column).Equals(contextData); SQLite::Statement select = builder.Prepare(connection); - - if (select.Step()) - { - return select.GetColumn(0); - } - else + while (select.Step()) { - return {}; + //DeleteById(connection, select.GetColumn(0)); } } } \ No newline at end of file diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointContextTable.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointContextTable.h index 146977ee46..08f49dd186 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointContextTable.h +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointContextTable.h @@ -3,7 +3,7 @@ #pragma once #include "SQLiteWrapper.h" #include "SQLiteStatementBuilder.h" -#include "Microsoft/Schema/ICheckpointIndex.h" +#include "Microsoft/Schema/ICheckpointRecord.h" #include namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 @@ -16,22 +16,15 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 // Creates the table with named indices. static void Create(SQLite::Connection& connection); - // Gets a value indicating whether a context with the given rowid exists. - static bool ExistsById(const SQLite::Connection& connection, SQLite::rowid_t id); - - // Deletes the Checkpoint row with the given rowid - static void DeleteById(SQLite::Connection& connection, SQLite::rowid_t id); - // Gets a value indicating whether the table is empty. static bool IsEmpty(SQLite::Connection& connection); - // Adds a checkpoint row. - static SQLite::rowid_t AddCheckpoint(SQLite::Connection& connection, std::string_view checkpointName); + // Adds a context data for a checkpoint. Index is used to represent the item number if the context data has more than one value. + static SQLite::rowid_t AddContextData(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int contextData, std::string_view name, std::string_view value, int index = 1); - static SQLite::rowid_t AddContextData(SQLite::Connection& connection, std::string_view checkpointName, int contextData, std::string_view name, std::string_view value); - - static std::string GetContextData(SQLite::Connection& connection, std::string_view checkpointName, int contextData, std::string_view name); + // Gets all values for a context data for a checkpoint. + static std::vector GetContextData(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int contextData, std::string_view name); - static void RemoveContextData(SQLite::Connection& connection, std::string_view checkpointName, int contextData); + static void RemoveContextData(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int contextData); }; } \ No newline at end of file diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointIndexInterface.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointIndexInterface.h deleted file mode 100644 index c35ebf701a..0000000000 --- a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointIndexInterface.h +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. -#pragma once -#include "Microsoft/Schema/ICheckpointIndex.h" - -namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 -{ - struct CheckpointIndexInterface : public ICheckpointIndex - { - // Version 1.0 - Schema::Version GetVersion() const override; - void CreateTables(SQLite::Connection& connection) override; - - private: - bool IsEmpty(SQLite::Connection& connection) override; - SQLite::rowid_t SetClientVersion(SQLite::Connection& connection, std::string_view clientVersion) override; - std::string GetClientVersion(SQLite::Connection& connection) override; - SQLite::rowid_t SetCommandName(SQLite::Connection& connection, std::string_view commandName) override; - std::string GetCommandName(SQLite::Connection& connection) override; - SQLite::rowid_t SetCommandArguments(SQLite::Connection& connection, std::string_view commandArguments) override; - std::string GetCommandArguments(SQLite::Connection& connection) override; - SQLite::rowid_t AddContextData(SQLite::Connection& connection, std::string_view checkpointName, int contextData, std::string_view name, std::string_view value) override; - std::string GetContextData(SQLite::Connection& connection, std::string_view checkpointName, int contextData, std::string_view name) override; - void RemoveContextData(SQLite::Connection& connection, std::string_view checkpointName, int contextData) override; - - }; -} \ No newline at end of file diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointIndexInterface_1_0.cpp b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointIndexInterface_1_0.cpp deleted file mode 100644 index 842865aa7e..0000000000 --- a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointIndexInterface_1_0.cpp +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. -#include "pch.h" -#include "Microsoft/Schema/Checkpoint_1_0/CheckpointIndexInterface.h" -#include "Microsoft/Schema/Checkpoint_1_0/CheckpointContextTable.h" -#include "Microsoft/Schema/Checkpoint_1_0/CheckpointMetadataTable.h" - -namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 -{ - Schema::Version CheckpointIndexInterface::GetVersion() const - { - return { 1, 0 }; - } - - void CheckpointIndexInterface::CreateTables(SQLite::Connection& connection) - { - SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "createCheckpointTables_v1_0"); - Checkpoint_V1_0::CheckpointContextTable::Create(connection); - Checkpoint_V1_0::CheckpointMetadataTable::Create(connection); - savepoint.Commit(); - } - - SQLite::rowid_t CheckpointIndexInterface::SetClientVersion(SQLite::Connection& connection, std::string_view clientVersion) - { - SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "setClientVersion_v1_0"); - SQLite::rowid_t argumentId = CheckpointMetadataTable::SetClientVersion(connection, clientVersion); - savepoint.Commit(); - return argumentId; - } - - std::string CheckpointIndexInterface::GetClientVersion(SQLite::Connection& connection) - { - return CheckpointMetadataTable::GetClientVersion(connection); - } - - SQLite::rowid_t CheckpointIndexInterface::SetCommandName(SQLite::Connection& connection, std::string_view commandName) - { - SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "setCommandName_v1_0"); - SQLite::rowid_t id = CheckpointMetadataTable::SetCommandName(connection, commandName); - savepoint.Commit(); - return id; - } - - std::string CheckpointIndexInterface::GetCommandName(SQLite::Connection& connection) - { - return CheckpointMetadataTable::GetCommandName(connection); - } - - SQLite::rowid_t CheckpointIndexInterface::SetCommandArguments(SQLite::Connection& connection, std::string_view commandArguments) - { - SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "setCommandName_v1_0"); - SQLite::rowid_t id = CheckpointMetadataTable::SetCommandArguments(connection, commandArguments); - savepoint.Commit(); - return id; - } - - std::string CheckpointIndexInterface::GetCommandArguments(SQLite::Connection& connection) - { - return CheckpointMetadataTable::GetCommandArguments(connection); - } - - bool CheckpointIndexInterface::IsEmpty(SQLite::Connection& connection) - { - return CheckpointContextTable::IsEmpty(connection); - } - - SQLite::rowid_t CheckpointIndexInterface::AddContextData(SQLite::Connection& connection, std::string_view checkpointName, int contextData, std::string_view name, std::string_view value) - { - SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "addContextData_v1_0"); - SQLite::rowid_t rowId = CheckpointContextTable::AddContextData(connection, checkpointName, contextData, name, value); - savepoint.Commit(); - return rowId; - } - - std::string CheckpointIndexInterface::GetContextData(SQLite::Connection& connection, std::string_view checkpointName, int contextData, std::string_view name) - { - return CheckpointContextTable::GetContextData(connection, checkpointName, contextData, name); - } - - void CheckpointIndexInterface::RemoveContextData(SQLite::Connection& connection, std::string_view checkpointName, int contextData) - { - SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "removeContextData_v1_0"); - CheckpointContextTable::RemoveContextData(connection, checkpointName, contextData); - savepoint.Commit(); - } -} \ No newline at end of file diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointMetadataTable.cpp b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointMetadataTable.cpp index 7af76e101c..ee67278a44 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointMetadataTable.cpp +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointMetadataTable.cpp @@ -3,7 +3,6 @@ #include "pch.h" #include "CheckpointMetadataTable.h" #include "SQLiteStatementBuilder.h" -#include "Microsoft/Schema/ICheckpointIndex.h" namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 { @@ -12,36 +11,6 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 static constexpr std::string_view s_CheckpointMetadataTable_Name_Column = "Name"; static constexpr std::string_view s_CheckpointMetadataTable_Value_Column = "Value"; - static constexpr std::string_view s_CheckpointMetadataTable_ClientVersion = "ClientVersion"sv; - static constexpr std::string_view s_CheckpointMetadataTable_CommandName = "CommandName"sv; - static constexpr std::string_view s_CheckpointMetadataTable_CommandArguments = "CommandArguments"sv; - - namespace - { - SQLite::rowid_t SetNamedValue(SQLite::Connection& connection, std::string_view name, std::string_view value) - { - SQLite::Builder::StatementBuilder builder; - builder.InsertInto(s_CheckpointMetadataTable_Table_Name) - .Columns({ s_CheckpointMetadataTable_Name_Column, - s_CheckpointMetadataTable_Value_Column }) - .Values(name, value); - - builder.Execute(connection); - return connection.GetLastInsertRowID(); - } - - std::string GetNamedValue(SQLite::Connection& connection, std::string_view name) - { - SQLite::Builder::StatementBuilder builder; - builder.Select({ s_CheckpointMetadataTable_Value_Column }) - .From(s_CheckpointMetadataTable_Table_Name).Where(s_CheckpointMetadataTable_Name_Column).Equals(name); - - SQLite::Statement statement = builder.Prepare(connection); - THROW_HR_IF(E_NOT_SET, !statement.Step()); - return statement.GetColumn(0); - } - } - std::string_view CheckpointMetadataTable::TableName() { return s_CheckpointMetadataTable_Table_Name; @@ -63,33 +32,26 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 savepoint.Commit(); } - SQLite::rowid_t CheckpointMetadataTable::SetClientVersion(SQLite::Connection& connection, std::string_view clientVersion) + std::string CheckpointMetadataTable::GetNamedValue(SQLite::Connection& connection, std::string_view name) { - return SetNamedValue(connection, s_CheckpointMetadataTable_ClientVersion, clientVersion); - } + SQLite::Builder::StatementBuilder builder; + builder.Select({ s_CheckpointMetadataTable_Value_Column }) + .From(s_CheckpointMetadataTable_Table_Name).Where(s_CheckpointMetadataTable_Name_Column).Equals(name); - std::string CheckpointMetadataTable::GetClientVersion(SQLite::Connection& connection) - { - return GetNamedValue(connection, s_CheckpointMetadataTable_ClientVersion); + SQLite::Statement statement = builder.Prepare(connection); + THROW_HR_IF(E_NOT_SET, !statement.Step()); + return statement.GetColumn(0); } - SQLite::rowid_t CheckpointMetadataTable::SetCommandName(SQLite::Connection& connection, std::string_view commandName) + SQLite::rowid_t CheckpointMetadataTable::SetNamedValue(SQLite::Connection& connection, std::string_view name, std::string_view value) { - return SetNamedValue(connection, s_CheckpointMetadataTable_CommandName, commandName); - } - - std::string CheckpointMetadataTable::GetCommandName(SQLite::Connection& connection) - { - return GetNamedValue(connection, s_CheckpointMetadataTable_CommandName); - } + SQLite::Builder::StatementBuilder builder; + builder.InsertInto(s_CheckpointMetadataTable_Table_Name) + .Columns({ s_CheckpointMetadataTable_Name_Column, + s_CheckpointMetadataTable_Value_Column }) + .Values(name, value); - SQLite::rowid_t CheckpointMetadataTable::SetCommandArguments(SQLite::Connection& connection, std::string_view commandArguments) - { - return SetNamedValue(connection, s_CheckpointMetadataTable_CommandArguments, commandArguments); - } - - std::string CheckpointMetadataTable::GetCommandArguments(SQLite::Connection& connection) - { - return GetNamedValue(connection, s_CheckpointMetadataTable_CommandArguments); + builder.Execute(connection); + return connection.GetLastInsertRowID(); } } \ No newline at end of file diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointMetadataTable.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointMetadataTable.h index 88040d100c..74756b1255 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointMetadataTable.h +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointMetadataTable.h @@ -3,7 +3,7 @@ #pragma once #include "SQLiteWrapper.h" #include "SQLiteStatementBuilder.h" -#include "Microsoft/Schema/ICheckpointIndex.h" +#include "Microsoft/Schema/ICheckpointRecord.h" #include namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 @@ -16,22 +16,10 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 // Creates the table with named indices. static void Create(SQLite::Connection& connection); - // Gets the client version of the saved state. - static std::string GetClientVersion(SQLite::Connection& connection); + // Gets the metadata value of the checkpoint state. + static std::string GetNamedValue(SQLite::Connection& connection, std::string_view name); - // Sets the client version of the saved state. - static SQLite::rowid_t SetClientVersion(SQLite::Connection& connection, std::string_view clientVersion); - - // Gets the command name of the saved state. - static std::string GetCommandName(SQLite::Connection& connection); - - // Sets the command name of the saved state. - static SQLite::rowid_t SetCommandName(SQLite::Connection& connection, std::string_view commandName); - - // Gets the command arguments of the saved state. - static std::string GetCommandArguments(SQLite::Connection& connection); - - // Sets the command name of the saved state. - static SQLite::rowid_t SetCommandArguments(SQLite::Connection& connection, std::string_view commandArguments); + // Sets the metadata value of the checkpoint state. + static SQLite::rowid_t SetNamedValue(SQLite::Connection& connection, std::string_view name, std::string_view value); }; } \ No newline at end of file diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointRecordInterface.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointRecordInterface.h new file mode 100644 index 0000000000..74bc23b2e5 --- /dev/null +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointRecordInterface.h @@ -0,0 +1,26 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once +#include "Microsoft/Schema/ICheckpointRecord.h" + +namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 +{ + struct CheckpointRecordInterface : public ICheckpointRecord + { + // Version 1.0 + Schema::Version GetVersion() const override; + void CreateTables(SQLite::Connection& connection) override; + + private: + bool IsEmpty(SQLite::Connection& connection) override; + SQLite::rowid_t SetMetadata(SQLite::Connection& connection, std::string_view name, std::string_view value) override; + std::string GetMetadata(SQLite::Connection& connection, std::string_view name) override; + + SQLite::rowid_t AddCheckpoint(SQLite::Connection& connection, std::string_view checkpointName) override; + std::optional GetCheckpointId(SQLite::Connection& connection, std::string_view checkpointName) override; + SQLite::rowid_t AddContextData(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int contextData, std::string_view name, std::string_view value, int index = 0) override; + + std::vector GetContextData(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int contextData, std::string_view name) override; + void RemoveContextData(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int contextData) override; + }; +} \ No newline at end of file diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointRecordInterface_1_0.cpp b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointRecordInterface_1_0.cpp new file mode 100644 index 0000000000..e32caeafc0 --- /dev/null +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointRecordInterface_1_0.cpp @@ -0,0 +1,76 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#include "pch.h" +#include "Microsoft/Schema/Checkpoint_1_0/CheckpointRecordInterface.h" +#include "Microsoft/Schema/Checkpoint_1_0/CheckpointContextTable.h" +#include "Microsoft/Schema/Checkpoint_1_0/CheckpointMetadataTable.h" +#include "Microsoft/Schema/Checkpoint_1_0/CheckpointTable.h" + +namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 +{ + Schema::Version CheckpointRecordInterface::GetVersion() const + { + return { 1, 0 }; + } + + void CheckpointRecordInterface::CreateTables(SQLite::Connection& connection) + { + SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "createCheckpointTables_v1_0"); + Checkpoint_V1_0::CheckpointContextTable::Create(connection); + Checkpoint_V1_0::CheckpointMetadataTable::Create(connection); + Checkpoint_V1_0::CheckpointTable::Create(connection); + savepoint.Commit(); + } + + SQLite::rowid_t CheckpointRecordInterface::SetMetadata(SQLite::Connection& connection, std::string_view name, std::string_view value) + { + SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "setMetadata_v1_0"); + SQLite::rowid_t argumentId = CheckpointMetadataTable::SetNamedValue(connection, name, value); + savepoint.Commit(); + return argumentId; + } + + std::string CheckpointRecordInterface::GetMetadata(SQLite::Connection& connection, std::string_view name) + { + return CheckpointMetadataTable::GetNamedValue(connection, name); + } + + bool CheckpointRecordInterface::IsEmpty(SQLite::Connection& connection) + { + return CheckpointContextTable::IsEmpty(connection); + } + + SQLite::rowid_t CheckpointRecordInterface::AddCheckpoint(SQLite::Connection& connection, std::string_view checkpointName) + { + SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "addCheckpoint_v1_0"); + SQLite::rowid_t checkpointId = CheckpointTable::AddCheckpoint(connection, checkpointName); + savepoint.Commit(); + return checkpointId; + } + + std::optional CheckpointRecordInterface::GetCheckpointId(SQLite::Connection& connection, std::string_view checkpointName) + { + return CheckpointTable::GetCheckpointId(connection, checkpointName); + } + + + SQLite::rowid_t CheckpointRecordInterface::AddContextData(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int contextData, std::string_view name, std::string_view value, int index) + { + SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "addContextData_v1_0"); + SQLite::rowid_t rowId = CheckpointContextTable::AddContextData(connection, checkpointId, contextData, name, value, index); + savepoint.Commit(); + return rowId; + } + + std::vector CheckpointRecordInterface::GetContextData(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int contextData, std::string_view name) + { + return CheckpointContextTable::GetContextData(connection, checkpointId, contextData, name); + } + + void CheckpointRecordInterface::RemoveContextData(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int contextData) + { + SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "removeContextData_v1_0"); + CheckpointContextTable::RemoveContextData(connection, checkpointId, contextData); + savepoint.Commit(); + } +} \ No newline at end of file diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointTable.cpp b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointTable.cpp new file mode 100644 index 0000000000..d55419528f --- /dev/null +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointTable.cpp @@ -0,0 +1,63 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#include "pch.h" +#include "CheckpointTable.h" +#include "SQLiteStatementBuilder.h" + +namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 +{ + using namespace std::string_view_literals; + static constexpr std::string_view s_CheckpointTable_Table_Name = "Checkpoints"sv; + static constexpr std::string_view s_CheckpointTable_Name_Column = "Name"; + static constexpr std::string_view s_CheckpointTable_WriteTime_Column = "WriteTime"; + + std::string_view CheckpointTable::TableName() + { + return s_CheckpointTable_Table_Name; + } + + void CheckpointTable::Create(SQLite::Connection& connection) + { + using namespace SQLite::Builder; + + SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "createCheckpointTable_v1_0"); + + StatementBuilder createTableBuilder; + createTableBuilder.CreateTable(s_CheckpointTable_Table_Name).BeginColumns(); + createTableBuilder.Column(ColumnBuilder(s_CheckpointTable_Name_Column, Type::Text).Unique()); + createTableBuilder.Column(ColumnBuilder(s_CheckpointTable_WriteTime_Column, Type::Int64)); + createTableBuilder.EndColumns(); + createTableBuilder.Execute(connection); + + savepoint.Commit(); + } + + SQLite::rowid_t CheckpointTable::AddCheckpoint(SQLite::Connection& connection, std::string_view checkpointName) + { + SQLite::Builder::StatementBuilder builder; + builder.InsertInto(s_CheckpointTable_Table_Name) + .Columns({ s_CheckpointTable_Name_Column, + s_CheckpointTable_WriteTime_Column }) + .Values(checkpointName, Utility::GetCurrentUnixEpoch()); + + builder.Execute(connection); + return connection.GetLastInsertRowID(); + } + + std::optional CheckpointTable::GetCheckpointId(SQLite::Connection& connection, std::string_view checkpointName) + { + SQLite::Builder::StatementBuilder builder; + builder.Select(SQLite::RowIDName).From(s_CheckpointTable_Table_Name).Where(s_CheckpointTable_Name_Column).Equals(checkpointName); + + SQLite::Statement select = builder.Prepare(connection); + + if (select.Step()) + { + return select.GetColumn(0); + } + else + { + return {}; + } + } +} \ No newline at end of file diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointTable.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointTable.h new file mode 100644 index 0000000000..479a0c3c26 --- /dev/null +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointTable.h @@ -0,0 +1,25 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once +#include "SQLiteWrapper.h" +#include "SQLiteStatementBuilder.h" +#include "Microsoft/Schema/ICheckpointRecord.h" +#include + +namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 +{ + struct CheckpointTable + { + // Get the table name. + static std::string_view TableName(); + + // Creates the table with named indices. + static void Create(SQLite::Connection& connection); + + // Adds a checkpoint. + static SQLite::rowid_t AddCheckpoint(SQLite::Connection& connection, std::string_view checkpointName); + + // Gets the id of a checkpoint. + static std::optional GetCheckpointId(SQLite::Connection& connection, std::string_view checkpointName); + }; +} \ No newline at end of file diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/ICheckpointIndex.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/ICheckpointIndex.h deleted file mode 100644 index 5e4755ffc0..0000000000 --- a/src/AppInstallerRepositoryCore/Microsoft/Schema/ICheckpointIndex.h +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. -#pragma once -#include "SQLiteWrapper.h" -#include "Microsoft/Schema/Version.h" - -namespace AppInstaller::Repository::Microsoft::Schema -{ - struct ICheckpointIndex - { - virtual ~ICheckpointIndex() = default; - - // Gets the schema version that this index interface is built for. - virtual Schema::Version GetVersion() const = 0; - - // Creates all of the version dependent tables within the database. - virtual void CreateTables(SQLite::Connection& connection) = 0; - - // Version 1.0 - // Returns a bool value indicating whether all checkpoint tables are empty. - virtual bool IsEmpty(SQLite::Connection& connection) = 0; - - // Sets the client version associated with this checkpoint index. - virtual SQLite::rowid_t SetClientVersion(SQLite::Connection& connection, std::string_view clientVersion) = 0; - - // Gets the client version associated with this checkpoint index. - virtual std::string GetClientVersion(SQLite::Connection& connection) = 0; - - // Sets the command name. - virtual SQLite::rowid_t SetCommandName(SQLite::Connection& connection, std::string_view commandName) = 0; - - // Gets the command name. - virtual std::string GetCommandName(SQLite::Connection& connection) = 0; - - // Sets the command arguments. - virtual SQLite::rowid_t SetCommandArguments(SQLite::Connection& connection, std::string_view commandArguments) = 0; - - // Gets the command arguments. - virtual std::string GetCommandArguments(SQLite::Connection& connection) = 0; - - // Adds the context data property for a given checkpoint. - virtual SQLite::rowid_t AddContextData(SQLite::Connection& connection, std::string_view checkpointName, int contextData, std::string_view name, std::string_view value) = 0; - - // Gets the context data property for a given checkpoint. - virtual std::string GetContextData(SQLite::Connection& connection, std::string_view checkpointName, int contextData, std::string_view name) = 0; - - // Removes the context data for a given checkpoint. - virtual void RemoveContextData(SQLite::Connection& connection, std::string_view checkpointName, int contextData) = 0; - }; -} \ No newline at end of file diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/ICheckpointRecord.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/ICheckpointRecord.h new file mode 100644 index 0000000000..45dc9ceb59 --- /dev/null +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/ICheckpointRecord.h @@ -0,0 +1,42 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once +#include "SQLiteWrapper.h" +#include "Microsoft/Schema/Version.h" + +namespace AppInstaller::Repository::Microsoft::Schema +{ + struct ICheckpointRecord + { + virtual ~ICheckpointRecord() = default; + + // Gets the schema version that this index interface is built for. + virtual Schema::Version GetVersion() const = 0; + + // Creates all of the version dependent tables within the database. + virtual void CreateTables(SQLite::Connection& connection) = 0; + + // Version 1.0 + // Returns a bool value indicating whether all checkpoint tables are empty. + virtual bool IsEmpty(SQLite::Connection& connection) = 0; + + // Sets the metadata value. + virtual SQLite::rowid_t SetMetadata(SQLite::Connection& connection, std::string_view name, std::string_view value) = 0; + + // Gets the metadata value. + virtual std::string GetMetadata(SQLite::Connection& connection, std::string_view name) = 0; + + virtual SQLite::rowid_t AddCheckpoint(SQLite::Connection& connection, std::string_view checkpointName) = 0; + + virtual std::optional GetCheckpointId(SQLite::Connection& connection, std::string_view checkpointName) = 0; + + // Adds the context data property for a given checkpoint. + virtual SQLite::rowid_t AddContextData(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int contextData, std::string_view name, std::string_view value, int index) = 0; + + // Gets the context data property for a given checkpoint. + virtual std::vector GetContextData(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int contextData, std::string_view name) = 0; + + // Removes the context data for a given checkpoint. + virtual void RemoveContextData(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int contextData) = 0; + }; +} \ No newline at end of file diff --git a/src/AppInstallerSharedLib/Errors.cpp b/src/AppInstallerSharedLib/Errors.cpp index 7b5172f5e8..aa2c3e6901 100644 --- a/src/AppInstallerSharedLib/Errors.cpp +++ b/src/AppInstallerSharedLib/Errors.cpp @@ -230,8 +230,6 @@ namespace AppInstaller return "Failed to download package dependencies."; case APPINSTALLER_CLI_ERROR_DOWNLOAD_COMMAND_PROHIBITED: return "Failed to download package. Download for offline installation is prohibited."; - case APPINSTALLER_CLI_ERROR_INVALID_RESUME_ID: - return "Argument for resume guid is invalid."; case APPINSTALLER_CLI_ERROR_RESUME_ID_NOT_FOUND: return "The guid provided does not correspond to a valid resume state."; case APPINSTALLER_CLI_ERROR_CLIENT_VERSION_MISMATCH: diff --git a/src/AppInstallerSharedLib/Public/AppInstallerErrors.h b/src/AppInstallerSharedLib/Public/AppInstallerErrors.h index 67ddbd0dd5..c294fab353 100644 --- a/src/AppInstallerSharedLib/Public/AppInstallerErrors.h +++ b/src/AppInstallerSharedLib/Public/AppInstallerErrors.h @@ -121,11 +121,10 @@ #define APPINSTALLER_CLI_ERROR_APPTERMINATION_RECEIVED ((HRESULT)0x8A15006A) #define APPINSTALLER_CLI_ERROR_DOWNLOAD_DEPENDENCIES ((HRESULT)0x8A15006B) #define APPINSTALLER_CLI_ERROR_DOWNLOAD_COMMAND_PROHIBITED ((HRESULT)0x8A15006C) -#define APPINSTALLER_CLI_ERROR_INVALID_RESUME_ID ((HRESULT)0x8A15006D) -#define APPINSTALLER_CLI_ERROR_RESUME_ID_NOT_FOUND ((HRESULT)0x8A15006E) -#define APPINSTALLER_CLI_ERROR_CLIENT_VERSION_MISMATCH ((HRESULT)0x8A15006F) -#define APPINSTALLER_CLI_ERROR_INVALID_RESUME_STATE ((HRESULT)0x8A150070) -#define APPINSTALLER_CLI_ERROR_CANNOT_OPEN_CHECKPOINT_INDEX ((HRESULT)0x8A150071) +#define APPINSTALLER_CLI_ERROR_RESUME_ID_NOT_FOUND ((HRESULT)0x8A15006D) +#define APPINSTALLER_CLI_ERROR_CLIENT_VERSION_MISMATCH ((HRESULT)0x8A15006E) +#define APPINSTALLER_CLI_ERROR_INVALID_RESUME_STATE ((HRESULT)0x8A15006F) +#define APPINSTALLER_CLI_ERROR_CANNOT_OPEN_CHECKPOINT_INDEX ((HRESULT)0x8A150070) // Install errors. #define APPINSTALLER_CLI_ERROR_INSTALL_PACKAGE_IN_USE ((HRESULT)0x8A150101) From 623e26cb4f446fe93f415c10ffb751c25a9b23ae Mon Sep 17 00:00:00 2001 From: Ryan Fu Date: Tue, 22 Aug 2023 09:24:22 -0700 Subject: [PATCH 23/43] save work --- src/AppInstallerCLICore/CheckpointManager.cpp | 48 ++++++---- src/AppInstallerCLICore/CheckpointManager.h | 42 ++++++--- src/AppInstallerCLICore/Command.cpp | 2 +- .../Commands/InstallCommand.cpp | 9 +- .../Commands/ResumeCommand.cpp | 11 +-- src/AppInstallerCLICore/ExecutionContext.cpp | 87 ++++++++++++++---- src/AppInstallerCLICore/ExecutionContext.h | 15 +++- .../Workflows/WorkflowBase.cpp | 8 ++ .../AppInstallerCLITests.vcxproj | 2 +- .../AppInstallerCLITests.vcxproj.filters | 2 +- src/AppInstallerCLITests/CheckpointIndex.cpp | 90 ------------------- src/AppInstallerCLITests/CheckpointRecord.cpp | 90 +++++++++++++++++++ src/AppInstallerCLITests/ResumeFlow.cpp | 16 ++-- src/AppInstallerCLITests/TestHooks.h | 15 ---- .../Microsoft/CheckpointRecord.cpp | 59 +++++------- .../Microsoft/CheckpointRecord.h | 31 +++++-- .../Checkpoint_1_0/CheckpointContextTable.cpp | 44 +++++++-- .../Checkpoint_1_0/CheckpointContextTable.h | 11 ++- .../CheckpointRecordInterface.h | 13 +-- .../CheckpointRecordInterface_1_0.cpp | 90 +++++++++++++++---- .../Schema/Checkpoint_1_0/CheckpointTable.cpp | 18 ++++ .../Schema/Checkpoint_1_0/CheckpointTable.h | 2 + .../Microsoft/Schema/ICheckpointRecord.h | 25 ++++-- .../SQLiteStatementBuilder.cpp | 12 +++ .../SQLiteStatementBuilder.h | 4 + 25 files changed, 485 insertions(+), 261 deletions(-) delete mode 100644 src/AppInstallerCLITests/CheckpointIndex.cpp create mode 100644 src/AppInstallerCLITests/CheckpointRecord.cpp diff --git a/src/AppInstallerCLICore/CheckpointManager.cpp b/src/AppInstallerCLICore/CheckpointManager.cpp index 931ceff8cb..c0b3ab13ab 100644 --- a/src/AppInstallerCLICore/CheckpointManager.cpp +++ b/src/AppInstallerCLICore/CheckpointManager.cpp @@ -10,39 +10,43 @@ namespace AppInstaller::CLI::Checkpoint void CheckpointManager::CreateRecord() { - THROW_HR_IF(E_UNEXPECTED, IsLoaded()); std::ignore = CoCreateGuid(&m_checkpointId); AICLI_LOG(CLI, Info, << "Creating checkpoint index with id: " << m_checkpointId); - m_checkpointRecord = CheckpointRecord::CreateDefault(m_checkpointId); + const auto& indexPath = CheckpointRecord::GetCheckpointRecordPath(m_checkpointId); + m_checkpointRecord = std::make_shared(CheckpointRecord::CreateNew(indexPath.u8string())); } - CheckpointManager::CheckpointManager() + void CheckpointManager::LoadRecord(GUID id) { - m_checkpointId = {}; + m_checkpointId = id; + AICLI_LOG(CLI, Info, << "Opening checkpoint index with id: " << m_checkpointId); + const auto& indexPath = CheckpointRecord::GetCheckpointRecordPath(m_checkpointId); + m_checkpointRecord = std::make_shared(CheckpointRecord::Open(indexPath.u8string())); } - void CheckpointManager::LoadExistingRecord(GUID id) + bool CheckpointManager::Exists(std::string_view checkpointName) { - m_checkpointId = id; - AICLI_LOG(CLI, Info, << "Opening checkpoint index with id: " << m_checkpointId); - m_checkpointRecord = CheckpointRecord::OpenDefault(m_checkpointId); + return m_checkpointRecord->CheckpointExists(checkpointName); } std::string CheckpointManager::GetClientVersion() { - THROW_HR_IF(E_UNEXPECTED, !IsLoaded()); return m_checkpointRecord->GetMetadata(CheckpointMetadata::ClientVersion); } std::string CheckpointManager::GetCommandName() { - THROW_HR_IF(E_UNEXPECTED, !IsLoaded()); return m_checkpointRecord->GetMetadata(CheckpointMetadata::CommandName); } - std::string CheckpointManager::GetArguments() + std::string CheckpointManager::GetLastCheckpoint() { - return {}; + return m_checkpointRecord->GetLastCheckpoint(); + } + + std::vector CheckpointManager::GetAvailableContextData(std::string_view checkpointName) + { + return m_checkpointRecord->GetAvailableData(checkpointName); } void CheckpointManager::SetClientVersion(std::string_view value) @@ -57,15 +61,25 @@ namespace AppInstaller::CLI::Checkpoint void CheckpointManager::AddContextData(std::string_view checkpointName, int contextData, std::string_view name, std::string_view value, int index) { - const auto& checkpointId = m_checkpointRecord->GetCheckpointId(checkpointName); - - if (checkpointId) + if (!Exists(checkpointName)) { - m_checkpointRecord->AddContextData(checkpointId.value(), contextData, name, value, index); + m_checkpointRecord->AddCheckpoint(checkpointName); } + + m_checkpointRecord->AddContextData(checkpointName, contextData, name, value, index); + } + + std::vector CheckpointManager::GetContextData(std::string_view checkpointName, int contextData) + { + return m_checkpointRecord->GetContextData(checkpointName, contextData); + } + + std::vector CheckpointManager::GetContextDataByName(std::string_view checkpointName, int contextData, std::string_view name) + { + return m_checkpointRecord->GetContextDataByName(checkpointName, contextData, name); } - void CheckpointManager::CleanUpIndex() + void CheckpointManager::DeleteRecord() { if (m_checkpointRecord) { diff --git a/src/AppInstallerCLICore/CheckpointManager.h b/src/AppInstallerCLICore/CheckpointManager.h index 650c8469a3..5d0a3c68c5 100644 --- a/src/AppInstallerCLICore/CheckpointManager.h +++ b/src/AppInstallerCLICore/CheckpointManager.h @@ -13,36 +13,50 @@ namespace AppInstaller::CLI::Checkpoint { struct CheckpointManager { - CheckpointManager(); - + // Returns a bool value indicating whether a record has been loaded. bool IsLoaded() { return m_checkpointRecord ? true: false; }; + // Creates a new checkpoint record. void CreateRecord(); // Loads an existing record from an id. - void LoadExistingRecord(GUID id); + void LoadRecord(GUID id); + + // Gets a boolean value indicating whether the checkpoint name exists in the record. + bool Exists(std::string_view checkpointName); + + // Sets the client version. + void SetClientVersion(std::string_view value); - // Gets the client version from the checkpoint index. + // Gets the client version from the checkpoint record. std::string GetClientVersion(); - // Gets the command name from the checkpoint index. - std::string GetCommandName(); + // Sets the command name. + void SetCommandName(std::string_view value); - // Gets the command arguments from the checkpoint index. - std::string GetArguments(); + // Gets the command name from the checkpoint record. + std::string GetCommandName(); - void SetClientVersion(std::string_view value); + // Gets the latest checkpoint. + std::string GetLastCheckpoint(); - void SetCommandName(std::string_view value); + // Gets the available context data items for a given checkpoint. + std::vector GetAvailableContextData(std::string_view checkpointName); // Adds a context data to the checkpoint record. - void AddContextData(std::string_view checkpointName, int contextData, std::string_view name, std::string_view value, int index); + void AddContextData(std::string_view checkpointName, int contextData, std::string_view name, std::string_view value, int record); + + // Gets the values associated with a context data + std::vector GetContextData(std::string_view checkpointName, int contextData); + + // Gets the values by property name associated with a context data. + std::vector GetContextDataByName(std::string_view checkpointName, int contextData, std::string_view name); - // Releases and deletes the checkpoint index. - void CleanUpIndex(); + // Releases and deletes the checkpoint record. + void DeleteRecord(); private: - GUID m_checkpointId; + GUID m_checkpointId = {}; std::shared_ptr m_checkpointRecord; }; } \ No newline at end of file diff --git a/src/AppInstallerCLICore/Command.cpp b/src/AppInstallerCLICore/Command.cpp index 49085470a6..7fcb31a079 100644 --- a/src/AppInstallerCLICore/Command.cpp +++ b/src/AppInstallerCLICore/Command.cpp @@ -857,7 +857,7 @@ namespace AppInstaller::CLI { if (!context.Args.Contains(Execution::Args::Type::ResumeId)) { - context.Checkpoint("Start"sv, {}); + context.Checkpoint("Start"sv); } } diff --git a/src/AppInstallerCLICore/Commands/InstallCommand.cpp b/src/AppInstallerCLICore/Commands/InstallCommand.cpp index a5e73a94e5..570ed2eccf 100644 --- a/src/AppInstallerCLICore/Commands/InstallCommand.cpp +++ b/src/AppInstallerCLICore/Commands/InstallCommand.cpp @@ -106,10 +106,10 @@ namespace AppInstaller::CLI void InstallCommand::Resume(Context& context) const { - // Move this to the resume command to load arguments. - //auto commandLine = context.CheckpointManager.GetArguments(); - //Invocation invocation{ std::move(commandLine) }; - //ParseArguments(invocation, context.Args); + const auto& lastCheckpoint = context.CheckpointManager.GetLastCheckpoint(); + context.SetTargetCheckpoint(lastCheckpoint); + + // TODO: Load context data from checkpoint for install command. context.SetFlags(Execution::ContextFlag::Resume); ExecuteInternal(context); @@ -125,7 +125,6 @@ namespace AppInstaller::CLI Workflow::ReportExecutionStage(ExecutionStage::Discovery) << Workflow::GetManifestFromArg << Workflow::SelectInstaller << - Workflow::Checkpoint("InstallerSelected"sv, {Execution::Data::Installer}) << Workflow::EnsureApplicableInstaller << Workflow::InstallSinglePackage; } diff --git a/src/AppInstallerCLICore/Commands/ResumeCommand.cpp b/src/AppInstallerCLICore/Commands/ResumeCommand.cpp index 70ae997c64..6963b54cec 100644 --- a/src/AppInstallerCLICore/Commands/ResumeCommand.cpp +++ b/src/AppInstallerCLICore/Commands/ResumeCommand.cpp @@ -49,19 +49,16 @@ namespace AppInstaller::CLI } Execution::Context resumeContext = context.CreateEmptyContext(); + resumeContext.CheckpointManager.LoadRecord(checkpointId); - CheckpointManager checkpointManager = resumeContext.CheckpointManager; - checkpointManager.LoadExistingRecord(checkpointId); - - - const auto& checkpointClientVersion = checkpointManager.GetClientVersion(); + const auto& checkpointClientVersion = resumeContext.CheckpointManager.GetClientVersion(); if (checkpointClientVersion != AppInstaller::Runtime::GetClientVersion().get()) { context.Reporter.Error() << Resource::String::ClientVersionMismatchError(Utility::LocIndView{ checkpointClientVersion }) << std::endl; AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_CLIENT_VERSION_MISMATCH); } - std::string commandName = checkpointManager.GetCommandName(); + std::string commandName = resumeContext.CheckpointManager.GetCommandName(); std::unique_ptr commandToResume; // Find the command using the root command. @@ -80,7 +77,7 @@ namespace AppInstaller::CLI resumeContext.SetExecutingCommand(commandToResume.get()); resumeContext.SetFlags(Execution::ContextFlag::Resume); - // Load arguments here: + resumeContext.Checkpoint("Start"sv); auto previousThreadGlobals = resumeContext.SetForCurrentThread(); resumeContext.EnableSignalTerminationHandler(); diff --git a/src/AppInstallerCLICore/ExecutionContext.cpp b/src/AppInstallerCLICore/ExecutionContext.cpp index ffd94fce18..cae5af020e 100644 --- a/src/AppInstallerCLICore/ExecutionContext.cpp +++ b/src/AppInstallerCLICore/ExecutionContext.cpp @@ -255,7 +255,7 @@ namespace AppInstaller::CLI::Execution { if (Settings::ExperimentalFeature::IsEnabled(ExperimentalFeature::Feature::Resume) && !IsTerminated()) { - CheckpointManager.CleanUpIndex(); + CheckpointManager.DeleteRecord(); } if (m_disableSignalTerminationHandlerOnExit) @@ -421,32 +421,87 @@ namespace AppInstaller::CLI::Execution } #endif - void Context::Checkpoint(std::string_view checkpointName, std::vector contextData) + bool Context::IsCurrentCheckpointAtTarget() { - CheckpointManager.CreateRecord(); + return m_currentCheckpoint == m_targetCheckpoint; + } - if (contextData.empty()) + void Context::Checkpoint(std::string_view checkpointName, std::vector contextData) + { + // Create record if it does not exist. + if (!CheckpointManager.IsLoaded()) { - // Capture automatic metadata. - CheckpointManager.SetClientVersion(AppInstaller::Runtime::GetClientVersion()); + CheckpointManager.CreateRecord(); + } - const auto& executingCommand = m_executingCommand; - if (executingCommand != nullptr) + if (CheckpointManager.Exists(checkpointName)) + { + if (contextData.empty()) + { + // Load arguments if there is no context data + const auto& availableData = CheckpointManager.GetAvailableContextData(checkpointName); + for (auto data : availableData) + { + const auto& values = CheckpointManager.GetContextData(checkpointName, data); + Execution::Args::Type type = static_cast(data); + if (values.empty()) + { + Args.AddArg(type); + } + else + { + for (const auto& value : values) + { + Args.AddArg(type, value); + } + } + } + } + else { - CheckpointManager.SetCommandName(executingCommand->Name()); + // Load context data from checkpoint. } - - // Capture arguments - } else { - // Capture context data. + if (contextData.empty()) + { + CheckpointManager.SetClientVersion(AppInstaller::Runtime::GetClientVersion()); + + const auto& executingCommand = m_executingCommand; + if (executingCommand != nullptr) + { + CheckpointManager.SetCommandName(executingCommand->Name()); + } + + const auto& argTypes = Args.GetTypes(); + + for (auto type : argTypes) + { + const auto& argName = Argument::ForType(type).Name(); + const auto& values = *Args.GetArgs(type); + int index = 0; + if (values.empty()) + { + CheckpointManager.AddContextData(checkpointName, static_cast(type), argName, {}, index); + } + else + { + for (const auto& value : values) + { + CheckpointManager.AddContextData(checkpointName, static_cast(type), argName, value, index); + index++; + } + } + } + } + else + { + // TODO: Capture context data. + } } - // TODO: Implementation for capturing each context data specified in the provided vector. - UNREFERENCED_PARAMETER(checkpointName); - UNREFERENCED_PARAMETER(contextData); + m_currentCheckpoint = checkpointName; } } diff --git a/src/AppInstallerCLICore/ExecutionContext.h b/src/AppInstallerCLICore/ExecutionContext.h index 03834d3027..8f8d7c02e3 100644 --- a/src/AppInstallerCLICore/ExecutionContext.h +++ b/src/AppInstallerCLICore/ExecutionContext.h @@ -166,10 +166,19 @@ namespace AppInstaller::CLI::Execution bool ShouldExecuteWorkflowTask(const Workflow::WorkflowTask& task); #endif + // Interacts with the checkpoint record for writing and loading checkpoints. Checkpoint::CheckpointManager CheckpointManager; - // Records the provided context data to the checkpoint index. If it already exists, loads the context instead. - void Checkpoint(std::string_view checkpointName, std::vector contextData); + // Returns a value indicating whether the current checkpoint matches the target checkpoint. + bool IsCurrentCheckpointAtTarget(); + + // Sets the target checkpoint. + void SetTargetCheckpoint(std::string_view checkpointName) { m_targetCheckpoint = checkpointName; }; + + // Writes the context data to the checkpoint record if the checkpoint does not yet exist. + // If no context data is provided, writes the automatic checkpoint metadata. + // If the checkpoint already exists, loads the context data from the saved checkpoint. + void Checkpoint(std::string_view checkpointName, std::vector contextData = {}); protected: // Copies the args that are also needed in a sub-context. E.g., silent @@ -189,5 +198,7 @@ namespace AppInstaller::CLI::Execution Workflow::ExecutionStage m_executionStage = Workflow::ExecutionStage::Initial; AppInstaller::ThreadLocalStorage::WingetThreadGlobals m_threadGlobals; AppInstaller::CLI::Command* m_executingCommand = nullptr; + std::string_view m_targetCheckpoint = {}; + std::string_view m_currentCheckpoint = {}; }; } diff --git a/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp b/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp index cdd114a516..83a6b813ef 100644 --- a/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp +++ b/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp @@ -1275,6 +1275,14 @@ AppInstaller::CLI::Execution::Context& operator<<(AppInstaller::CLI::Execution:: AppInstaller::CLI::Execution::Context& operator<<(AppInstaller::CLI::Execution::Context& context, const AppInstaller::CLI::Workflow::WorkflowTask& task) { + if (AppInstaller::Settings::ExperimentalFeature::IsEnabled(ExperimentalFeature::Feature::Resume)) + { + if (WI_IsFlagSet(context.GetFlags(), AppInstaller::CLI::Execution::ContextFlag::Resume) && !context.IsCurrentCheckpointAtTarget()) + { + return context; + } + } + if (!context.IsTerminated()) { #ifndef AICLI_DISABLE_TEST_HOOKS diff --git a/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj b/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj index da28cbf34f..587c360a91 100644 --- a/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj +++ b/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj @@ -198,7 +198,7 @@ - + diff --git a/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj.filters b/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj.filters index 640cf04df7..3ffaebee8f 100644 --- a/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj.filters +++ b/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj.filters @@ -317,7 +317,7 @@ Source Files\CLI - + Source Files\Repository diff --git a/src/AppInstallerCLITests/CheckpointIndex.cpp b/src/AppInstallerCLITests/CheckpointIndex.cpp deleted file mode 100644 index d9625d623b..0000000000 --- a/src/AppInstallerCLITests/CheckpointIndex.cpp +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. -#include "pch.h" -#include "TestCommon.h" -#include - -using namespace std::string_literals; -using namespace TestCommon; -using namespace AppInstaller::Repository::Microsoft; -using namespace AppInstaller::Repository::SQLite; -using namespace AppInstaller::Repository::Microsoft::Schema; - -TEST_CASE("CheckpointIndexCreateLatestAndReopen", "[checkpointIndex]") -{ - TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; - INFO("Using temporary file named: " << tempFile.GetPath()); - - Schema::Version versionCreated; - - // Create the index - { - CheckpointIndex index = CheckpointIndex::CreateNew(tempFile, Schema::Version::Latest()); - versionCreated = index.GetVersion(); - } - - // Reopen the index - { - INFO("Trying with ReadWrite"); - CheckpointIndex index = CheckpointIndex::Open(tempFile, SQLiteStorageBase::OpenDisposition::ReadWrite); - Schema::Version versionRead = index.GetVersion(); - REQUIRE(versionRead == versionCreated); - } -} - -TEST_CASE("CheckpointIndex_WriteMetadata", "[checkpointIndex]") -{ - TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; - INFO("Using temporary file named: " << tempFile.GetPath()); - - std::string_view testCommand = "install"sv; - std::string_view testClientVersion = "1.20.1234"sv; - std::string_view testArguments = "install --id Microsoft.PowerToys"; - - { - CheckpointIndex index = CheckpointIndex::CreateNew(tempFile, { 1, 0 }); - index.SetCommandName(testCommand); - index.SetClientVersion(testClientVersion); - index.SetCommandArguments(testArguments); - } - - { - CheckpointIndex index = CheckpointIndex::Open(tempFile, SQLiteStorageBase::OpenDisposition::ReadWrite); - REQUIRE(testCommand == index.GetCommandName()); - REQUIRE(testClientVersion == index.GetClientVersion()); - REQUIRE(testArguments == index.GetCommandArguments()); - } -} - -TEST_CASE("CheckpointIndex_WriteContextData", "[checkpointIndex]") -{ - TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; - INFO("Using temporary file named: " << tempFile.GetPath()); - - std::string_view testCheckpoint = "testCheckpoint"sv; - int testContextData = 3; - std::string_view testName = "testName"; - std::string_view testValue = "testValue"; - - std::string_view testName2 = "exampleName"; - std::string_view testValue2 = "exampleValue"; - - { - CheckpointIndex index = CheckpointIndex::CreateNew(tempFile, { 1, 0 }); - index.AddContextData(testCheckpoint, testContextData, testName, testValue); - index.AddContextData(testCheckpoint, testContextData, testName2, testValue2); - } - - { - CheckpointIndex index = CheckpointIndex::Open(tempFile, SQLiteStorageBase::OpenDisposition::ReadWrite); - REQUIRE(testValue == index.GetContextData(testCheckpoint, testContextData, testName)); - REQUIRE(testValue2 == index.GetContextData(testCheckpoint, testContextData, testName2)); - - index.RemoveContextData(testCheckpoint, testContextData); - } - - { - CheckpointIndex index = CheckpointIndex::Open(tempFile, SQLiteStorageBase::OpenDisposition::ReadWrite); - REQUIRE(index.IsEmpty()); - } -} diff --git a/src/AppInstallerCLITests/CheckpointRecord.cpp b/src/AppInstallerCLITests/CheckpointRecord.cpp new file mode 100644 index 0000000000..aed07cad97 --- /dev/null +++ b/src/AppInstallerCLITests/CheckpointRecord.cpp @@ -0,0 +1,90 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#include "pch.h" +#include "TestCommon.h" +#include + +using namespace std::string_literals; +using namespace TestCommon; +using namespace AppInstaller::Repository::Microsoft; +using namespace AppInstaller::Repository::SQLite; +using namespace AppInstaller::Repository::Microsoft::Schema; + +TEST_CASE("CheckpointRecordCreateLatestAndReopen", "[checkpointRecord]") +{ + TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; + INFO("Using temporary file named: " << tempFile.GetPath()); + + Schema::Version versionCreated; + + // Create the record + { + CheckpointRecord record = CheckpointRecord::CreateNew(tempFile, Schema::Version::Latest()); + versionCreated = record.GetVersion(); + } + + // Reopen the record + { + INFO("Trying with ReadWrite"); + CheckpointRecord record = CheckpointRecord::Open(tempFile, SQLiteStorageBase::OpenDisposition::ReadWrite); + Schema::Version versionRead = record.GetVersion(); + REQUIRE(versionRead == versionCreated); + } +} + +TEST_CASE("CheckpointRecord_WriteMetadata", "[checkpointRecord]") +{ + TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; + INFO("Using temporary file named: " << tempFile.GetPath()); + + std::string_view testCommand = "install"sv; + std::string_view testClientVersion = "1.20.1234"sv; + std::string_view testArguments = "install --id Microsoft.PowerToys"; + + { + CheckpointRecord record = CheckpointRecord::CreateNew(tempFile, { 1, 0 }); + record.SetMetadata(CheckpointMetadata::CommandName, testCommand); + record.SetMetadata(CheckpointMetadata::ClientVersion, testClientVersion); + } + + { + CheckpointRecord record = CheckpointRecord::Open(tempFile, SQLiteStorageBase::OpenDisposition::ReadWrite); + REQUIRE(testCommand == record.GetMetadata(CheckpointMetadata::CommandName)); + REQUIRE(testClientVersion == record.GetMetadata(CheckpointMetadata::ClientVersion)); + } +} + +TEST_CASE("CheckpointRecord_WriteContextData", "[checkpointRecord]") +{ + TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; + INFO("Using temporary file named: " << tempFile.GetPath()); + + std::string_view testCheckpoint = "testCheckpoint"sv; + int testContextData = 3; + std::string_view testName = "testName"; + std::string_view testValue = "testValue"; + + std::string_view testName2 = "exampleName"; + std::string_view testValue2 = "exampleValue"; + + { + CheckpointRecord record = CheckpointRecord::CreateNew(tempFile, { 1, 0 }); + record.AddCheckpoint(testCheckpoint); + record.AddContextData(testCheckpoint, testContextData, testName, testValue, 0); + record.AddContextData(testCheckpoint, testContextData, testName2, testValue2, 1); + } + + { + CheckpointRecord record = CheckpointRecord::Open(tempFile, SQLiteStorageBase::OpenDisposition::ReadWrite); + auto contextData = record.GetContextData(testCheckpoint, testContextData); + REQUIRE(contextData.size() == 2); + REQUIRE(testValue == contextData[0]); + REQUIRE(testValue2 == contextData[1]); + record.RemoveContextData(testCheckpoint, testContextData); + } + + { + CheckpointRecord record = CheckpointRecord::Open(tempFile, SQLiteStorageBase::OpenDisposition::ReadWrite); + REQUIRE(record.IsEmpty()); + } +} diff --git a/src/AppInstallerCLITests/ResumeFlow.cpp b/src/AppInstallerCLITests/ResumeFlow.cpp index 51c6741edc..e6c649ef58 100644 --- a/src/AppInstallerCLITests/ResumeFlow.cpp +++ b/src/AppInstallerCLITests/ResumeFlow.cpp @@ -5,7 +5,7 @@ #include "TestHooks.h" #include #include -#include +#include #include #include #include @@ -15,6 +15,7 @@ using namespace std::string_literals; using namespace AppInstaller::CLI; using namespace AppInstaller::Repository::Microsoft; using namespace AppInstaller::Settings; +using namespace AppInstaller::Runtime; using namespace TestCommon; TEST_CASE("ResumeFlow_InvalidGuid", "[Resume]") @@ -30,7 +31,6 @@ TEST_CASE("ResumeFlow_InvalidGuid", "[Resume]") resume.Execute(context); INFO(resumeOutput.str()); - REQUIRE(context.GetTerminationHR() == APPINSTALLER_CLI_ERROR_INVALID_RESUME_ID); auto expectedMessage = Resource::String::InvalidResumeIdError(AppInstaller::Utility::LocIndString{ "badGuid"s }); REQUIRE(resumeOutput.str().find(Resource::LocString(expectedMessage).get()) != std::string::npos); } @@ -59,7 +59,7 @@ TEST_CASE("ResumeFlow_InvalidClientVersion", "[Resume]") TestCommon::TempDirectory tempCheckpointIndexDirectory("TempCheckpointIndexDirectory", true); const auto& tempCheckpointIndexDirectoryPath = tempCheckpointIndexDirectory.GetPath(); - TestHook::SetCheckpointIndexDirectory_Override checkpointIndexDirectoryOverride(tempCheckpointIndexDirectoryPath); + TestHook_SetPathOverride(PathName::CheckpointsLocation, tempCheckpointIndexDirectoryPath); // Create temp guid and populate with invalid client version. std::string tempGuidString = "{b157d11f-4487-4e03-9447-9f9d50d66d8e}"; @@ -71,7 +71,7 @@ TEST_CASE("ResumeFlow_InvalidClientVersion", "[Resume]") { CheckpointRecord index = CheckpointRecord::CreateNew(tempIndexPath.u8string()); - index.SetClientVersion(invalidClientVersion); + index.SetMetadata(CheckpointMetadata::ClientVersion, invalidClientVersion); } std::ostringstream resumeOutput; @@ -94,7 +94,7 @@ TEST_CASE("ResumeFlow_EmptyIndex", "Resume") TestCommon::TempDirectory tempCheckpointIndexDirectory("TempCheckpointIndexDirectory", false); const auto& tempCheckpointIndexDirectoryPath = tempCheckpointIndexDirectory.GetPath(); - TestHook::SetCheckpointIndexDirectory_Override checkpointIndexDirectoryOverride(tempCheckpointIndexDirectoryPath); + TestHook_SetPathOverride(PathName::CheckpointsLocation, tempCheckpointIndexDirectoryPath); // Create temp guid and populate with invalid client version. std::string tempGuidString = "{43ca664c-3eae-4f73-99ee-18cf83912c02}"; @@ -105,7 +105,7 @@ TEST_CASE("ResumeFlow_EmptyIndex", "Resume") { CheckpointRecord index = CheckpointRecord::CreateNew(tempGuidString); - index.SetClientVersion(AppInstaller::Runtime::GetClientVersion()); + index.SetMetadata(CheckpointMetadata::ClientVersion, AppInstaller::Runtime::GetClientVersion()); } std::ostringstream resumeOutput; @@ -127,7 +127,7 @@ TEST_CASE("ResumeFlow_InstallSuccess", "[Resume]") TestCommon::TempDirectory tempCheckpointIndexDirectory("TempCheckpointIndexDirectory", false); const auto& tempCheckpointIndexDirectoryPath = tempCheckpointIndexDirectory.GetPath(); - TestHook::SetCheckpointIndexDirectory_Override checkpointIndexDirectoryOverride(tempCheckpointIndexDirectoryPath); + TestHook_SetPathOverride(PathName::CheckpointsLocation, tempCheckpointIndexDirectoryPath); TestCommon::TestUserSettings testSettings; testSettings.Set(true); @@ -174,7 +174,7 @@ TEST_CASE("ResumeFlow_InstallFailure", "[Resume]") TestCommon::TempDirectory tempCheckpointIndexDirectory("TempCheckpointIndexDirectory", false); const auto& tempCheckpointIndexDirectoryPath = tempCheckpointIndexDirectory.GetPath(); - TestHook::SetCheckpointIndexDirectory_Override checkpointIndexDirectoryOverride(tempCheckpointIndexDirectoryPath); + TestHook_SetPathOverride(PathName::CheckpointsLocation, tempCheckpointIndexDirectoryPath); TestCommon::TestUserSettings testSettings; testSettings.Set(true); diff --git a/src/AppInstallerCLITests/TestHooks.h b/src/AppInstallerCLITests/TestHooks.h index 16bf9698cc..b66aa7a507 100644 --- a/src/AppInstallerCLITests/TestHooks.h +++ b/src/AppInstallerCLITests/TestHooks.h @@ -9,7 +9,6 @@ #include #include -#include #include #include #include @@ -41,7 +40,6 @@ namespace AppInstaller namespace Repository::Microsoft { void TestHook_SetPinningIndex_Override(std::optional&& indexPath); - void TestHook_SetCheckpointIndexDirectory_Override(std::optional&& checkpointIndexDirectory); } namespace Logging @@ -122,19 +120,6 @@ namespace TestHook } }; - struct SetCheckpointIndexDirectory_Override - { - SetCheckpointIndexDirectory_Override(const std::filesystem::path& checkpointIndexDirectoryPath) - { - AppInstaller::Repository::Microsoft::TestHook_SetCheckpointIndexDirectory_Override(checkpointIndexDirectoryPath); - } - - ~SetCheckpointIndexDirectory_Override() - { - AppInstaller::Repository::Microsoft::TestHook_SetCheckpointIndexDirectory_Override({}); - } - }; - struct MockDismHelper_Override { MockDismHelper_Override() diff --git a/src/AppInstallerRepositoryCore/Microsoft/CheckpointRecord.cpp b/src/AppInstallerRepositoryCore/Microsoft/CheckpointRecord.cpp index e2a05fc710..5ff98e0fb1 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/CheckpointRecord.cpp +++ b/src/AppInstallerRepositoryCore/Microsoft/CheckpointRecord.cpp @@ -48,34 +48,6 @@ namespace AppInstaller::Repository::Microsoft return result; } - std::shared_ptr CheckpointRecord::CreateDefault(GUID guid) - { - const auto& indexPath = GetCheckpointRecordPath(guid); - AICLI_LOG(Repo, Info, << "Creating checkpoint index"); - - try - { - return std::make_shared(CheckpointRecord::CreateNew(indexPath.u8string())); - } - CATCH_LOG(); - - return {}; - } - - std::shared_ptr CheckpointRecord::OpenDefault(GUID guid) - { - const auto& indexPath = GetCheckpointRecordPath(guid); - AICLI_LOG(Repo, Info, << "Opening existing checkpoint index"); - - try - { - return std::make_shared(CheckpointRecord::Open(indexPath.u8string(), OpenDisposition::ReadWrite)); - } - CATCH_LOG(); - - return {}; - } - std::filesystem::path CheckpointRecord::GetCheckpointRecordPath(GUID guid) { wchar_t checkpointGuid[256]; @@ -102,6 +74,11 @@ namespace AppInstaller::Repository::Microsoft return m_interface->IsEmpty(m_dbconn); } + std::vector CheckpointRecord::GetAvailableData(std::string_view name) + { + return m_interface->GetAvailableContextData(m_dbconn, name); + } + std::string CheckpointRecord::GetMetadata(CheckpointMetadata checkpointMetadata) { const auto& metadataName = GetCheckpointMetadataString(checkpointMetadata); @@ -137,34 +114,44 @@ namespace AppInstaller::Repository::Microsoft return result; } - std::optional CheckpointRecord::GetCheckpointId(std::string_view checkpointName) + bool CheckpointRecord::CheckpointExists(std::string_view checkpointName) { - return m_interface->GetCheckpointId(m_dbconn, checkpointName); + return m_interface->CheckpointExists(m_dbconn, checkpointName); } - CheckpointRecord::IdType CheckpointRecord::AddContextData(SQLite::rowid_t checkpointId, int contextData, std::string_view name, std::string_view value, int index) + CheckpointRecord::IdType CheckpointRecord::AddContextData(std::string_view checkpointName, int contextData, std::string_view name, std::string_view value, int index) { std::lock_guard lockInterface{ *m_interfaceLock }; AICLI_LOG(Repo, Verbose, << "Setting context data [" << contextData << "] for [" << name << "] with value [" << value << "] value"); SQLite::Savepoint savepoint = SQLite::Savepoint::Create(m_dbconn, "checkpointindex_addcontextdata"); - SQLite::rowid_t rowId = m_interface->AddContextData(m_dbconn, checkpointId, contextData, name, value, index); + SQLite::rowid_t rowId = m_interface->AddContextData(m_dbconn, checkpointName, contextData, name, value, index); savepoint.Commit(); return rowId; } - std::vector CheckpointRecord::GetContextData(SQLite::rowid_t checkpointId, int contextData, std::string_view name) + std::string CheckpointRecord::GetLastCheckpoint() + { + return m_interface->GetLastCheckpoint(m_dbconn); + } + + std::vector CheckpointRecord::GetContextData(std::string_view checkpointName, int contextData) + { + return m_interface->GetContextData(m_dbconn, checkpointName, contextData); + } + + std::vector CheckpointRecord::GetContextDataByName(std::string_view checkpointName, int contextData, std::string_view name) { - return m_interface->GetContextData(m_dbconn, checkpointId, contextData, name); + return m_interface->GetContextDataByName(m_dbconn, checkpointName, contextData, name); } - void CheckpointRecord::RemoveContextData(SQLite::rowid_t checkpointId, int contextData) + void CheckpointRecord::RemoveContextData(std::string_view checkpointName, int contextData) { std::lock_guard lockInterface{ *m_interfaceLock }; AICLI_LOG(Repo, Verbose, << "Removing context data [" << contextData << "]"); SQLite::Savepoint savepoint = SQLite::Savepoint::Create(m_dbconn, "checkpointindex_addcontextdata"); - m_interface->RemoveContextData(m_dbconn, checkpointId, contextData); + m_interface->RemoveContextData(m_dbconn, checkpointName, contextData); savepoint.Commit(); } diff --git a/src/AppInstallerRepositoryCore/Microsoft/CheckpointRecord.h b/src/AppInstallerRepositoryCore/Microsoft/CheckpointRecord.h index c5067838e6..dfae36e88f 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/CheckpointRecord.h +++ b/src/AppInstallerRepositoryCore/Microsoft/CheckpointRecord.h @@ -26,7 +26,7 @@ namespace AppInstaller::Repository::Microsoft CheckpointRecord& operator=(CheckpointRecord&&) = default; // Opens an existing CheckpointRecord database. - static CheckpointRecord Open(const std::string& filePath, OpenDisposition disposition, Utility::ManagedFile&& indexFile = {}) + static CheckpointRecord Open(const std::string& filePath, OpenDisposition disposition = OpenDisposition::ReadWrite, Utility::ManagedFile&& indexFile = {}) { return { filePath, disposition, std::move(indexFile) }; } @@ -34,28 +34,41 @@ namespace AppInstaller::Repository::Microsoft // Create a new CheckpointRecord database. static CheckpointRecord CreateNew(const std::string& filePath, Schema::Version version = Schema::Version::Latest()); - static std::shared_ptr OpenDefault(GUID guid); - - static std::shared_ptr CreateDefault(GUID guid); - // Gets the file path of the CheckpointRecord database. static std::filesystem::path GetCheckpointRecordPath(GUID guid); + // Returns a value indicating whether the record is empty. bool IsEmpty(); + // Gets all available context data for a checkpoint. + std::vector GetAvailableData(std::string_view checkpointName); + + // Gets the specified metadata value. std::string GetMetadata(CheckpointMetadata checkpointMetadata); + // Sets the specified metadata value. IdType SetMetadata(CheckpointMetadata checkpointMetadata, std::string_view value); + // Adds a new checkpoint. IdType AddCheckpoint(std::string_view checkpointName); - std::optional GetCheckpointId(std::string_view checkpointName); + // Gets the latest checkpoint. + std::string GetLastCheckpoint(); + + // Returns a value indicating whether the checkpoint exists. + bool CheckpointExists(std::string_view checkpointName); + + // Adds a context data value. + IdType AddContextData(std::string_view checkpointName, int contextData, std::string_view name, std::string_view value, int index); - IdType AddContextData(SQLite::rowid_t checkpointId, int contextData, std::string_view name, std::string_view value, int index); + // Gets the context data values. + std::vector GetContextData(std::string_view checkpointName, int contextData); - std::vector GetContextData(SQLite::rowid_t checkpointId, int contextData, std::string_view name); + // Gets the context data values by property name. + std::vector GetContextDataByName(std::string_view checkpointName, int contextData, std::string_view name); - void RemoveContextData(SQLite::rowid_t checkpointId, int contextData); + // Removes the context data. + void RemoveContextData(std::string_view checkpointName, int contextData); private: // Constructor used to open an existing index. diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointContextTable.cpp b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointContextTable.cpp index 4b3d69c4fd..68a358b4fc 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointContextTable.cpp +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointContextTable.cpp @@ -61,7 +61,6 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 createTableBuilder.Column(ColumnBuilder(s_CheckpointContextTable_ContextData_Column, Type::Int)); createTableBuilder.Column(ColumnBuilder(s_CheckpointContextTable_Name_Column, Type::Text)); createTableBuilder.Column(ColumnBuilder(s_CheckpointContextTable_Value_Column, Type::Text)); - createTableBuilder.Column(ColumnBuilder(s_CheckpointContextTable_Value_Column, Type::Text)); createTableBuilder.Column(ColumnBuilder(s_CheckpointContextTable_Index_Column, Type::Int)); createTableBuilder.EndColumns(); createTableBuilder.Execute(connection); @@ -80,6 +79,24 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 return (countStatement.GetColumn(0) == 0); } + std::vector CheckpointContextTable::GetAvailableData(SQLite::Connection& connection, SQLite::rowid_t checkpointId) + { + SQLite::Builder::StatementBuilder builder; + builder.Select(s_CheckpointContextTable_ContextData_Column).From(s_CheckpointContextTable_Table_Name).Where(s_CheckpointContextTable_CheckpointId_Column); + builder.Equals(checkpointId); + + SQLite::Statement select = builder.Prepare(connection); + + std::vector availableData; + + while (select.Step()) + { + availableData.emplace_back(select.GetColumn(0)); + } + + return availableData; + } + SQLite::rowid_t CheckpointContextTable::AddContextData(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int contextData, std::string_view name, std::string_view value, int index) { SQLite::Builder::StatementBuilder builder; @@ -95,11 +112,19 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 return connection.GetLastInsertRowID(); } - std::vector CheckpointContextTable::GetContextData(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int contextData, std::string_view name) + void CheckpointContextTable::RemoveContextData(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int contextData) + { + SQLite::Builder::StatementBuilder builder; + builder.DeleteFrom(s_CheckpointContextTable_Table_Name).Where(s_CheckpointContextTable_CheckpointId_Column).Equals(checkpointId) + .And(s_CheckpointContextTable_ContextData_Column).Equals(contextData); + builder.Execute(connection); + } + + std::vector CheckpointContextTable::GetContextData(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int contextData) { SQLite::Builder::StatementBuilder builder; builder.Select(s_CheckpointContextTable_Value_Column).From(s_CheckpointContextTable_Table_Name).Where(s_CheckpointContextTable_CheckpointId_Column); - builder.Equals(checkpointId).And(s_CheckpointContextTable_ContextData_Column).Equals(contextData).And(s_CheckpointContextTable_Name_Column).Equals(name); + builder.Equals(checkpointId).And(s_CheckpointContextTable_ContextData_Column).Equals(contextData); SQLite::Statement select = builder.Prepare(connection); @@ -113,16 +138,21 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 return values; } - void CheckpointContextTable::RemoveContextData(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int contextData) + std::vector CheckpointContextTable::GetContextDataByName(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int contextData, std::string_view name) { SQLite::Builder::StatementBuilder builder; - builder.Select(SQLite::RowIDName).From(s_CheckpointContextTable_Table_Name).Where(s_CheckpointContextTable_CheckpointId_Column).Equals(checkpointId) - .And(s_CheckpointContextTable_ContextData_Column).Equals(contextData); + builder.Select(s_CheckpointContextTable_Value_Column).From(s_CheckpointContextTable_Table_Name).Where(s_CheckpointContextTable_CheckpointId_Column); + builder.Equals(checkpointId).And(s_CheckpointContextTable_ContextData_Column).Equals(contextData).And(s_CheckpointContextTable_Name_Column).Equals(name); SQLite::Statement select = builder.Prepare(connection); + + std::vector values; + while (select.Step()) { - //DeleteById(connection, select.GetColumn(0)); + values.emplace_back(select.GetColumn(0)); } + + return values; } } \ No newline at end of file diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointContextTable.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointContextTable.h index 08f49dd186..e838d637e4 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointContextTable.h +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointContextTable.h @@ -19,12 +19,19 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 // Gets a value indicating whether the table is empty. static bool IsEmpty(SQLite::Connection& connection); + // Get all available context data. + static std::vector GetAvailableData(SQLite::Connection& connection, SQLite::rowid_t checkpointId); + // Adds a context data for a checkpoint. Index is used to represent the item number if the context data has more than one value. static SQLite::rowid_t AddContextData(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int contextData, std::string_view name, std::string_view value, int index = 1); - // Gets all values for a context data for a checkpoint. - static std::vector GetContextData(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int contextData, std::string_view name); + // Gets the context data values from a checkpoint id. + static std::vector GetContextData(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int contextData); + + // Gets the context data values by property name from a checkpoint id. + static std::vector GetContextDataByName(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int contextData, std::string_view name); + // Removes the context data by checkpoint id. static void RemoveContextData(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int contextData); }; } \ No newline at end of file diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointRecordInterface.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointRecordInterface.h index 74bc23b2e5..f2b464a56c 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointRecordInterface.h +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointRecordInterface.h @@ -15,12 +15,13 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 bool IsEmpty(SQLite::Connection& connection) override; SQLite::rowid_t SetMetadata(SQLite::Connection& connection, std::string_view name, std::string_view value) override; std::string GetMetadata(SQLite::Connection& connection, std::string_view name) override; - + std::string GetLastCheckpoint(SQLite::Connection& connection) override; + bool CheckpointExists(SQLite::Connection& connection, std::string_view checkpointName) override; + std::vector GetAvailableContextData(SQLite::Connection& connection, std::string_view checkpointName) override; SQLite::rowid_t AddCheckpoint(SQLite::Connection& connection, std::string_view checkpointName) override; - std::optional GetCheckpointId(SQLite::Connection& connection, std::string_view checkpointName) override; - SQLite::rowid_t AddContextData(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int contextData, std::string_view name, std::string_view value, int index = 0) override; - - std::vector GetContextData(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int contextData, std::string_view name) override; - void RemoveContextData(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int contextData) override; + SQLite::rowid_t AddContextData(SQLite::Connection& connection, std::string_view checkpointName, int contextData, std::string_view name, std::string_view value, int index = 0) override; + std::vector GetContextData(SQLite::Connection& connection, std::string_view checkpointName, int contextData) override; + std::vector GetContextDataByName(SQLite::Connection& connection, std::string_view checkpointName, int contextData, std::string_view name) override; + void RemoveContextData(SQLite::Connection& connection, std::string_view checkpointName, int contextData) override; }; } \ No newline at end of file diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointRecordInterface_1_0.cpp b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointRecordInterface_1_0.cpp index e32caeafc0..cb06190b07 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointRecordInterface_1_0.cpp +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointRecordInterface_1_0.cpp @@ -8,6 +8,21 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 { + namespace + { + std::optional GetExistingCheckpointId(SQLite::Connection& connection, std::string_view checkpointName) + { + auto result = CheckpointTable::GetCheckpointId(connection, checkpointName); + + if (!result) + { + AICLI_LOG(Repo, Verbose, << "Did not find checkpoint " << checkpointName); + } + + return result; + } + } + Schema::Version CheckpointRecordInterface::GetVersion() const { return { 1, 0 }; @@ -22,6 +37,11 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 savepoint.Commit(); } + bool CheckpointRecordInterface::IsEmpty(SQLite::Connection& connection) + { + return CheckpointContextTable::IsEmpty(connection); + } + SQLite::rowid_t CheckpointRecordInterface::SetMetadata(SQLite::Connection& connection, std::string_view name, std::string_view value) { SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "setMetadata_v1_0"); @@ -29,48 +49,84 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 savepoint.Commit(); return argumentId; } - + + bool CheckpointRecordInterface::CheckpointExists(SQLite::Connection& connection, std::string_view checkpointName) + { + return GetExistingCheckpointId(connection, checkpointName).has_value(); + } + std::string CheckpointRecordInterface::GetMetadata(SQLite::Connection& connection, std::string_view name) { return CheckpointMetadataTable::GetNamedValue(connection, name); } - bool CheckpointRecordInterface::IsEmpty(SQLite::Connection& connection) + std::string CheckpointRecordInterface::GetLastCheckpoint(SQLite::Connection& connection) { - return CheckpointContextTable::IsEmpty(connection); + return CheckpointTable::GetLastCheckpoint(connection); + } + + std::vector CheckpointRecordInterface::GetAvailableContextData(SQLite::Connection& connection, std::string_view checkpointName) + { + auto existingCheckpointId = GetExistingCheckpointId(connection, checkpointName); + if (!existingCheckpointId) + { + return {}; + } + + return CheckpointContextTable::GetAvailableData(connection, existingCheckpointId.value()); } SQLite::rowid_t CheckpointRecordInterface::AddCheckpoint(SQLite::Connection& connection, std::string_view checkpointName) { - SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "addCheckpoint_v1_0"); + auto existingCheckpointId = GetExistingCheckpointId(connection, checkpointName); + THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS), existingCheckpointId); + + SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "addcheckpoint_v1_0"); SQLite::rowid_t checkpointId = CheckpointTable::AddCheckpoint(connection, checkpointName); savepoint.Commit(); return checkpointId; } - std::optional CheckpointRecordInterface::GetCheckpointId(SQLite::Connection& connection, std::string_view checkpointName) + SQLite::rowid_t CheckpointRecordInterface::AddContextData(SQLite::Connection& connection, std::string_view checkpointName, int contextData, std::string_view name, std::string_view value, int index) { - return CheckpointTable::GetCheckpointId(connection, checkpointName); - } - + auto existingCheckpointId = GetExistingCheckpointId(connection, checkpointName); + THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_NOT_FOUND), !existingCheckpointId); - SQLite::rowid_t CheckpointRecordInterface::AddContextData(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int contextData, std::string_view name, std::string_view value, int index) - { - SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "addContextData_v1_0"); - SQLite::rowid_t rowId = CheckpointContextTable::AddContextData(connection, checkpointId, contextData, name, value, index); + SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "addcontextdata_v1_0"); + SQLite::rowid_t rowId = CheckpointContextTable::AddContextData(connection, existingCheckpointId.value(), contextData, name, value, index); savepoint.Commit(); return rowId; } - std::vector CheckpointRecordInterface::GetContextData(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int contextData, std::string_view name) + std::vector CheckpointRecordInterface::GetContextData(SQLite::Connection& connection, std::string_view checkpointName, int contextData) { - return CheckpointContextTable::GetContextData(connection, checkpointId, contextData, name); + auto existingCheckpointId = GetExistingCheckpointId(connection, checkpointName); + if (!existingCheckpointId) + { + return {}; + } + + return CheckpointContextTable::GetContextData(connection, existingCheckpointId.value(), contextData); } - void CheckpointRecordInterface::RemoveContextData(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int contextData) + std::vector CheckpointRecordInterface::GetContextDataByName(SQLite::Connection& connection, std::string_view checkpointName, int contextData, std::string_view name) { - SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "removeContextData_v1_0"); - CheckpointContextTable::RemoveContextData(connection, checkpointId, contextData); + auto existingCheckpointId = GetExistingCheckpointId(connection, checkpointName); + if (!existingCheckpointId) + { + return {}; + } + + return CheckpointContextTable::GetContextDataByName(connection, existingCheckpointId.value(), contextData, name); + } + + void CheckpointRecordInterface::RemoveContextData(SQLite::Connection& connection, std::string_view checkpointName, int contextData) + { + auto existingCheckpointId = GetExistingCheckpointId(connection, checkpointName); + THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_NOT_FOUND), !existingCheckpointId); + + SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "removecontextdata_v1_0"); + CheckpointContextTable::RemoveContextData(connection, existingCheckpointId.value(), contextData); savepoint.Commit(); } } \ No newline at end of file diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointTable.cpp b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointTable.cpp index d55419528f..fef5b45324 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointTable.cpp +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointTable.cpp @@ -32,6 +32,24 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 savepoint.Commit(); } + std::string CheckpointTable::GetLastCheckpoint(SQLite::Connection& connection) + { + // Sort by descending and get last checkpoint. + SQLite::Builder::StatementBuilder builder; + builder.Select(s_CheckpointTable_Name_Column).From(s_CheckpointTable_Table_Name).OrderBy(SQLite::RowIDName).Descending(); + + SQLite::Statement select = builder.Prepare(connection); + + if (select.Step()) + { + return select.GetColumn(0); + } + else + { + return {}; + } + } + SQLite::rowid_t CheckpointTable::AddCheckpoint(SQLite::Connection& connection, std::string_view checkpointName) { SQLite::Builder::StatementBuilder builder; diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointTable.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointTable.h index 479a0c3c26..e727deab6f 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointTable.h +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointTable.h @@ -16,6 +16,8 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 // Creates the table with named indices. static void Create(SQLite::Connection& connection); + static std::string GetLastCheckpoint(SQLite::Connection& connection); + // Adds a checkpoint. static SQLite::rowid_t AddCheckpoint(SQLite::Connection& connection, std::string_view checkpointName); diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/ICheckpointRecord.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/ICheckpointRecord.h index 45dc9ceb59..55a0f82692 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/Schema/ICheckpointRecord.h +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/ICheckpointRecord.h @@ -26,17 +26,28 @@ namespace AppInstaller::Repository::Microsoft::Schema // Gets the metadata value. virtual std::string GetMetadata(SQLite::Connection& connection, std::string_view name) = 0; + // Gets the latest checkpoint. + virtual std::string GetLastCheckpoint(SQLite::Connection& connection) = 0; + + // Returns a value indicating whether the checkpoint exists. + virtual bool CheckpointExists(SQLite::Connection& connection, std::string_view checkpointName) = 0; + + // Gets the available context data from a checkpoint. + virtual std::vector GetAvailableContextData(SQLite::Connection& connection, std::string_view checkpointName) = 0; + + // Adds a checkpoint. virtual SQLite::rowid_t AddCheckpoint(SQLite::Connection& connection, std::string_view checkpointName) = 0; - virtual std::optional GetCheckpointId(SQLite::Connection& connection, std::string_view checkpointName) = 0; + // Adds a context data value for a checkpoint. + virtual SQLite::rowid_t AddContextData(SQLite::Connection& connection, std::string_view checkpointName, int contextData, std::string_view name, std::string_view value, int index) = 0; - // Adds the context data property for a given checkpoint. - virtual SQLite::rowid_t AddContextData(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int contextData, std::string_view name, std::string_view value, int index) = 0; + // Gets the context data values from a checkpoint. + virtual std::vector GetContextData(SQLite::Connection& connection, std::string_view checkpointName, int contextData) = 0; - // Gets the context data property for a given checkpoint. - virtual std::vector GetContextData(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int contextData, std::string_view name) = 0; + // Gets the context data values by property name from a checkpoint. + virtual std::vector GetContextDataByName(SQLite::Connection& connection, std::string_view checkpointName, int contextData, std::string_view name) = 0; - // Removes the context data for a given checkpoint. - virtual void RemoveContextData(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int contextData) = 0; + // Removes the context data from a checkpoint. + virtual void RemoveContextData(SQLite::Connection& connection, std::string_view checkpointName, int contextData) = 0; }; } \ No newline at end of file diff --git a/src/AppInstallerRepositoryCore/SQLiteStatementBuilder.cpp b/src/AppInstallerRepositoryCore/SQLiteStatementBuilder.cpp index 216acaac33..ee3fd2b4bc 100644 --- a/src/AppInstallerRepositoryCore/SQLiteStatementBuilder.cpp +++ b/src/AppInstallerRepositoryCore/SQLiteStatementBuilder.cpp @@ -495,6 +495,18 @@ namespace AppInstaller::Repository::SQLite::Builder return *this; } + StatementBuilder& StatementBuilder::Ascending() + { + m_stream << " ASC"; + return *this; + } + + StatementBuilder& StatementBuilder::Descending() + { + m_stream << " DESC"; + return *this; + } + StatementBuilder& StatementBuilder::InsertInto(std::string_view table) { OutputOperationAndTable(m_stream, "INSERT INTO", table); diff --git a/src/AppInstallerRepositoryCore/SQLiteStatementBuilder.h b/src/AppInstallerRepositoryCore/SQLiteStatementBuilder.h index 193728b636..a108301cce 100644 --- a/src/AppInstallerRepositoryCore/SQLiteStatementBuilder.h +++ b/src/AppInstallerRepositoryCore/SQLiteStatementBuilder.h @@ -294,6 +294,10 @@ namespace AppInstaller::Repository::SQLite::Builder StatementBuilder& OrderBy(std::string_view column); StatementBuilder& OrderBy(const QualifiedColumn& column); + // Specify the ordering behavior. + StatementBuilder& Ascending(); + StatementBuilder& Descending(); + // Limits the result set to the given number of rows. StatementBuilder& Limit(size_t rowCount); From f0d86b699717952506e79780bc84f724d7f70eb4 Mon Sep 17 00:00:00 2001 From: Ryan Fu Date: Tue, 22 Aug 2023 09:39:26 -0700 Subject: [PATCH 24/43] save notes --- src/AppInstallerCLICore/CheckpointManager.h | 2 +- src/AppInstallerCLICore/Command.cpp | 3 +++ src/AppInstallerCLICore/Commands/InstallCommand.cpp | 1 + src/AppInstallerCLICore/Commands/ResumeCommand.cpp | 9 ++++++++- src/AppInstallerCLICore/ExecutionContext.cpp | 2 ++ src/AppInstallerCLICore/ExecutionContext.h | 1 + 6 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/AppInstallerCLICore/CheckpointManager.h b/src/AppInstallerCLICore/CheckpointManager.h index 5d0a3c68c5..7a0ff352ac 100644 --- a/src/AppInstallerCLICore/CheckpointManager.h +++ b/src/AppInstallerCLICore/CheckpointManager.h @@ -44,7 +44,7 @@ namespace AppInstaller::CLI::Checkpoint std::vector GetAvailableContextData(std::string_view checkpointName); // Adds a context data to the checkpoint record. - void AddContextData(std::string_view checkpointName, int contextData, std::string_view name, std::string_view value, int record); + void AddContextData(std::string_view checkpointName, int contextData, std::string_view name, std::string_view value, int index); // Gets the values associated with a context data std::vector GetContextData(std::string_view checkpointName, int contextData); diff --git a/src/AppInstallerCLICore/Command.cpp b/src/AppInstallerCLICore/Command.cpp index 7fcb31a079..093fc5527d 100644 --- a/src/AppInstallerCLICore/Command.cpp +++ b/src/AppInstallerCLICore/Command.cpp @@ -605,6 +605,8 @@ namespace AppInstaller::CLI ParseArgumentsStateMachine stateMachine{ inv, execArgs, std::move(definedArgs) }; + // Add function to save all of the execArgs, store in context Args data, store it in the record. + while (stateMachine.Step()) { stateMachine.ThrowIfError(); @@ -853,6 +855,7 @@ namespace AppInstaller::CLI throw GroupPolicyException(Settings::TogglePolicy::Policy::WinGet); } + // Remove this. if (Settings::ExperimentalFeature::IsEnabled(ExperimentalFeature::Feature::Resume)) { if (!context.Args.Contains(Execution::Args::Type::ResumeId)) diff --git a/src/AppInstallerCLICore/Commands/InstallCommand.cpp b/src/AppInstallerCLICore/Commands/InstallCommand.cpp index 570ed2eccf..3f47a7145e 100644 --- a/src/AppInstallerCLICore/Commands/InstallCommand.cpp +++ b/src/AppInstallerCLICore/Commands/InstallCommand.cpp @@ -126,6 +126,7 @@ namespace AppInstaller::CLI Workflow::GetManifestFromArg << Workflow::SelectInstaller << Workflow::EnsureApplicableInstaller << + // Example: Checkpoint an installer Workflow::InstallSinglePackage; } else diff --git a/src/AppInstallerCLICore/Commands/ResumeCommand.cpp b/src/AppInstallerCLICore/Commands/ResumeCommand.cpp index 6963b54cec..55e6711f54 100644 --- a/src/AppInstallerCLICore/Commands/ResumeCommand.cpp +++ b/src/AppInstallerCLICore/Commands/ResumeCommand.cpp @@ -42,6 +42,7 @@ namespace AppInstaller::CLI std::string resumeGuidString{ context.Args.GetArg(Execution::Args::Type::ResumeId) }; GUID checkpointId = Utility::ConvertToGuid(resumeGuidString); + // Change this to a CheckpointRecord helepr function like Exists if (!std::filesystem::exists(AppInstaller::Repository::Microsoft::CheckpointRecord::GetCheckpointRecordPath(checkpointId))) { context.Reporter.Error() << Resource::String::ResumeIdNotFoundError(Utility::LocIndView{ resumeGuidString }) << std::endl; @@ -75,12 +76,18 @@ namespace AppInstaller::CLI THROW_HR_IF_MSG(E_UNEXPECTED, !commandToResume, "Command to resume not found."); resumeContext.SetExecutingCommand(commandToResume.get()); - resumeContext.SetFlags(Execution::ContextFlag::Resume); + resumeContext.SetFlags(Execution::ContextFlag::Resume); // this should be captured by telemetry resumeContext.Checkpoint("Start"sv); + auto previousThreadGlobals = resumeContext.SetForCurrentThread(); + + // Must be after or call initialize + // move local objects to resume context resumeContext.EnableSignalTerminationHandler(); + // double check this context, + commandToResume->Resume(resumeContext); context.SetTerminationHR(resumeContext.GetTerminationHR()); } diff --git a/src/AppInstallerCLICore/ExecutionContext.cpp b/src/AppInstallerCLICore/ExecutionContext.cpp index cae5af020e..a0e6728321 100644 --- a/src/AppInstallerCLICore/ExecutionContext.cpp +++ b/src/AppInstallerCLICore/ExecutionContext.cpp @@ -266,6 +266,7 @@ namespace AppInstaller::CLI::Execution Context Context::CreateEmptyContext() { + // make own thread globals return Context(Reporter, m_threadGlobals); } @@ -483,6 +484,7 @@ namespace AppInstaller::CLI::Execution int index = 0; if (values.empty()) { + // CheckpointManager.AddContextData(checkpointName, static_cast(type), argName, {}, index); } else diff --git a/src/AppInstallerCLICore/ExecutionContext.h b/src/AppInstallerCLICore/ExecutionContext.h index 8f8d7c02e3..f2a87db1e2 100644 --- a/src/AppInstallerCLICore/ExecutionContext.h +++ b/src/AppInstallerCLICore/ExecutionContext.h @@ -167,6 +167,7 @@ namespace AppInstaller::CLI::Execution #endif // Interacts with the checkpoint record for writing and loading checkpoints. + // Change to smart pointer and add a getter function. Checkpoint::CheckpointManager CheckpointManager; // Returns a value indicating whether the current checkpoint matches the target checkpoint. From 5ef5dce4d3997fc3a3e859d49129ce959f9e1909 Mon Sep 17 00:00:00 2001 From: Ryan Fu Date: Wed, 23 Aug 2023 12:59:34 -0700 Subject: [PATCH 25/43] template example --- src/AppInstallerCLICore/CheckpointManager.h | 10 ++- src/AppInstallerCLICore/ExecutionContext.cpp | 64 ++++++++++++++++++++ src/AppInstallerCLICore/ExecutionContext.h | 8 +-- 3 files changed, 77 insertions(+), 5 deletions(-) diff --git a/src/AppInstallerCLICore/CheckpointManager.h b/src/AppInstallerCLICore/CheckpointManager.h index 7a0ff352ac..46f42364bf 100644 --- a/src/AppInstallerCLICore/CheckpointManager.h +++ b/src/AppInstallerCLICore/CheckpointManager.h @@ -9,8 +9,14 @@ namespace AppInstaller::Repository::Microsoft struct CheckpointRecord; } -namespace AppInstaller::CLI::Checkpoint +namespace AppInstaller::CLI::Checkpoints { + enum class CheckpointData + { + Args, + }; + + struct CheckpointManager { // Returns a bool value indicating whether a record has been loaded. @@ -22,6 +28,8 @@ namespace AppInstaller::CLI::Checkpoint // Loads an existing record from an id. void LoadRecord(GUID id); + Checkpoint CreateCheckpoint(std::string_view checkpointName, T ); + // Gets a boolean value indicating whether the checkpoint name exists in the record. bool Exists(std::string_view checkpointName); diff --git a/src/AppInstallerCLICore/ExecutionContext.cpp b/src/AppInstallerCLICore/ExecutionContext.cpp index a0e6728321..334904a038 100644 --- a/src/AppInstallerCLICore/ExecutionContext.cpp +++ b/src/AppInstallerCLICore/ExecutionContext.cpp @@ -427,6 +427,68 @@ namespace AppInstaller::CLI::Execution return m_currentCheckpoint == m_targetCheckpoint; } + + // Representation of a single checkpoint + enum class CheckpointData + { + Args, + }; + + template + struct CheckpointContextData + { + CheckpointContextData(T data) {}; + + std::map> GetContextDataMap() { return m_contextDataMap; }; + + private: + std::map> m_contextDataMap; + }; + + template<> + CheckpointContextData::CheckpointContextData(Execution::Args args) + { + const auto& argTypes = args.GetTypes(); + + for (auto type : argTypes) + { + const auto& argName = Argument::ForType(type).Name(); + const auto& values = *args.GetArgs(type); + int index = 0; + int castedType = static_cast(type); + m_contextDataMap.emplace(castedType, values); + } + } + + struct Checkpoint + { + template + void AddContextData(CheckpointData type, const CheckpointContextData& data) + { + m_checkpointData[type] = std::move(data); + }; + + std::map> GetDataItem(CheckpointData checkpointData) { }; + + private: + std::string_view m_checkpointName; + std::map>> m_checkpointData; + }; + + + + + + + + + + + + + + + void Context::Checkpoint(std::string_view checkpointName, std::vector contextData) { // Create record if it does not exist. @@ -506,4 +568,6 @@ namespace AppInstaller::CLI::Execution m_currentCheckpoint = checkpointName; } + + } diff --git a/src/AppInstallerCLICore/ExecutionContext.h b/src/AppInstallerCLICore/ExecutionContext.h index f2a87db1e2..5d9bdbe2e0 100644 --- a/src/AppInstallerCLICore/ExecutionContext.h +++ b/src/AppInstallerCLICore/ExecutionContext.h @@ -166,16 +166,14 @@ namespace AppInstaller::CLI::Execution bool ShouldExecuteWorkflowTask(const Workflow::WorkflowTask& task); #endif - // Interacts with the checkpoint record for writing and loading checkpoints. - // Change to smart pointer and add a getter function. - Checkpoint::CheckpointManager CheckpointManager; - // Returns a value indicating whether the current checkpoint matches the target checkpoint. bool IsCurrentCheckpointAtTarget(); // Sets the target checkpoint. void SetTargetCheckpoint(std::string_view checkpointName) { m_targetCheckpoint = checkpointName; }; + Checkpoints::Checkpoint CreateCheckpoint(std::string_view checkpointName, std::vector contextData); + // Writes the context data to the checkpoint record if the checkpoint does not yet exist. // If no context data is provided, writes the automatic checkpoint metadata. // If the checkpoint already exists, loads the context data from the saved checkpoint. @@ -199,6 +197,8 @@ namespace AppInstaller::CLI::Execution Workflow::ExecutionStage m_executionStage = Workflow::ExecutionStage::Initial; AppInstaller::ThreadLocalStorage::WingetThreadGlobals m_threadGlobals; AppInstaller::CLI::Command* m_executingCommand = nullptr; + + std::unique_ptr m_checkpointManager; std::string_view m_targetCheckpoint = {}; std::string_view m_currentCheckpoint = {}; }; From 3775cb08e1799708fcb291db26bf3c5f330a4ac2 Mon Sep 17 00:00:00 2001 From: Ryan Fu Date: Mon, 28 Aug 2023 09:20:57 -0700 Subject: [PATCH 26/43] save work --- src/AppInstallerCLICore/CheckpointManager.cpp | 134 +++++++-------- src/AppInstallerCLICore/CheckpointManager.h | 154 +++++++++++++----- src/AppInstallerCLICore/Command.cpp | 9 - .../Commands/InstallCommand.cpp | 2 + src/AppInstallerCLICore/ExecutionContext.cpp | 45 ++++- src/AppInstallerCLICore/ExecutionContext.h | 2 +- .../Microsoft/CheckpointRecord.cpp | 2 + .../Microsoft/CheckpointRecord.h | 7 +- .../CheckpointRecordInterface.h | 6 + .../CheckpointRecordInterface_1_0.cpp | 4 + .../Microsoft/Schema/ICheckpointRecord.h | 16 ++ 11 files changed, 258 insertions(+), 123 deletions(-) diff --git a/src/AppInstallerCLICore/CheckpointManager.cpp b/src/AppInstallerCLICore/CheckpointManager.cpp index c0b3ab13ab..11a97a11f2 100644 --- a/src/AppInstallerCLICore/CheckpointManager.cpp +++ b/src/AppInstallerCLICore/CheckpointManager.cpp @@ -2,102 +2,106 @@ // Licensed under the MIT License. #include "pch.h" #include "CheckpointManager.h" -#include "Microsoft/CheckpointRecord.h" +#include "Microsoft/Schema/Checkpoint_1_0/CheckpointRecordInterface.h" +#include -namespace AppInstaller::CLI::Checkpoint +namespace AppInstaller::Checkpoints { using namespace AppInstaller::Repository::Microsoft; + using namespace AppInstaller::Repository::SQLite; - void CheckpointManager::CreateRecord() + namespace { - std::ignore = CoCreateGuid(&m_checkpointId); - AICLI_LOG(CLI, Info, << "Creating checkpoint index with id: " << m_checkpointId); - const auto& indexPath = CheckpointRecord::GetCheckpointRecordPath(m_checkpointId); - m_checkpointRecord = std::make_shared(CheckpointRecord::CreateNew(indexPath.u8string())); - } + constexpr std::string_view s_checkpoints_filename = "checkpoints.db"sv; - void CheckpointManager::LoadRecord(GUID id) - { - m_checkpointId = id; - AICLI_LOG(CLI, Info, << "Opening checkpoint index with id: " << m_checkpointId); - const auto& indexPath = CheckpointRecord::GetCheckpointRecordPath(m_checkpointId); - m_checkpointRecord = std::make_shared(CheckpointRecord::Open(indexPath.u8string())); - } + constexpr std::string_view s_Checkpoints = "Checkpoints"sv; + constexpr std::string_view s_ClientVersion = "ClientVersion"sv; + constexpr std::string_view s_CommandName = "CommandName"sv; - bool CheckpointManager::Exists(std::string_view checkpointName) - { - return m_checkpointRecord->CheckpointExists(checkpointName); + std::string_view GetCheckpointMetadataString(AutomaticCheckpointData checkpointMetadata) + { + switch (checkpointMetadata) + { + case AutomaticCheckpointData::ClientVersion: + return s_ClientVersion; + case AutomaticCheckpointData::CommandName: + return s_CommandName; + default: + return "unknown"sv; + } + } } - std::string CheckpointManager::GetClientVersion() + CheckpointRecord CheckpointRecord::CreateNew(const std::string& filePath, Schema::Version version) { - return m_checkpointRecord->GetMetadata(CheckpointMetadata::ClientVersion); - } + AICLI_LOG(Repo, Info, << "Creating new Checkpoint Index with version [" << version << "] at '" << filePath << "'"); + CheckpointRecord result{ filePath, version }; - std::string CheckpointManager::GetCommandName() - { - return m_checkpointRecord->GetMetadata(CheckpointMetadata::CommandName); - } + SQLite::Savepoint savepoint = SQLite::Savepoint::Create(result.m_dbconn, "CheckpointRecord_createnew"); - std::string CheckpointManager::GetLastCheckpoint() - { - return m_checkpointRecord->GetLastCheckpoint(); - } + // Use calculated version, as incoming version could be 'latest' + result.m_version.SetSchemaVersion(result.m_dbconn); - std::vector CheckpointManager::GetAvailableContextData(std::string_view checkpointName) - { - return m_checkpointRecord->GetAvailableData(checkpointName); - } + result.m_interface->CreateTables(result.m_dbconn); - void CheckpointManager::SetClientVersion(std::string_view value) - { - m_checkpointRecord->SetMetadata(CheckpointMetadata::ClientVersion, value); - } + result.SetLastWriteTime(); - void CheckpointManager::SetCommandName(std::string_view value) - { - m_checkpointRecord->SetMetadata(CheckpointMetadata::CommandName, value); + savepoint.Commit(); + + return result; } - void CheckpointManager::AddContextData(std::string_view checkpointName, int contextData, std::string_view name, std::string_view value, int index) + std::filesystem::path CheckpointRecord::GetCheckpointRecordPath(GUID guid) { - if (!Exists(checkpointName)) + wchar_t checkpointGuid[256]; + THROW_HR_IF(E_UNEXPECTED, !StringFromGUID2(guid, checkpointGuid, ARRAYSIZE(checkpointGuid))); + + const auto checkpointsDirectory = Runtime::GetPathTo(Runtime::PathName::CheckpointsLocation) / checkpointGuid; + + if (!std::filesystem::exists(checkpointsDirectory)) { - m_checkpointRecord->AddCheckpoint(checkpointName); + std::filesystem::create_directories(checkpointsDirectory); + AICLI_LOG(Repo, Info, << "Creating checkpoint index directory: " << checkpointsDirectory); + } + else + { + THROW_HR_IF(ERROR_CANNOT_MAKE, !std::filesystem::is_directory(checkpointsDirectory)); } - m_checkpointRecord->AddContextData(checkpointName, contextData, name, value, index); + auto indexPath = checkpointsDirectory / s_checkpoints_filename; + return indexPath; } - std::vector CheckpointManager::GetContextData(std::string_view checkpointName, int contextData) + bool CheckpointRecord::IsEmpty() { - return m_checkpointRecord->GetContextData(checkpointName, contextData); + return m_interface->IsEmpty(m_dbconn); } - std::vector CheckpointManager::GetContextDataByName(std::string_view checkpointName, int contextData, std::string_view name) - { - return m_checkpointRecord->GetContextDataByName(checkpointName, contextData, name); - } + - void CheckpointManager::DeleteRecord() + std::unique_ptr CheckpointRecord::CreateICheckpointRecord() const { - if (m_checkpointRecord) + if (m_version == Schema::Version{ 1, 0 } || + m_version.MajorVersion == 1 || + m_version.IsLatest()) { - m_checkpointRecord.reset(); + return std::make_unique(); } - if (m_checkpointId != GUID_NULL) - { - const auto& checkpointRecordPath = CheckpointRecord::GetCheckpointRecordPath(m_checkpointId); + THROW_HR(HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED)); + } - if (std::filesystem::exists(checkpointRecordPath)) - { - std::error_code error; - if (std::filesystem::remove(checkpointRecordPath, error)) - { - AICLI_LOG(CLI, Info, << "Checkpoint index deleted: " << checkpointRecordPath); - } - } - } + CheckpointRecord::CheckpointRecord(const std::string& target, SQLiteStorageBase::OpenDisposition disposition, Utility::ManagedFile&& indexFile) : + SQLiteStorageBase(target, disposition, std::move(indexFile)) + { + AICLI_LOG(Repo, Info, << "Opened Checkpoint Index with version [" << m_version << "], last write [" << GetLastWriteTime() << "]"); + m_interface = CreateICheckpointRecord(); + THROW_HR_IF(APPINSTALLER_CLI_ERROR_CANNOT_WRITE_TO_UPLEVEL_INDEX, disposition == SQLiteStorageBase::OpenDisposition::ReadWrite && m_version != m_interface->GetVersion()); + } + + CheckpointRecord::CheckpointRecord(const std::string& target, Schema::Version version) : SQLiteStorageBase(target, version) + { + m_interface = CreateICheckpointRecord(); + m_version = m_interface->GetVersion(); } } diff --git a/src/AppInstallerCLICore/CheckpointManager.h b/src/AppInstallerCLICore/CheckpointManager.h index 46f42364bf..103f311c4c 100644 --- a/src/AppInstallerCLICore/CheckpointManager.h +++ b/src/AppInstallerCLICore/CheckpointManager.h @@ -2,69 +2,143 @@ // Licensed under the MIT License. #pragma once #include -#include "winget/ManifestCommon.h" +#include "SQLiteWrapper.h" +#include "Microsoft/Schema/ICheckpointRecord.h" +#include "Microsoft/SQLiteStorageBase.h" +#include +#include "ExecutionContextData.h" -namespace AppInstaller::Repository::Microsoft -{ - struct CheckpointRecord; -} +using namespace AppInstaller::Repository; +using namespace AppInstaller::Repository::SQLite; -namespace AppInstaller::CLI::Checkpoints +namespace AppInstaller::Checkpoints { - enum class CheckpointData + // A representation of a single context data (all rows for a single checkpoint and single context data). + struct CheckpointData { - Args, + CheckpointData(int64_t contextDataId) : m_contextDataId(contextDataId) {}; + + // Returns a boolean value indicating whether the field name exists. + bool Has(std::string fieldName) + { + return m_values.find(fieldName) != m_values.end(); + } + + // Gets all available field names. + std::vector GetFieldNames() + { + std::vector fieldNames; + for (auto it = m_values.begin(); it != m_values.end(); ++it) { + fieldNames.push_back(it->first); + } + return fieldNames; + } + + std::vector Get(std::string fieldName) + { + auto it = m_values.find(fieldName); + return it->second; + } + + void Set(std::string fieldName, std::vector values) + { + m_values.insert({ fieldName, values }); + } + + // This will be used to rebuild our data objects later... + //template + //T Get(std::string fieldName); + + //template + //void Set(std::string fieldName, T data); + + private: + int64_t m_contextDataId; + std::map> m_values; }; + // Enum to define the types of checkpoint data + enum AutomaticCheckpointData + { + ClientVersion, + CommandName, + Arguments + }; - struct CheckpointManager + struct CheckpointRecord : SQLiteStorageBase { - // Returns a bool value indicating whether a record has been loaded. - bool IsLoaded() { return m_checkpointRecord ? true: false; }; + // An id that refers to a specific Checkpoint. + using IdType = SQLite::rowid_t; + + CheckpointRecord(const CheckpointRecord&) = delete; + CheckpointRecord& operator=(const CheckpointRecord&) = delete; + + CheckpointRecord(CheckpointRecord&&) = default; + CheckpointRecord& operator=(CheckpointRecord&&) = default; + + // Opens an existing CheckpointRecord database. + static CheckpointRecord Open(const std::string& filePath, OpenDisposition disposition = OpenDisposition::ReadWrite, Utility::ManagedFile&& indexFile = {}) + { + return { filePath, disposition, std::move(indexFile) }; + } - // Creates a new checkpoint record. - void CreateRecord(); + // Create a new CheckpointRecord database. + static CheckpointRecord CreateNew(const std::string& filePath, Schema::Version version = Schema::Version::Latest()); - // Loads an existing record from an id. - void LoadRecord(GUID id); + // Gets the file path of the CheckpointRecord database. + static std::filesystem::path GetCheckpointRecordPath(GUID guid); - Checkpoint CreateCheckpoint(std::string_view checkpointName, T ); + // Returns a value indicating whether the record is empty. + bool IsEmpty(); - // Gets a boolean value indicating whether the checkpoint name exists in the record. - bool Exists(std::string_view checkpointName); + Checkpoint GetStartingCheckpoint() + { + // Gets the checkpoint metadata + } - // Sets the client version. - void SetClientVersion(std::string_view value); + std::map> GetCheckpoints(); - // Gets the client version from the checkpoint record. - std::string GetClientVersion(); - // Sets the command name. - void SetCommandName(std::string_view value); + private: + // Constructor used to open an existing index. + CheckpointRecord(const std::string& target, SQLiteStorageBase::OpenDisposition disposition, Utility::ManagedFile&& indexFile); + + // Constructor used to create a new index. + CheckpointRecord(const std::string& target, Schema::Version version); + + // Creates the ICheckpointRecord interface object for this version. + std::unique_ptr CreateICheckpointRecord() const; + + std::unique_ptr m_interface; + }; - // Gets the command name from the checkpoint record. - std::string GetCommandName(); + enum class CheckpointNames + { + Automatic, + Installer, + }; - // Gets the latest checkpoint. - std::string GetLastCheckpoint(); + // A representation of a row in the Checkpoint table. + template + struct Checkpoint + { + friend CheckpointRecord; - // Gets the available context data items for a given checkpoint. - std::vector GetAvailableContextData(std::string_view checkpointName); + Checkpoint(std::vector checkpointData) : m_checkpointData(checkpointData) {}; - // Adds a context data to the checkpoint record. - void AddContextData(std::string_view checkpointName, int contextData, std::string_view name, std::string_view value, int index); + CheckpointData GetData(T type) + { - // Gets the values associated with a context data - std::vector GetContextData(std::string_view checkpointName, int contextData); + } - // Gets the values by property name associated with a context data. - std::vector GetContextDataByName(std::string_view checkpointName, int contextData, std::string_view name); + std::vector GetCheckpointDataTypes() + { - // Releases and deletes the checkpoint record. - void DeleteRecord(); + } private: - GUID m_checkpointId = {}; - std::shared_ptr m_checkpointRecord; + Checkpoint(SQLite::rowid_t id) : m_rowid(id) {}; + SQLite::rowid_t m_rowId; + std::vector m_checkpointData; }; } \ No newline at end of file diff --git a/src/AppInstallerCLICore/Command.cpp b/src/AppInstallerCLICore/Command.cpp index 093fc5527d..8ea43bc564 100644 --- a/src/AppInstallerCLICore/Command.cpp +++ b/src/AppInstallerCLICore/Command.cpp @@ -855,15 +855,6 @@ namespace AppInstaller::CLI throw GroupPolicyException(Settings::TogglePolicy::Policy::WinGet); } - // Remove this. - if (Settings::ExperimentalFeature::IsEnabled(ExperimentalFeature::Feature::Resume)) - { - if (!context.Args.Contains(Execution::Args::Type::ResumeId)) - { - context.Checkpoint("Start"sv); - } - } - AICLI_LOG(CLI, Info, << "Executing command: " << Name()); if (context.Args.Contains(Execution::Args::Type::Help)) { diff --git a/src/AppInstallerCLICore/Commands/InstallCommand.cpp b/src/AppInstallerCLICore/Commands/InstallCommand.cpp index 3f47a7145e..66ec5592f3 100644 --- a/src/AppInstallerCLICore/Commands/InstallCommand.cpp +++ b/src/AppInstallerCLICore/Commands/InstallCommand.cpp @@ -121,6 +121,8 @@ namespace AppInstaller::CLI if (context.Args.Contains(Execution::Args::Type::Manifest)) { + context.CreateCheckpoint("start", {}); + context << Workflow::ReportExecutionStage(ExecutionStage::Discovery) << Workflow::GetManifestFromArg << diff --git a/src/AppInstallerCLICore/ExecutionContext.cpp b/src/AppInstallerCLICore/ExecutionContext.cpp index 334904a038..24a98b935f 100644 --- a/src/AppInstallerCLICore/ExecutionContext.cpp +++ b/src/AppInstallerCLICore/ExecutionContext.cpp @@ -475,22 +475,53 @@ namespace AppInstaller::CLI::Execution std::map>> m_checkpointData; }; + void Context::Checkpoint(std::string_view checkpointName, std::vector contextData) + { + // Create automatic checkpoints + // If the checkpoint index doesn't already create it, and also add the automatic checkpoints. + if (!m_checkpointRecord) + { + GUID checkpointId; + std::ignore = CoCreateGuid(&checkpointId); + const auto& checkpointRecordPath = Checkpoints::CheckpointRecord::GetCheckpointRecordPath(checkpointId); + m_checkpointRecord = std::make_unique(Checkpoints::CheckpointRecord::CreateNew(checkpointRecordPath.u8string())); + + std::vector checkpointData; + + // Create automatic checkpoint data: + Checkpoints::CheckpointData clientVersionData{ 0 }; + clientVersionData.Set("clientVersion", { AppInstaller::Runtime::GetClientVersion() }); + checkpointData.emplace_back(clientVersionData); + + Checkpoints::CheckpointData commandName{ 1 }; + const auto& executingCommand = m_executingCommand; + if (executingCommand != nullptr) + { + commandName.Set("commandName", { std::string{ m_executingCommand->Name() } }); + } + checkpointData.emplace_back(commandName); + Checkpoints::CheckpointData commandArguments{ 2 }; + const auto& argTypes = Args.GetTypes(); + for (auto type : argTypes) + { + const auto& argName = Argument::ForType(type).Name(); + const auto& values = *Args.GetArgs(type); + commandArguments.Set(std::string{ argName }, values); + } + checkpointData.emplace_back(commandArguments); + Checkpoints::Checkpoint automaticCheckpoint; + Checkpoints::Checkpoint checkpoint{ checkpointData }; + + } - - - - - - void Context::Checkpoint(std::string_view checkpointName, std::vector contextData) - { // Create record if it does not exist. if (!CheckpointManager.IsLoaded()) { diff --git a/src/AppInstallerCLICore/ExecutionContext.h b/src/AppInstallerCLICore/ExecutionContext.h index 5d9bdbe2e0..c9e9c5abad 100644 --- a/src/AppInstallerCLICore/ExecutionContext.h +++ b/src/AppInstallerCLICore/ExecutionContext.h @@ -198,7 +198,7 @@ namespace AppInstaller::CLI::Execution AppInstaller::ThreadLocalStorage::WingetThreadGlobals m_threadGlobals; AppInstaller::CLI::Command* m_executingCommand = nullptr; - std::unique_ptr m_checkpointManager; + std::unique_ptr m_checkpointRecord; std::string_view m_targetCheckpoint = {}; std::string_view m_currentCheckpoint = {}; }; diff --git a/src/AppInstallerRepositoryCore/Microsoft/CheckpointRecord.cpp b/src/AppInstallerRepositoryCore/Microsoft/CheckpointRecord.cpp index 5ff98e0fb1..cbbcca3b6a 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/CheckpointRecord.cpp +++ b/src/AppInstallerRepositoryCore/Microsoft/CheckpointRecord.cpp @@ -74,6 +74,8 @@ namespace AppInstaller::Repository::Microsoft return m_interface->IsEmpty(m_dbconn); } + + std::vector CheckpointRecord::GetAvailableData(std::string_view name) { return m_interface->GetAvailableContextData(m_dbconn, name); diff --git a/src/AppInstallerRepositoryCore/Microsoft/CheckpointRecord.h b/src/AppInstallerRepositoryCore/Microsoft/CheckpointRecord.h index dfae36e88f..fa07898dbf 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/CheckpointRecord.h +++ b/src/AppInstallerRepositoryCore/Microsoft/CheckpointRecord.h @@ -8,7 +8,7 @@ namespace AppInstaller::Repository::Microsoft { - enum CheckpointMetadata + enum AutomaticCheckpointData { ClientVersion, CommandName @@ -40,6 +40,11 @@ namespace AppInstaller::Repository::Microsoft // Returns a value indicating whether the record is empty. bool IsEmpty(); + Checkpoint GetAutomaticCheckpoint(); + + std::map> GetCheckpoints(); + + // Gets all available context data for a checkpoint. std::vector GetAvailableData(std::string_view checkpointName); diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointRecordInterface.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointRecordInterface.h index f2b464a56c..0097cc6f23 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointRecordInterface.h +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointRecordInterface.h @@ -13,6 +13,12 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 private: bool IsEmpty(SQLite::Connection& connection) override; + + std::vector GetCheckpoints(SQLite::Connection& connection) override; + + + + SQLite::rowid_t SetMetadata(SQLite::Connection& connection, std::string_view name, std::string_view value) override; std::string GetMetadata(SQLite::Connection& connection, std::string_view name) override; std::string GetLastCheckpoint(SQLite::Connection& connection) override; diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointRecordInterface_1_0.cpp b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointRecordInterface_1_0.cpp index cb06190b07..ca40e98d21 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointRecordInterface_1_0.cpp +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointRecordInterface_1_0.cpp @@ -42,6 +42,10 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 return CheckpointContextTable::IsEmpty(connection); } + + + + SQLite::rowid_t CheckpointRecordInterface::SetMetadata(SQLite::Connection& connection, std::string_view name, std::string_view value) { SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "setMetadata_v1_0"); diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/ICheckpointRecord.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/ICheckpointRecord.h index 55a0f82692..03963a40e7 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/Schema/ICheckpointRecord.h +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/ICheckpointRecord.h @@ -20,6 +20,22 @@ namespace AppInstaller::Repository::Microsoft::Schema // Returns a bool value indicating whether all checkpoint tables are empty. virtual bool IsEmpty(SQLite::Connection& connection) = 0; + virtual std::vector GetCheckpoints(SQLite::Connection& connection) = 0; + + virtual std:: + + + + + + + + + + + + + // Sets the metadata value. virtual SQLite::rowid_t SetMetadata(SQLite::Connection& connection, std::string_view name, std::string_view value) = 0; From 44ebf0bada0ad35c80cb6d859881cb367b71ab92 Mon Sep 17 00:00:00 2001 From: Ryan Fu Date: Tue, 29 Aug 2023 11:39:29 -0700 Subject: [PATCH 27/43] save work --- src/AppInstallerCLICore/CheckpointManager.cpp | 12 +++- src/AppInstallerCLICore/CheckpointManager.h | 19 ++++--- .../CheckpointRecordInterface.h | 31 ++++++---- .../CheckpointRecordInterface_1_0.cpp | 57 ++++++++++++++++++- .../Schema/Checkpoint_1_0/CheckpointTable.cpp | 18 ++++++ .../Schema/Checkpoint_1_0/CheckpointTable.h | 2 + .../Microsoft/Schema/ICheckpointRecord.h | 51 ++++++++--------- 7 files changed, 143 insertions(+), 47 deletions(-) diff --git a/src/AppInstallerCLICore/CheckpointManager.cpp b/src/AppInstallerCLICore/CheckpointManager.cpp index 11a97a11f2..d455d186f8 100644 --- a/src/AppInstallerCLICore/CheckpointManager.cpp +++ b/src/AppInstallerCLICore/CheckpointManager.cpp @@ -77,7 +77,17 @@ namespace AppInstaller::Checkpoints return m_interface->IsEmpty(m_dbconn); } - + Checkpoint CheckpointRecord::GetStartingCheckpoint() + { + // Get just the initial first checkpoint. + return m_interface->GetCheckpointByName("start"sv); + } + + std::map> CheckpointRecord::GetCheckpoints() + { + // Get all of the available data items for a checkpoint.... + return m_interface->GetCheckpoints(); + } std::unique_ptr CheckpointRecord::CreateICheckpointRecord() const { diff --git a/src/AppInstallerCLICore/CheckpointManager.h b/src/AppInstallerCLICore/CheckpointManager.h index 103f311c4c..97f7b3c306 100644 --- a/src/AppInstallerCLICore/CheckpointManager.h +++ b/src/AppInstallerCLICore/CheckpointManager.h @@ -10,6 +10,7 @@ using namespace AppInstaller::Repository; using namespace AppInstaller::Repository::SQLite; +using namespace AppInstaller::Repository::Microsoft; namespace AppInstaller::Checkpoints { @@ -57,6 +58,8 @@ namespace AppInstaller::Checkpoints std::map> m_values; }; + // The other enum would be execution context data. + // Enum to define the types of checkpoint data enum AutomaticCheckpointData { @@ -91,14 +94,10 @@ namespace AppInstaller::Checkpoints // Returns a value indicating whether the record is empty. bool IsEmpty(); - Checkpoint GetStartingCheckpoint() - { - // Gets the checkpoint metadata - } + Checkpoint GetStartingCheckpoint(); std::map> GetCheckpoints(); - private: // Constructor used to open an existing index. CheckpointRecord(const std::string& target, SQLiteStorageBase::OpenDisposition disposition, Utility::ManagedFile&& indexFile); @@ -128,17 +127,23 @@ namespace AppInstaller::Checkpoints CheckpointData GetData(T type) { - + return m_checkpointDataMap[type]; } std::vector GetCheckpointDataTypes() { + std::vector dataTypes; + for (const auto& data : m_checkpointDataMap) + { + dataTypes.emplace_back(data.first); + } + return dataTypes; } private: Checkpoint(SQLite::rowid_t id) : m_rowid(id) {}; SQLite::rowid_t m_rowId; - std::vector m_checkpointData; + std::map m_checkpointDataMap; }; } \ No newline at end of file diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointRecordInterface.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointRecordInterface.h index 0097cc6f23..f1d0f4b653 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointRecordInterface.h +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointRecordInterface.h @@ -14,20 +14,29 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 private: bool IsEmpty(SQLite::Connection& connection) override; - std::vector GetCheckpoints(SQLite::Connection& connection) override; + std::vector GetAvailableCheckpoints(SQLite::Connection& connection) override; + std::map> GetContextDataByContextId(SQLite::Connection& connection, std::string checkpointName, int64_t dataId) override; + std::vector GetAvailableDataTypes(SQLite::Connection& connection, std::string checkpointName) override; + SQLite::rowid_t AddCheckpoint(SQLite::Connection& connection, std::string checkpointName) override; - SQLite::rowid_t SetMetadata(SQLite::Connection& connection, std::string_view name, std::string_view value) override; - std::string GetMetadata(SQLite::Connection& connection, std::string_view name) override; - std::string GetLastCheckpoint(SQLite::Connection& connection) override; - bool CheckpointExists(SQLite::Connection& connection, std::string_view checkpointName) override; - std::vector GetAvailableContextData(SQLite::Connection& connection, std::string_view checkpointName) override; - SQLite::rowid_t AddCheckpoint(SQLite::Connection& connection, std::string_view checkpointName) override; - SQLite::rowid_t AddContextData(SQLite::Connection& connection, std::string_view checkpointName, int contextData, std::string_view name, std::string_view value, int index = 0) override; - std::vector GetContextData(SQLite::Connection& connection, std::string_view checkpointName, int contextData) override; - std::vector GetContextDataByName(SQLite::Connection& connection, std::string_view checkpointName, int contextData, std::string_view name) override; - void RemoveContextData(SQLite::Connection& connection, std::string_view checkpointName, int contextData) override; + void AddContextData(SQLite::Connection& connection, std::string checkpointName, int dataId, std::string name, std::vector values) override; + + + + + + //SQLite::rowid_t SetMetadata(SQLite::Connection& connection, std::string_view name, std::string_view value) override; + //std::string GetMetadata(SQLite::Connection& connection, std::string_view name) override; + //std::string GetLastCheckpoint(SQLite::Connection& connection) override; + //bool CheckpointExists(SQLite::Connection& connection, std::string_view checkpointName) override; + //std::vector GetAvailableContextData(SQLite::Connection& connection, std::string_view checkpointName) override; + //SQLite::rowid_t AddCheckpoint(SQLite::Connection& connection, std::string_view checkpointName) override; + //SQLite::rowid_t AddContextData(SQLite::Connection& connection, std::string_view checkpointName, int contextData, std::string_view name, std::string_view value, int index = 0) override; + //std::vector GetContextData(SQLite::Connection& connection, std::string_view checkpointName, int contextData) override; + //std::vector GetContextDataByName(SQLite::Connection& connection, std::string_view checkpointName, int contextData, std::string_view name) override; + //void RemoveContextData(SQLite::Connection& connection, std::string_view checkpointName, int contextData) override; }; } \ No newline at end of file diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointRecordInterface_1_0.cpp b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointRecordInterface_1_0.cpp index ca40e98d21..045c3538b5 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointRecordInterface_1_0.cpp +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointRecordInterface_1_0.cpp @@ -10,7 +10,7 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 { namespace { - std::optional GetExistingCheckpointId(SQLite::Connection& connection, std::string_view checkpointName) + std::optional GetCheckpointIdByName(SQLite::Connection& connection, std::string_view checkpointName) { auto result = CheckpointTable::GetCheckpointId(connection, checkpointName); @@ -42,8 +42,61 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 return CheckpointContextTable::IsEmpty(connection); } - + std::vector CheckpointRecordInterface::GetAvailableCheckpoints(SQLite::Connection& connection) + { + return CheckpointTable::GetCheckpoints(connection); + } + std::map> CheckpointRecordInterface::GetContextDataByContextId(SQLite::Connection& connection, std::string checkpointName, int64_t dataId) + { + auto checkpointId = GetCheckpointIdByName(connection, checkpointName); + if (!checkpointId) + { + return {}; + } + + const auto& availableDataTypes = CheckpointContextTable::GetAvailableData(connection, checkpointId.value()); + std::map> contextDataMap; + + for (auto dataTypes : availableDataTypes) + { + const auto& values = CheckpointContextTable::GetContextData(connection, checkpointId.value(), dataTypes); + contextDataMap[dataTypes] = values; + } + + return contextDataMap; + } + + std::vector CheckpointRecordInterface::GetAvailableDataTypes(SQLite::Connection& connection, std::string checkpointName) + { + auto checkpointId = GetCheckpointIdByName(connection, checkpointName); + if (!checkpointId) + { + return {}; + } + + return CheckpointContextTable::GetAvailableData(connection, checkpointId.value()); + } + + SQLite::rowid_t CheckpointRecordInterface::AddCheckpoint(SQLite::Connection& connection, std::string checkpointName) + { + SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "addcheckpoint_v1_0"); + SQLite::rowid_t checkpointId = CheckpointTable::AddCheckpoint(connection, checkpointName); + savepoint.Commit(); + return checkpointId; + } + + void CheckpointRecordInterface::AddContextData(SQLite::Connection& connection, std::string checkpointName, int dataId, std::string name, std::vector values) + { + auto checkpointId = GetCheckpointIdByName(connection, checkpointName); + THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_NOT_FOUND), !checkpointId); + + SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "addcontextdata_v1_0"); + SQLite::rowid_t rowId = CheckpointContextTable::AddContextData(connection, checkpointId.value(), dataId, name, values); + savepoint.Commit(); + return rowId; + } + SQLite::rowid_t CheckpointRecordInterface::SetMetadata(SQLite::Connection& connection, std::string_view name, std::string_view value) diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointTable.cpp b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointTable.cpp index fef5b45324..b353c7312a 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointTable.cpp +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointTable.cpp @@ -32,6 +32,24 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 savepoint.Commit(); } + std::vector CheckpointTable::GetCheckpoints(SQLite::Connection& connection) + { + SQLite::Builder::StatementBuilder builder; + builder.Select(s_CheckpointTable_Name_Column).From(s_CheckpointTable_Table_Name).OrderBy(SQLite::RowIDName).Descending(); + + SQLite::Statement select = builder.Prepare(connection); + + std::vector checkpoints; + if (select.Step()) + { + checkpoints.emplace_back(select.GetColumn(0)); + } + else + { + return {}; + } + } + std::string CheckpointTable::GetLastCheckpoint(SQLite::Connection& connection) { // Sort by descending and get last checkpoint. diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointTable.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointTable.h index e727deab6f..1a537a041d 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointTable.h +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointTable.h @@ -16,6 +16,8 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 // Creates the table with named indices. static void Create(SQLite::Connection& connection); + static std::vector GetCheckpoints(SQLite::Connection& connection); + static std::string GetLastCheckpoint(SQLite::Connection& connection); // Adds a checkpoint. diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/ICheckpointRecord.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/ICheckpointRecord.h index 03963a40e7..ca9b13b31d 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/Schema/ICheckpointRecord.h +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/ICheckpointRecord.h @@ -20,50 +20,49 @@ namespace AppInstaller::Repository::Microsoft::Schema // Returns a bool value indicating whether all checkpoint tables are empty. virtual bool IsEmpty(SQLite::Connection& connection) = 0; - virtual std::vector GetCheckpoints(SQLite::Connection& connection) = 0; - - virtual std:: - - - - + virtual std::vector GetAvailableCheckpoints(SQLite::Connection& connection) = 0; + virtual std::map> GetContextDataByContextId(SQLite::Connection& connection, std::string checkpointName, int64_t dataId) = 0; + virtual std::vector GetAvailableDataTypes(SQLite::Connection& connection, std::string checkpointName) = 0; + // Adding a checkpoint + virtual SQLite::rowid_t AddCheckpoint(SQLite::Connection& connection) = 0; + virtual void AddContextData(SQLite::Connection& connection, std::string checkpointName, int dataId, std::string name, std::vector values) = 0; // Sets the metadata value. - virtual SQLite::rowid_t SetMetadata(SQLite::Connection& connection, std::string_view name, std::string_view value) = 0; + //virtual SQLite::rowid_t SetMetadata(SQLite::Connection& connection, std::string_view name, std::string_view value) = 0; - // Gets the metadata value. - virtual std::string GetMetadata(SQLite::Connection& connection, std::string_view name) = 0; + //// Gets the metadata value. + //virtual std::string GetMetadata(SQLite::Connection& connection, std::string_view name) = 0; - // Gets the latest checkpoint. - virtual std::string GetLastCheckpoint(SQLite::Connection& connection) = 0; + //// Gets the latest checkpoint. + //virtual std::string GetLastCheckpoint(SQLite::Connection& connection) = 0; - // Returns a value indicating whether the checkpoint exists. - virtual bool CheckpointExists(SQLite::Connection& connection, std::string_view checkpointName) = 0; + //// Returns a value indicating whether the checkpoint exists. + //virtual bool CheckpointExists(SQLite::Connection& connection, std::string_view checkpointName) = 0; - // Gets the available context data from a checkpoint. - virtual std::vector GetAvailableContextData(SQLite::Connection& connection, std::string_view checkpointName) = 0; + //// Gets the available context data from a checkpoint. + //virtual std::vector GetAvailableContextData(SQLite::Connection& connection, std::string_view checkpointName) = 0; - // Adds a checkpoint. - virtual SQLite::rowid_t AddCheckpoint(SQLite::Connection& connection, std::string_view checkpointName) = 0; + //// Adds a checkpoint. + //virtual SQLite::rowid_t AddCheckpoint(SQLite::Connection& connection, std::string_view checkpointName) = 0; - // Adds a context data value for a checkpoint. - virtual SQLite::rowid_t AddContextData(SQLite::Connection& connection, std::string_view checkpointName, int contextData, std::string_view name, std::string_view value, int index) = 0; + //// Adds a context data value for a checkpoint. + //virtual SQLite::rowid_t AddContextData(SQLite::Connection& connection, std::string_view checkpointName, int contextData, std::string_view name, std::string_view value, int index) = 0; - // Gets the context data values from a checkpoint. - virtual std::vector GetContextData(SQLite::Connection& connection, std::string_view checkpointName, int contextData) = 0; + //// Gets the context data values from a checkpoint. + //virtual std::vector GetContextData(SQLite::Connection& connection, std::string_view checkpointName, int contextData) = 0; - // Gets the context data values by property name from a checkpoint. - virtual std::vector GetContextDataByName(SQLite::Connection& connection, std::string_view checkpointName, int contextData, std::string_view name) = 0; + //// Gets the context data values by property name from a checkpoint. + //virtual std::vector GetContextDataByName(SQLite::Connection& connection, std::string_view checkpointName, int contextData, std::string_view name) = 0; - // Removes the context data from a checkpoint. - virtual void RemoveContextData(SQLite::Connection& connection, std::string_view checkpointName, int contextData) = 0; + //// Removes the context data from a checkpoint. + //virtual void RemoveContextData(SQLite::Connection& connection, std::string_view checkpointName, int contextData) = 0; }; } \ No newline at end of file From bbee38e33ffbf851afefca354105d67da05f3762 Mon Sep 17 00:00:00 2001 From: Ryan Fu Date: Wed, 30 Aug 2023 11:19:14 -0700 Subject: [PATCH 28/43] save work --- src/AppInstallerCLICore/CheckpointManager.cpp | 7 + .../CheckpointRecordInterface.h | 2 +- .../CheckpointRecordInterface_1_0.cpp | 183 +++++++++--------- .../Microsoft/Schema/ICheckpointRecord.h | 9 +- 4 files changed, 105 insertions(+), 96 deletions(-) diff --git a/src/AppInstallerCLICore/CheckpointManager.cpp b/src/AppInstallerCLICore/CheckpointManager.cpp index d455d186f8..244d6331cd 100644 --- a/src/AppInstallerCLICore/CheckpointManager.cpp +++ b/src/AppInstallerCLICore/CheckpointManager.cpp @@ -13,6 +13,8 @@ namespace AppInstaller::Checkpoints namespace { constexpr std::string_view s_checkpoints_filename = "checkpoints.db"sv; + constexpr std::string_view s_StartCheckpoint = "start"sv; + constexpr std::string_view s_Checkpoints = "Checkpoints"sv; constexpr std::string_view s_ClientVersion = "ClientVersion"sv; @@ -79,6 +81,11 @@ namespace AppInstaller::Checkpoints Checkpoint CheckpointRecord::GetStartingCheckpoint() { + int rowId = GetCheckpointIdByName(s_StartCheckpoint); + Checkpoint checkpoint{ rowId }; + checkpoint.GetData(AutomaticCheckpointData::Arguments); + + // Get just the initial first checkpoint. return m_interface->GetCheckpointByName("start"sv); } diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointRecordInterface.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointRecordInterface.h index f1d0f4b653..942a12d2bc 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointRecordInterface.h +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointRecordInterface.h @@ -16,7 +16,7 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 std::vector GetAvailableCheckpoints(SQLite::Connection& connection) override; - std::map> GetContextDataByContextId(SQLite::Connection& connection, std::string checkpointName, int64_t dataId) override; + std::map> GetContextDataByContextId(SQLite::Connection& connection, std::string checkpointName, int64_t dataId) override; std::vector GetAvailableDataTypes(SQLite::Connection& connection, std::string checkpointName) override; diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointRecordInterface_1_0.cpp b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointRecordInterface_1_0.cpp index 045c3538b5..9ae33c3dbc 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointRecordInterface_1_0.cpp +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointRecordInterface_1_0.cpp @@ -47,7 +47,7 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 return CheckpointTable::GetCheckpoints(connection); } - std::map> CheckpointRecordInterface::GetContextDataByContextId(SQLite::Connection& connection, std::string checkpointName, int64_t dataId) + std::map> CheckpointRecordInterface::GetContextDataByContextId(SQLite::Connection& connection, std::string checkpointName, int64_t dataId) { auto checkpointId = GetCheckpointIdByName(connection, checkpointName); if (!checkpointId) @@ -92,98 +92,105 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_NOT_FOUND), !checkpointId); SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "addcontextdata_v1_0"); - SQLite::rowid_t rowId = CheckpointContextTable::AddContextData(connection, checkpointId.value(), dataId, name, values); - savepoint.Commit(); - return rowId; - } - - + + // This may have issues if the index is not properly correlated + // Get latest context data. + int index = 0; - SQLite::rowid_t CheckpointRecordInterface::SetMetadata(SQLite::Connection& connection, std::string_view name, std::string_view value) - { - SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "setMetadata_v1_0"); - SQLite::rowid_t argumentId = CheckpointMetadataTable::SetNamedValue(connection, name, value); - savepoint.Commit(); - return argumentId; - } - - bool CheckpointRecordInterface::CheckpointExists(SQLite::Connection& connection, std::string_view checkpointName) - { - return GetExistingCheckpointId(connection, checkpointName).has_value(); - } - - std::string CheckpointRecordInterface::GetMetadata(SQLite::Connection& connection, std::string_view name) - { - return CheckpointMetadataTable::GetNamedValue(connection, name); - } - - std::string CheckpointRecordInterface::GetLastCheckpoint(SQLite::Connection& connection) - { - return CheckpointTable::GetLastCheckpoint(connection); - } - - std::vector CheckpointRecordInterface::GetAvailableContextData(SQLite::Connection& connection, std::string_view checkpointName) - { - auto existingCheckpointId = GetExistingCheckpointId(connection, checkpointName); - if (!existingCheckpointId) + for (const auto& value : values) { - return {}; + CheckpointContextTable::AddContextData(connection, checkpointId.value(), dataId, name, value, index); + index++; } - return CheckpointContextTable::GetAvailableData(connection, existingCheckpointId.value()); - } - - SQLite::rowid_t CheckpointRecordInterface::AddCheckpoint(SQLite::Connection& connection, std::string_view checkpointName) - { - auto existingCheckpointId = GetExistingCheckpointId(connection, checkpointName); - THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS), existingCheckpointId); - - SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "addcheckpoint_v1_0"); - SQLite::rowid_t checkpointId = CheckpointTable::AddCheckpoint(connection, checkpointName); - savepoint.Commit(); - return checkpointId; - } - - SQLite::rowid_t CheckpointRecordInterface::AddContextData(SQLite::Connection& connection, std::string_view checkpointName, int contextData, std::string_view name, std::string_view value, int index) - { - auto existingCheckpointId = GetExistingCheckpointId(connection, checkpointName); - THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_NOT_FOUND), !existingCheckpointId); - - SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "addcontextdata_v1_0"); - SQLite::rowid_t rowId = CheckpointContextTable::AddContextData(connection, existingCheckpointId.value(), contextData, name, value, index); savepoint.Commit(); - return rowId; } - std::vector CheckpointRecordInterface::GetContextData(SQLite::Connection& connection, std::string_view checkpointName, int contextData) - { - auto existingCheckpointId = GetExistingCheckpointId(connection, checkpointName); - if (!existingCheckpointId) - { - return {}; - } - - return CheckpointContextTable::GetContextData(connection, existingCheckpointId.value(), contextData); - } - - std::vector CheckpointRecordInterface::GetContextDataByName(SQLite::Connection& connection, std::string_view checkpointName, int contextData, std::string_view name) - { - auto existingCheckpointId = GetExistingCheckpointId(connection, checkpointName); - if (!existingCheckpointId) - { - return {}; - } - - return CheckpointContextTable::GetContextDataByName(connection, existingCheckpointId.value(), contextData, name); - } - - void CheckpointRecordInterface::RemoveContextData(SQLite::Connection& connection, std::string_view checkpointName, int contextData) - { - auto existingCheckpointId = GetExistingCheckpointId(connection, checkpointName); - THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_NOT_FOUND), !existingCheckpointId); - - SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "removecontextdata_v1_0"); - CheckpointContextTable::RemoveContextData(connection, existingCheckpointId.value(), contextData); - savepoint.Commit(); - } + //SQLite::rowid_t CheckpointRecordInterface::SetMetadata(SQLite::Connection& connection, std::string_view name, std::string_view value) + //{ + // SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "setMetadata_v1_0"); + // SQLite::rowid_t argumentId = CheckpointMetadataTable::SetNamedValue(connection, name, value); + // savepoint.Commit(); + // return argumentId; + //} + + //bool CheckpointRecordInterface::CheckpointExists(SQLite::Connection& connection, std::string_view checkpointName) + //{ + // return GetExistingCheckpointId(connection, checkpointName).has_value(); + //} + + //std::string CheckpointRecordInterface::GetMetadata(SQLite::Connection& connection, std::string_view name) + //{ + // return CheckpointMetadataTable::GetNamedValue(connection, name); + //} + + //std::string CheckpointRecordInterface::GetLastCheckpoint(SQLite::Connection& connection) + //{ + // return CheckpointTable::GetLastCheckpoint(connection); + //} + + //std::vector CheckpointRecordInterface::GetAvailableContextData(SQLite::Connection& connection, std::string_view checkpointName) + //{ + // auto existingCheckpointId = GetExistingCheckpointId(connection, checkpointName); + // if (!existingCheckpointId) + // { + // return {}; + // } + + // return CheckpointContextTable::GetAvailableData(connection, existingCheckpointId.value()); + //} + + //SQLite::rowid_t CheckpointRecordInterface::AddCheckpoint(SQLite::Connection& connection, std::string_view checkpointName) + //{ + // auto existingCheckpointId = GetExistingCheckpointId(connection, checkpointName); + // THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS), existingCheckpointId); + + // SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "addcheckpoint_v1_0"); + // SQLite::rowid_t checkpointId = CheckpointTable::AddCheckpoint(connection, checkpointName); + // savepoint.Commit(); + // return checkpointId; + //} + + //SQLite::rowid_t CheckpointRecordInterface::AddContextData(SQLite::Connection& connection, std::string_view checkpointName, int contextData, std::string_view name, std::string_view value, int index) + //{ + // auto existingCheckpointId = GetExistingCheckpointId(connection, checkpointName); + // THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_NOT_FOUND), !existingCheckpointId); + + // SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "addcontextdata_v1_0"); + // SQLite::rowid_t rowId = CheckpointContextTable::AddContextData(connection, existingCheckpointId.value(), contextData, name, value, index); + // savepoint.Commit(); + // return rowId; + //} + + //std::vector CheckpointRecordInterface::GetContextData(SQLite::Connection& connection, std::string_view checkpointName, int contextData) + //{ + // auto existingCheckpointId = GetExistingCheckpointId(connection, checkpointName); + // if (!existingCheckpointId) + // { + // return {}; + // } + + // return CheckpointContextTable::GetContextData(connection, existingCheckpointId.value(), contextData); + //} + + //std::vector CheckpointRecordInterface::GetContextDataByName(SQLite::Connection& connection, std::string_view checkpointName, int contextData, std::string_view name) + //{ + // auto existingCheckpointId = GetExistingCheckpointId(connection, checkpointName); + // if (!existingCheckpointId) + // { + // return {}; + // } + + // return CheckpointContextTable::GetContextDataByName(connection, existingCheckpointId.value(), contextData, name); + //} + + //void CheckpointRecordInterface::RemoveContextData(SQLite::Connection& connection, std::string_view checkpointName, int contextData) + //{ + // auto existingCheckpointId = GetExistingCheckpointId(connection, checkpointName); + // THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_NOT_FOUND), !existingCheckpointId); + + // SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "removecontextdata_v1_0"); + // CheckpointContextTable::RemoveContextData(connection, existingCheckpointId.value(), contextData); + // savepoint.Commit(); + //} } \ No newline at end of file diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/ICheckpointRecord.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/ICheckpointRecord.h index ca9b13b31d..03a7278efe 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/Schema/ICheckpointRecord.h +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/ICheckpointRecord.h @@ -22,19 +22,14 @@ namespace AppInstaller::Repository::Microsoft::Schema virtual std::vector GetAvailableCheckpoints(SQLite::Connection& connection) = 0; - virtual std::map> GetContextDataByContextId(SQLite::Connection& connection, std::string checkpointName, int64_t dataId) = 0; + virtual std::map> GetContextDataByContextId(SQLite::Connection& connection, std::string checkpointName, int64_t dataId) = 0; virtual std::vector GetAvailableDataTypes(SQLite::Connection& connection, std::string checkpointName) = 0; - - - // Adding a checkpoint - - virtual SQLite::rowid_t AddCheckpoint(SQLite::Connection& connection) = 0; + virtual SQLite::rowid_t AddCheckpoint(SQLite::Connection& connection, std::string checkpointName) = 0; virtual void AddContextData(SQLite::Connection& connection, std::string checkpointName, int dataId, std::string name, std::vector values) = 0; - // Sets the metadata value. //virtual SQLite::rowid_t SetMetadata(SQLite::Connection& connection, std::string_view name, std::string_view value) = 0; From b0e64a662fa3f5b4f997479dcc8a945b9f0b171a Mon Sep 17 00:00:00 2001 From: Ryan Fu Date: Fri, 8 Sep 2023 15:13:07 -0700 Subject: [PATCH 29/43] save work --- .../AppInstallerCLICore.vcxproj | 1 + .../AppInstallerCLICore.vcxproj.filters | 3 + src/AppInstallerCLICore/COMContext.cpp | 2 + src/AppInstallerCLICore/COMContext.h | 2 +- src/AppInstallerCLICore/Checkpoint.h | 85 ++++++++ src/AppInstallerCLICore/CheckpointManager.cpp | 93 ++++----- src/AppInstallerCLICore/CheckpointManager.h | 145 ++------------ .../Commands/InstallCommand.cpp | 12 +- .../Commands/ResumeCommand.cpp | 43 +++-- src/AppInstallerCLICore/ExecutionContext.cpp | 181 ++---------------- src/AppInstallerCLICore/ExecutionContext.h | 24 +-- .../Workflows/ResumeFlow.cpp | 2 - .../Workflows/WorkflowBase.cpp | 8 +- .../AppInstallerRepositoryCore.vcxproj | 4 +- ...AppInstallerRepositoryCore.vcxproj.filters | 6 - .../Microsoft/CheckpointRecord.cpp | 136 +++++-------- .../Microsoft/CheckpointRecord.h | 60 +++--- .../Checkpoint_1_0/CheckpointContextTable.cpp | 59 ++++-- .../Checkpoint_1_0/CheckpointContextTable.h | 12 +- .../CheckpointMetadataTable.cpp | 57 ------ .../Checkpoint_1_0/CheckpointMetadataTable.h | 25 --- .../CheckpointRecordInterface.h | 29 +-- .../CheckpointRecordInterface_1_0.cpp | 172 ++++------------- .../Schema/Checkpoint_1_0/CheckpointTable.cpp | 9 +- .../Schema/Checkpoint_1_0/CheckpointTable.h | 5 +- .../Microsoft/Schema/ICheckpointRecord.h | 38 +--- .../Microsoft/Schema/Version.cpp | 22 +++ .../Microsoft/Schema/Version.h | 9 +- 28 files changed, 426 insertions(+), 818 deletions(-) create mode 100644 src/AppInstallerCLICore/Checkpoint.h delete mode 100644 src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointMetadataTable.cpp delete mode 100644 src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointMetadataTable.h diff --git a/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj b/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj index f8ce7f0465..34b5a83569 100644 --- a/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj +++ b/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj @@ -350,6 +350,7 @@ + diff --git a/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj.filters b/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj.filters index b2f33e04b2..58d31280f8 100644 --- a/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj.filters +++ b/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj.filters @@ -236,6 +236,9 @@ Header Files + + Header Files + diff --git a/src/AppInstallerCLICore/COMContext.cpp b/src/AppInstallerCLICore/COMContext.cpp index 17ae73ba86..936a46a7d2 100644 --- a/src/AppInstallerCLICore/COMContext.cpp +++ b/src/AppInstallerCLICore/COMContext.cpp @@ -9,6 +9,8 @@ namespace AppInstaller::CLI::Execution { static constexpr std::string_view s_comLogFileNamePrefix = "WinGetCOM"sv; + COMContext::~COMContext() = default; + NullStream::NullStream() { m_nullOut.reset(new std::ostream(&m_nullStreamBuf)); diff --git a/src/AppInstallerCLICore/COMContext.h b/src/AppInstallerCLICore/COMContext.h index 472907d76e..a20ffd2b79 100644 --- a/src/AppInstallerCLICore/COMContext.h +++ b/src/AppInstallerCLICore/COMContext.h @@ -49,7 +49,7 @@ namespace AppInstaller::CLI::Execution SetFlags(CLI::Execution::ContextFlag::DisableInteractivity); } - ~COMContext() = default; + ~COMContext(); // IProgressSink void BeginProgress() override; diff --git a/src/AppInstallerCLICore/Checkpoint.h b/src/AppInstallerCLICore/Checkpoint.h new file mode 100644 index 0000000000..a67178db68 --- /dev/null +++ b/src/AppInstallerCLICore/Checkpoint.h @@ -0,0 +1,85 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once +#include "ExecutionContextData.h" +#include "Microsoft/CheckpointRecord.h" +#include + +using namespace AppInstaller::Repository::Microsoft; + +namespace AppInstaller::Checkpoints +{ + enum AutomaticCheckpointData + { + ClientVersion, + CommandName, + Arguments + }; + + struct CheckpointManager; + + // A representation of a row in the Checkpoint table. + template + struct Checkpoint + { + // call this here static_assert(dataType) + // static_assert() + + friend CheckpointManager; + + std::vector GetCheckpointDataTypes() + { + return m_checkpointRecord->GetDataTypes(m_checkpointId); + } + + // Returns a boolean value indicating whether the field name exists. + bool Has(T dataType, std::string fieldName) + { + // to integral wrapper around the data type + + // return a boolean to determine if the context has the field name that exists. + return m_checkpointRecord->HasDataField(m_checkpointId, dataType, fieldName); + } + + // Gets all available field names. + std::vector GetFieldNames(T dataType) + { + return m_checkpointRecord->GetDataFieldNames(m_checkpointId, dataType); + } + + std::string GetOne(T dataType) + { + return m_checkpointRecord->GetDataSingleValue(m_checkpointId, dataType); + // For usage with a data types that only have a single file. + } + + std::vector GetMany(T dataType) + { + + } + + std::vector Get(T dataType, std::string fieldName) + { + // Dispatch function? + // Passing in the object you want it to fill and utilize overloads + return m_checkpointRecord->GetDataFieldValue(m_checkpointId, dataType, fieldName); + } + + void SetOne(T dataType, std::string value) + { + + } + + void Set(T dataType, std::string_view fieldName, std::vector values) + { + m_checkpointRecord->SetDataFieldValue(m_checkpointId, dataType, fieldName, values); + } + + private: + Checkpoint(std::shared_ptr checkpointRecord, AppInstaller::Repository::Microsoft::CheckpointRecord::IdType checkpointId) : + m_checkpointRecord(checkpointRecord), m_checkpointId(checkpointId){}; + + AppInstaller::Repository::Microsoft::CheckpointRecord::IdType m_checkpointId; + std::shared_ptr m_checkpointRecord; + }; +} \ No newline at end of file diff --git a/src/AppInstallerCLICore/CheckpointManager.cpp b/src/AppInstallerCLICore/CheckpointManager.cpp index 244d6331cd..68eddcb09a 100644 --- a/src/AppInstallerCLICore/CheckpointManager.cpp +++ b/src/AppInstallerCLICore/CheckpointManager.cpp @@ -2,8 +2,8 @@ // Licensed under the MIT License. #include "pch.h" #include "CheckpointManager.h" -#include "Microsoft/Schema/Checkpoint_1_0/CheckpointRecordInterface.h" #include +#include "ExecutionContextData.h" namespace AppInstaller::Checkpoints { @@ -13,9 +13,9 @@ namespace AppInstaller::Checkpoints namespace { constexpr std::string_view s_checkpoints_filename = "checkpoints.db"sv; - constexpr std::string_view s_StartCheckpoint = "start"sv; - + // This checkpoint name is reserved for the starting checkpoint which captures the automatic metadata. + constexpr std::string_view s_StartCheckpoint = "start"sv; constexpr std::string_view s_Checkpoints = "Checkpoints"sv; constexpr std::string_view s_ClientVersion = "ClientVersion"sv; constexpr std::string_view s_CommandName = "CommandName"sv; @@ -34,26 +34,7 @@ namespace AppInstaller::Checkpoints } } - CheckpointRecord CheckpointRecord::CreateNew(const std::string& filePath, Schema::Version version) - { - AICLI_LOG(Repo, Info, << "Creating new Checkpoint Index with version [" << version << "] at '" << filePath << "'"); - CheckpointRecord result{ filePath, version }; - - SQLite::Savepoint savepoint = SQLite::Savepoint::Create(result.m_dbconn, "CheckpointRecord_createnew"); - - // Use calculated version, as incoming version could be 'latest' - result.m_version.SetSchemaVersion(result.m_dbconn); - - result.m_interface->CreateTables(result.m_dbconn); - - result.SetLastWriteTime(); - - savepoint.Commit(); - - return result; - } - - std::filesystem::path CheckpointRecord::GetCheckpointRecordPath(GUID guid) + std::filesystem::path CheckpointManager::GetCheckpointRecordPath(GUID guid) { wchar_t checkpointGuid[256]; THROW_HR_IF(E_UNEXPECTED, !StringFromGUID2(guid, checkpointGuid, ARRAYSIZE(checkpointGuid))); @@ -74,51 +55,61 @@ namespace AppInstaller::Checkpoints return indexPath; } - bool CheckpointRecord::IsEmpty() + CheckpointManager::CheckpointManager() { - return m_interface->IsEmpty(m_dbconn); + std::ignore = CoCreateGuid(&m_resumeId); + const auto& checkpointRecordPath = GetCheckpointRecordPath(m_resumeId); + m_checkpointRecord = std::make_shared(CheckpointRecord::CreateNew(checkpointRecordPath.u8string())); } - Checkpoint CheckpointRecord::GetStartingCheckpoint() + CheckpointManager::CheckpointManager(GUID resumeId) { - int rowId = GetCheckpointIdByName(s_StartCheckpoint); - Checkpoint checkpoint{ rowId }; - checkpoint.GetData(AutomaticCheckpointData::Arguments); - + m_resumeId = resumeId; + const auto& checkpointRecordPath = GetCheckpointRecordPath(m_resumeId); + m_checkpointRecord = std::make_shared(CheckpointRecord::Open(checkpointRecordPath.u8string())); + } - // Get just the initial first checkpoint. - return m_interface->GetCheckpointByName("start"sv); + Checkpoint CheckpointManager::CreateAutomaticCheckpoint() + { + CheckpointRecord::IdType startCheckpointId = m_checkpointRecord->AddCheckpoint(s_StartCheckpoint); + Checkpoint checkpoint{ m_checkpointRecord, startCheckpointId }; + return checkpoint; } - std::map> CheckpointRecord::GetCheckpoints() + Checkpoint CheckpointManager::CreateCheckpoint(std::string_view checkpointName) { - // Get all of the available data items for a checkpoint.... - return m_interface->GetCheckpoints(); + CheckpointRecord::IdType startCheckpointId = m_checkpointRecord->AddCheckpoint(checkpointName); + Checkpoint checkpoint{ m_checkpointRecord, startCheckpointId }; + return checkpoint; } - std::unique_ptr CheckpointRecord::CreateICheckpointRecord() const + Checkpoint CheckpointManager::GetAutomaticCheckpoint() { - if (m_version == Schema::Version{ 1, 0 } || - m_version.MajorVersion == 1 || - m_version.IsLatest()) + std::optional startCheckpointId = m_checkpointRecord->GetCheckpointIdByName(s_StartCheckpoint); + + if (!startCheckpointId.has_value()) { - return std::make_unique(); + THROW_HR(E_UNEXPECTED); } - THROW_HR(HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED)); + return Checkpoint{ std::move(m_checkpointRecord), startCheckpointId.value() }; } - CheckpointRecord::CheckpointRecord(const std::string& target, SQLiteStorageBase::OpenDisposition disposition, Utility::ManagedFile&& indexFile) : - SQLiteStorageBase(target, disposition, std::move(indexFile)) + std::vector> CheckpointManager::GetCheckpoints() { - AICLI_LOG(Repo, Info, << "Opened Checkpoint Index with version [" << m_version << "], last write [" << GetLastWriteTime() << "]"); - m_interface = CreateICheckpointRecord(); - THROW_HR_IF(APPINSTALLER_CLI_ERROR_CANNOT_WRITE_TO_UPLEVEL_INDEX, disposition == SQLiteStorageBase::OpenDisposition::ReadWrite && m_version != m_interface->GetVersion()); - } + std::vector> checkpoints; + for (const auto& checkpoint : m_checkpointRecord->GetCheckpoints()) + { + // Exclude starting checkpoint from these context data related checkpoints. + if (checkpoint == s_StartCheckpoint) + { + continue; + } - CheckpointRecord::CheckpointRecord(const std::string& target, Schema::Version version) : SQLiteStorageBase(target, version) - { - m_interface = CreateICheckpointRecord(); - m_version = m_interface->GetVersion(); + CheckpointRecord::IdType checkpointId = m_checkpointRecord->GetCheckpointIdByName(checkpoint).value(); + checkpoints.emplace_back(Checkpoint{ std::move(m_checkpointRecord), checkpointId }); + } + + return checkpoints; } } diff --git a/src/AppInstallerCLICore/CheckpointManager.h b/src/AppInstallerCLICore/CheckpointManager.h index 97f7b3c306..112cbd14eb 100644 --- a/src/AppInstallerCLICore/CheckpointManager.h +++ b/src/AppInstallerCLICore/CheckpointManager.h @@ -1,149 +1,38 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once -#include -#include "SQLiteWrapper.h" -#include "Microsoft/Schema/ICheckpointRecord.h" -#include "Microsoft/SQLiteStorageBase.h" -#include #include "ExecutionContextData.h" +#include "Checkpoint.h" +#include -using namespace AppInstaller::Repository; -using namespace AppInstaller::Repository::SQLite; -using namespace AppInstaller::Repository::Microsoft; +namespace AppInstaller::Repository::Microsoft +{ + struct CheckpointRecord; +} namespace AppInstaller::Checkpoints { - // A representation of a single context data (all rows for a single checkpoint and single context data). - struct CheckpointData - { - CheckpointData(int64_t contextDataId) : m_contextDataId(contextDataId) {}; - - // Returns a boolean value indicating whether the field name exists. - bool Has(std::string fieldName) - { - return m_values.find(fieldName) != m_values.end(); - } - - // Gets all available field names. - std::vector GetFieldNames() - { - std::vector fieldNames; - for (auto it = m_values.begin(); it != m_values.end(); ++it) { - fieldNames.push_back(it->first); - } - return fieldNames; - } - - std::vector Get(std::string fieldName) - { - auto it = m_values.find(fieldName); - return it->second; - } - - void Set(std::string fieldName, std::vector values) - { - m_values.insert({ fieldName, values }); - } - - // This will be used to rebuild our data objects later... - //template - //T Get(std::string fieldName); - - //template - //void Set(std::string fieldName, T data); - - private: - int64_t m_contextDataId; - std::map> m_values; - }; - - // The other enum would be execution context data. - - // Enum to define the types of checkpoint data - enum AutomaticCheckpointData - { - ClientVersion, - CommandName, - Arguments - }; - - struct CheckpointRecord : SQLiteStorageBase + // Owns the lifetime of a checkpoint data base and creates the checkpoints. + struct CheckpointManager { - // An id that refers to a specific Checkpoint. - using IdType = SQLite::rowid_t; + CheckpointManager(); + CheckpointManager(GUID resumeId); - CheckpointRecord(const CheckpointRecord&) = delete; - CheckpointRecord& operator=(const CheckpointRecord&) = delete; - - CheckpointRecord(CheckpointRecord&&) = default; - CheckpointRecord& operator=(CheckpointRecord&&) = default; - - // Opens an existing CheckpointRecord database. - static CheckpointRecord Open(const std::string& filePath, OpenDisposition disposition = OpenDisposition::ReadWrite, Utility::ManagedFile&& indexFile = {}) - { - return { filePath, disposition, std::move(indexFile) }; - } - - // Create a new CheckpointRecord database. - static CheckpointRecord CreateNew(const std::string& filePath, Schema::Version version = Schema::Version::Latest()); + ~CheckpointManager() = default; // Gets the file path of the CheckpointRecord database. static std::filesystem::path GetCheckpointRecordPath(GUID guid); - // Returns a value indicating whether the record is empty. - bool IsEmpty(); - - Checkpoint GetStartingCheckpoint(); - - std::map> GetCheckpoints(); - - private: - // Constructor used to open an existing index. - CheckpointRecord(const std::string& target, SQLiteStorageBase::OpenDisposition disposition, Utility::ManagedFile&& indexFile); - - // Constructor used to create a new index. - CheckpointRecord(const std::string& target, Schema::Version version); - - // Creates the ICheckpointRecord interface object for this version. - std::unique_ptr CreateICheckpointRecord() const; - - std::unique_ptr m_interface; - }; - - enum class CheckpointNames - { - Automatic, - Installer, - }; - - // A representation of a row in the Checkpoint table. - template - struct Checkpoint - { - friend CheckpointRecord; - - Checkpoint(std::vector checkpointData) : m_checkpointData(checkpointData) {}; + Checkpoint CreateCheckpoint(std::string_view checkpointName); - CheckpointData GetData(T type) - { - return m_checkpointDataMap[type]; - } + Checkpoint CreateAutomaticCheckpoint(); - std::vector GetCheckpointDataTypes() - { - std::vector dataTypes; - for (const auto& data : m_checkpointDataMap) - { - dataTypes.emplace_back(data.first); - } + Checkpoint GetAutomaticCheckpoint(); - return dataTypes; - } + std::vector> GetCheckpoints(); private: - Checkpoint(SQLite::rowid_t id) : m_rowid(id) {}; - SQLite::rowid_t m_rowId; - std::map m_checkpointDataMap; + GUID m_resumeId = {}; + std::shared_ptr m_checkpointRecord; }; } \ No newline at end of file diff --git a/src/AppInstallerCLICore/Commands/InstallCommand.cpp b/src/AppInstallerCLICore/Commands/InstallCommand.cpp index 66ec5592f3..05b84572d5 100644 --- a/src/AppInstallerCLICore/Commands/InstallCommand.cpp +++ b/src/AppInstallerCLICore/Commands/InstallCommand.cpp @@ -14,7 +14,6 @@ namespace AppInstaller::CLI { - using namespace AppInstaller::CLI::Checkpoint; using namespace AppInstaller::CLI::Execution; using namespace AppInstaller::CLI::Workflow; using namespace AppInstaller::Manifest; @@ -106,29 +105,24 @@ namespace AppInstaller::CLI void InstallCommand::Resume(Context& context) const { - const auto& lastCheckpoint = context.CheckpointManager.GetLastCheckpoint(); - context.SetTargetCheckpoint(lastCheckpoint); - // TODO: Load context data from checkpoint for install command. - context.SetFlags(Execution::ContextFlag::Resume); ExecuteInternal(context); } void InstallCommand::ExecuteInternal(Context& context) const { - context.SetFlags(ContextFlag::ShowSearchResultsOnPartialFailure); + context.Checkpoint("exampleCheckpoint", {}); + + context.SetFlags(ContextFlag::ShowSearchResultsOnPartialFailure); if (context.Args.Contains(Execution::Args::Type::Manifest)) { - context.CreateCheckpoint("start", {}); - context << Workflow::ReportExecutionStage(ExecutionStage::Discovery) << Workflow::GetManifestFromArg << Workflow::SelectInstaller << Workflow::EnsureApplicableInstaller << - // Example: Checkpoint an installer Workflow::InstallSinglePackage; } else diff --git a/src/AppInstallerCLICore/Commands/ResumeCommand.cpp b/src/AppInstallerCLICore/Commands/ResumeCommand.cpp index 55e6711f54..a8b6cff582 100644 --- a/src/AppInstallerCLICore/Commands/ResumeCommand.cpp +++ b/src/AppInstallerCLICore/Commands/ResumeCommand.cpp @@ -6,14 +6,15 @@ #include "ResumeCommand.h" #include "RootCommand.h" #include "CheckpointManager.h" -#include "Microsoft/CheckpointRecord.h" #include "Workflows/ResumeFlow.h" +#include "Checkpoint.h" + +using namespace AppInstaller::Checkpoints; namespace AppInstaller::CLI { using namespace std::string_view_literals; using namespace Execution; - using namespace Checkpoint; std::vector ResumeCommand::GetArguments() const { @@ -42,31 +43,30 @@ namespace AppInstaller::CLI std::string resumeGuidString{ context.Args.GetArg(Execution::Args::Type::ResumeId) }; GUID checkpointId = Utility::ConvertToGuid(resumeGuidString); - // Change this to a CheckpointRecord helepr function like Exists - if (!std::filesystem::exists(AppInstaller::Repository::Microsoft::CheckpointRecord::GetCheckpointRecordPath(checkpointId))) + if (!std::filesystem::exists(Checkpoints::CheckpointManager::GetCheckpointRecordPath(checkpointId))) { context.Reporter.Error() << Resource::String::ResumeIdNotFoundError(Utility::LocIndView{ resumeGuidString }) << std::endl; AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_RESUME_ID_NOT_FOUND); } Execution::Context resumeContext = context.CreateEmptyContext(); - resumeContext.CheckpointManager.LoadRecord(checkpointId); - const auto& checkpointClientVersion = resumeContext.CheckpointManager.GetClientVersion(); + Checkpoint automaticCheckpoint = resumeContext.LoadCheckpoint(checkpointId); + + const auto& checkpointClientVersion = automaticCheckpoint.GetOne(AutomaticCheckpointData::ClientVersion); if (checkpointClientVersion != AppInstaller::Runtime::GetClientVersion().get()) { context.Reporter.Error() << Resource::String::ClientVersionMismatchError(Utility::LocIndView{ checkpointClientVersion }) << std::endl; AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_CLIENT_VERSION_MISMATCH); } - std::string commandName = resumeContext.CheckpointManager.GetCommandName(); + const auto& checkpointCommandName = automaticCheckpoint.GetOne(AutomaticCheckpointData::CommandName); std::unique_ptr commandToResume; - // Find the command using the root command. - AICLI_LOG(CLI, Info, << "Resuming command: " << commandName); + AICLI_LOG(CLI, Info, << "Resuming command: " << checkpointCommandName); for (auto& command : std::make_unique()->GetCommands()) { - if (Utility::CaseInsensitiveEquals(commandName, command->Name())) + if (Utility::CaseInsensitiveEquals(checkpointCommandName, command->Name())) { commandToResume = std::move(command); break; @@ -75,18 +75,29 @@ namespace AppInstaller::CLI THROW_HR_IF_MSG(E_UNEXPECTED, !commandToResume, "Command to resume not found."); + for (const auto& fieldNames : automaticCheckpoint.GetFieldNames(AutomaticCheckpointData::Arguments)) + { + const auto& values = automaticCheckpoint.Get(AutomaticCheckpointData::Arguments, fieldNames); + Execution::Args::Type type = static_cast(std::stoi(fieldNames)); + if (values.empty()) + { + resumeContext.Args.AddArg(type); + } + else + { + for (const auto& value : values) + { + resumeContext.Args.AddArg(type, value); + } + } + } + resumeContext.SetExecutingCommand(commandToResume.get()); resumeContext.SetFlags(Execution::ContextFlag::Resume); // this should be captured by telemetry - resumeContext.Checkpoint("Start"sv); - - auto previousThreadGlobals = resumeContext.SetForCurrentThread(); - // Must be after or call initialize - // move local objects to resume context resumeContext.EnableSignalTerminationHandler(); - // double check this context, commandToResume->Resume(resumeContext); context.SetTerminationHR(resumeContext.GetTerminationHR()); diff --git a/src/AppInstallerCLICore/ExecutionContext.cpp b/src/AppInstallerCLICore/ExecutionContext.cpp index 24a98b935f..24ad0a9b18 100644 --- a/src/AppInstallerCLICore/ExecutionContext.cpp +++ b/src/AppInstallerCLICore/ExecutionContext.cpp @@ -7,11 +7,14 @@ #include "winget/UserSettings.h" #include "AppInstallerRuntime.h" #include "Command.h" +#include "CheckpointManager.h" +#include "Checkpoint.h" + +using namespace AppInstaller::Checkpoints; namespace AppInstaller::CLI::Execution { using namespace Settings; - using namespace Checkpoint; namespace { @@ -255,7 +258,6 @@ namespace AppInstaller::CLI::Execution { if (Settings::ExperimentalFeature::IsEnabled(ExperimentalFeature::Feature::Resume) && !IsTerminated()) { - CheckpointManager.DeleteRecord(); } if (m_disableSignalTerminationHandlerOnExit) @@ -422,183 +424,42 @@ namespace AppInstaller::CLI::Execution } #endif - bool Context::IsCurrentCheckpointAtTarget() + Checkpoint Context::LoadCheckpoint(GUID resumeId) { - return m_currentCheckpoint == m_targetCheckpoint; + m_checkpointManager = std::make_unique(resumeId); + return m_checkpointManager->GetAutomaticCheckpoint(); } - - // Representation of a single checkpoint - enum class CheckpointData - { - Args, - }; - - template - struct CheckpointContextData - { - CheckpointContextData(T data) {}; - - std::map> GetContextDataMap() { return m_contextDataMap; }; - - private: - std::map> m_contextDataMap; - }; - - template<> - CheckpointContextData::CheckpointContextData(Execution::Args args) + void Context::Checkpoint(std::string_view checkpointName, std::vector contextData) { - const auto& argTypes = args.GetTypes(); + UNREFERENCED_PARAMETER(checkpointName); - for (auto type : argTypes) + if (!m_checkpointManager) { - const auto& argName = Argument::ForType(type).Name(); - const auto& values = *args.GetArgs(type); - int index = 0; - int castedType = static_cast(type); - m_contextDataMap.emplace(castedType, values); - } - } - - struct Checkpoint - { - template - void AddContextData(CheckpointData type, const CheckpointContextData& data) - { - m_checkpointData[type] = std::move(data); - }; + m_checkpointManager = std::make_unique(); - std::map> GetDataItem(CheckpointData checkpointData) { }; + Checkpoints::Checkpoint startingCheckpoint = m_checkpointManager->CreateAutomaticCheckpoint(); - private: - std::string_view m_checkpointName; - std::map>> m_checkpointData; - }; + // Set client version. + startingCheckpoint.Set(AutomaticCheckpointData::ClientVersion, "clientVersion"sv, { AppInstaller::Runtime::GetClientVersion() }); - void Context::Checkpoint(std::string_view checkpointName, std::vector contextData) - { - // Create automatic checkpoints - // If the checkpoint index doesn't already create it, and also add the automatic checkpoints. - if (!m_checkpointRecord) - { - GUID checkpointId; - std::ignore = CoCreateGuid(&checkpointId); - const auto& checkpointRecordPath = Checkpoints::CheckpointRecord::GetCheckpointRecordPath(checkpointId); - m_checkpointRecord = std::make_unique(Checkpoints::CheckpointRecord::CreateNew(checkpointRecordPath.u8string())); - - std::vector checkpointData; - - // Create automatic checkpoint data: - Checkpoints::CheckpointData clientVersionData{ 0 }; - clientVersionData.Set("clientVersion", { AppInstaller::Runtime::GetClientVersion() }); - checkpointData.emplace_back(clientVersionData); - - Checkpoints::CheckpointData commandName{ 1 }; + // Set command const auto& executingCommand = m_executingCommand; if (executingCommand != nullptr) { - commandName.Set("commandName", { std::string{ m_executingCommand->Name() } }); + startingCheckpoint.Set(AutomaticCheckpointData::CommandName, "commandName"sv, { std::string{m_executingCommand->Name()} }); } - checkpointData.emplace_back(commandName); - - Checkpoints::CheckpointData commandArguments{ 2 }; + // Set arguments const auto& argTypes = Args.GetTypes(); - for (auto type : argTypes) { - const auto& argName = Argument::ForType(type).Name(); - const auto& values = *Args.GetArgs(type); - commandArguments.Set(std::string{ argName }, values); - } - checkpointData.emplace_back(commandArguments); - - Checkpoints::Checkpoint automaticCheckpoint; - - - - Checkpoints::Checkpoint checkpoint{ checkpointData }; - - - } - - // Create record if it does not exist. - if (!CheckpointManager.IsLoaded()) - { - CheckpointManager.CreateRecord(); - } - - if (CheckpointManager.Exists(checkpointName)) - { - if (contextData.empty()) - { - // Load arguments if there is no context data - const auto& availableData = CheckpointManager.GetAvailableContextData(checkpointName); - for (auto data : availableData) - { - const auto& values = CheckpointManager.GetContextData(checkpointName, data); - Execution::Args::Type type = static_cast(data); - if (values.empty()) - { - Args.AddArg(type); - } - else - { - for (const auto& value : values) - { - Args.AddArg(type, value); - } - } - } - } - else - { - // Load context data from checkpoint. - } - } - else - { - if (contextData.empty()) - { - CheckpointManager.SetClientVersion(AppInstaller::Runtime::GetClientVersion()); - - const auto& executingCommand = m_executingCommand; - if (executingCommand != nullptr) - { - CheckpointManager.SetCommandName(executingCommand->Name()); - } - - const auto& argTypes = Args.GetTypes(); + //const auto& argName = Argument::ForType(type).Type.Name(); - for (auto type : argTypes) - { - const auto& argName = Argument::ForType(type).Name(); - const auto& values = *Args.GetArgs(type); - int index = 0; - if (values.empty()) - { - // - CheckpointManager.AddContextData(checkpointName, static_cast(type), argName, {}, index); - } - else - { - for (const auto& value : values) - { - CheckpointManager.AddContextData(checkpointName, static_cast(type), argName, value, index); - index++; - } - } - } - } - else - { - // TODO: Capture context data. + // what the argument name is here:. + const auto& values = *Args.GetArgs(type); + startingCheckpoint.Set(AutomaticCheckpointData::Arguments, std::to_string(static_cast(type)), values); } } - - m_currentCheckpoint = checkpointName; } - - - } diff --git a/src/AppInstallerCLICore/ExecutionContext.h b/src/AppInstallerCLICore/ExecutionContext.h index c9e9c5abad..07fbe2366a 100644 --- a/src/AppInstallerCLICore/ExecutionContext.h +++ b/src/AppInstallerCLICore/ExecutionContext.h @@ -5,8 +5,8 @@ #include "ExecutionReporter.h" #include "ExecutionArgs.h" #include "ExecutionContextData.h" -#include "CheckpointManager.h" #include "CompletionData.h" +#include "CheckpointManager.h" #include @@ -90,7 +90,7 @@ namespace AppInstaller::CLI::Execution Reporter(reporter, Execution::Reporter::clone_t{}), m_threadGlobals(threadGlobals, ThreadLocalStorage::WingetThreadGlobals::create_sub_thread_globals_t{}) {} - virtual ~Context(); + virtual ~Context(); // The path for console input/output for all functionality. Reporter Reporter; @@ -166,18 +166,11 @@ namespace AppInstaller::CLI::Execution bool ShouldExecuteWorkflowTask(const Workflow::WorkflowTask& task); #endif - // Returns a value indicating whether the current checkpoint matches the target checkpoint. - bool IsCurrentCheckpointAtTarget(); - - // Sets the target checkpoint. - void SetTargetCheckpoint(std::string_view checkpointName) { m_targetCheckpoint = checkpointName; }; - - Checkpoints::Checkpoint CreateCheckpoint(std::string_view checkpointName, std::vector contextData); + // This should only be called by the resume , Sets the resume id for the checkpoint manager. + AppInstaller::Checkpoints::Checkpoint LoadCheckpoint(GUID resumeId); - // Writes the context data to the checkpoint record if the checkpoint does not yet exist. - // If no context data is provided, writes the automatic checkpoint metadata. - // If the checkpoint already exists, loads the context data from the saved checkpoint. - void Checkpoint(std::string_view checkpointName, std::vector contextData = {}); + // Creates a checkpoint for the provided context data. + void Checkpoint(std::string_view checkpointName, std::vector contextData); protected: // Copies the args that are also needed in a sub-context. E.g., silent @@ -198,8 +191,7 @@ namespace AppInstaller::CLI::Execution AppInstaller::ThreadLocalStorage::WingetThreadGlobals m_threadGlobals; AppInstaller::CLI::Command* m_executingCommand = nullptr; - std::unique_ptr m_checkpointRecord; - std::string_view m_targetCheckpoint = {}; - std::string_view m_currentCheckpoint = {}; + // Change this to unique pointer after you're done with implmementation. + std::unique_ptr m_checkpointManager; }; } diff --git a/src/AppInstallerCLICore/Workflows/ResumeFlow.cpp b/src/AppInstallerCLICore/Workflows/ResumeFlow.cpp index bcd8067ed2..46840825cd 100644 --- a/src/AppInstallerCLICore/Workflows/ResumeFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/ResumeFlow.cpp @@ -18,8 +18,6 @@ namespace AppInstaller::CLI::Workflow } else { - // If this is not a resume, simply capture the context data. - context.Checkpoint(m_checkpointName, m_contextData); } } } diff --git a/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp b/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp index 83a6b813ef..74330070d7 100644 --- a/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp +++ b/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp @@ -1277,10 +1277,10 @@ AppInstaller::CLI::Execution::Context& operator<<(AppInstaller::CLI::Execution:: { if (AppInstaller::Settings::ExperimentalFeature::IsEnabled(ExperimentalFeature::Feature::Resume)) { - if (WI_IsFlagSet(context.GetFlags(), AppInstaller::CLI::Execution::ContextFlag::Resume) && !context.IsCurrentCheckpointAtTarget()) - { - return context; - } + //if (WI_IsFlagSet(context.GetFlags(), AppInstaller::CLI::Execution::ContextFlag::Resume) && !context.IsCurrentCheckpointAtTarget()) + //{ + // return context; + //} } if (!context.IsTerminated()) diff --git a/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj b/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj index 27c048d5f3..fffa7be19e 100644 --- a/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj +++ b/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj @@ -388,7 +388,7 @@ - + @@ -397,7 +397,6 @@ - @@ -502,7 +501,6 @@ - diff --git a/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj.filters b/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj.filters index 0c73be575d..e8cb926b2e 100644 --- a/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj.filters +++ b/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj.filters @@ -402,9 +402,6 @@ Microsoft\Schema\Checkpoint_1_0 - - Microsoft\Schema\Checkpoint_1_0 - Microsoft\Schema\Checkpoint_1_0 @@ -644,9 +641,6 @@ Source Files - - Microsoft\Schema\Checkpoint_1_0 - Microsoft\Schema\Checkpoint_1_0 diff --git a/src/AppInstallerRepositoryCore/Microsoft/CheckpointRecord.cpp b/src/AppInstallerRepositoryCore/Microsoft/CheckpointRecord.cpp index cbbcca3b6a..3ca55b4006 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/CheckpointRecord.cpp +++ b/src/AppInstallerRepositoryCore/Microsoft/CheckpointRecord.cpp @@ -2,33 +2,10 @@ // Licensed under the MIT License. #include "pch.h" #include "CheckpointRecord.h" -#include "SQLiteStorageBase.h" #include "Schema/Checkpoint_1_0/CheckpointRecordInterface.h" namespace AppInstaller::Repository::Microsoft { - namespace - { - constexpr std::string_view s_checkpoints_filename = "checkpoints.db"sv; - - constexpr std::string_view s_Checkpoints = "Checkpoints"sv; - constexpr std::string_view s_ClientVersion = "ClientVersion"sv; - constexpr std::string_view s_CommandName = "CommandName"sv; - - std::string_view GetCheckpointMetadataString(CheckpointMetadata checkpointMetadata) - { - switch (checkpointMetadata) - { - case CheckpointMetadata::ClientVersion: - return s_ClientVersion; - case CheckpointMetadata::CommandName: - return s_CommandName; - default: - return "unknown"sv; - } - } - } - CheckpointRecord CheckpointRecord::CreateNew(const std::string& filePath, Schema::Version version) { AICLI_LOG(Repo, Info, << "Creating new Checkpoint Index with version [" << version << "] at '" << filePath << "'"); @@ -48,112 +25,103 @@ namespace AppInstaller::Repository::Microsoft return result; } - std::filesystem::path CheckpointRecord::GetCheckpointRecordPath(GUID guid) - { - wchar_t checkpointGuid[256]; - THROW_HR_IF(E_UNEXPECTED, !StringFromGUID2(guid, checkpointGuid, ARRAYSIZE(checkpointGuid))); - - const auto checkpointsDirectory = Runtime::GetPathTo(Runtime::PathName::CheckpointsLocation) / checkpointGuid; - - if (!std::filesystem::exists(checkpointsDirectory)) - { - std::filesystem::create_directories(checkpointsDirectory); - AICLI_LOG(Repo, Info, << "Creating checkpoint index directory: " << checkpointsDirectory); - } - else - { - THROW_HR_IF(ERROR_CANNOT_MAKE, !std::filesystem::is_directory(checkpointsDirectory)); - } - - auto indexPath = checkpointsDirectory / s_checkpoints_filename; - return indexPath; - } - bool CheckpointRecord::IsEmpty() { return m_interface->IsEmpty(m_dbconn); } - - - std::vector CheckpointRecord::GetAvailableData(std::string_view name) + std::optional CheckpointRecord::GetCheckpointIdByName(std::string_view checkpointName) { - return m_interface->GetAvailableContextData(m_dbconn, name); + return m_interface->GetCheckpointIdByName(m_dbconn, checkpointName); } - std::string CheckpointRecord::GetMetadata(CheckpointMetadata checkpointMetadata) - { - const auto& metadataName = GetCheckpointMetadataString(checkpointMetadata); - return m_interface->GetMetadata(m_dbconn, metadataName); - } - - CheckpointRecord::IdType CheckpointRecord::SetMetadata(CheckpointMetadata checkpointMetadata, std::string_view value) + CheckpointRecord::IdType CheckpointRecord::AddCheckpoint(std::string_view checkpointName) { std::lock_guard lockInterface{ *m_interfaceLock }; - AICLI_LOG(Repo, Verbose, << "Setting client version [" << value << "]"); + AICLI_LOG(Repo, Verbose, << "Adding checkpoint [" << checkpointName << "]"); - SQLite::Savepoint savepoint = SQLite::Savepoint::Create(m_dbconn, "checkpointindex_setmetadata"); + SQLite::Savepoint savepoint = SQLite::Savepoint::Create(m_dbconn, "checkpointrecord_addcheckpoint"); - const auto& metadataName = GetCheckpointMetadataString(checkpointMetadata); - IdType result = m_interface->SetMetadata(m_dbconn, metadataName, value); + IdType result = m_interface->AddCheckpoint(m_dbconn, checkpointName); SetLastWriteTime(); savepoint.Commit(); return result; } - CheckpointRecord::IdType CheckpointRecord::AddCheckpoint(std::string_view checkpointName) + std::vector CheckpointRecord::GetCheckpoints() + { + return m_interface->GetAvailableCheckpoints(m_dbconn); + } + + bool CheckpointRecord::HasDataField(IdType checkpointId, int type, std::string name) + { + return m_interface->HasCheckpointDataField(m_dbconn, checkpointId, type, name); + } + + std::vector CheckpointRecord::GetDataTypes(IdType checkpointId) + { + return m_interface->GetCheckpointDataTypes(m_dbconn, checkpointId); + } + + std::vector CheckpointRecord::GetDataFieldNames(IdType checkpointId, int dataType) + { + return m_interface->GetCheckpointDataFields(m_dbconn, checkpointId, dataType); + } + + // Set data single value can be reused for all of these methods. + + void CheckpointRecord::SetDataSingleValue(IdType checkpointId, int dataType, std::string value) { std::lock_guard lockInterface{ *m_interfaceLock }; - AICLI_LOG(Repo, Verbose, << "Adding checkpoint [" << checkpointName << "]"); + AICLI_LOG(Repo, Verbose, << "Setting checkpoint data [" << dataType << "] with value [" << value << "]"); - SQLite::Savepoint savepoint = SQLite::Savepoint::Create(m_dbconn, "checkpointrecord_addcheckpoint"); + SQLite::Savepoint savepoint = SQLite::Savepoint::Create(m_dbconn, "checkpointrecord_setdatasinglevalue"); - IdType result = m_interface->AddCheckpoint(m_dbconn, checkpointName); + m_interface->SetCheckpointDataValue(m_dbconn, checkpointId, dataType, {}, { value }); SetLastWriteTime(); savepoint.Commit(); - return result; } - bool CheckpointRecord::CheckpointExists(std::string_view checkpointName) + std::string CheckpointRecord::GetDataSingleValue(IdType checkpointId, int dataType) { - return m_interface->CheckpointExists(m_dbconn, checkpointName); + return m_interface->GetDataSingleValue(m_dbconn, checkpointId, dataType); } - CheckpointRecord::IdType CheckpointRecord::AddContextData(std::string_view checkpointName, int contextData, std::string_view name, std::string_view value, int index) + void CheckpointRecord::SetDataFieldSingleValue(IdType checkpointId, int dataType, std::string field, std::string value) { std::lock_guard lockInterface{ *m_interfaceLock }; - AICLI_LOG(Repo, Verbose, << "Setting context data [" << contextData << "] for [" << name << "] with value [" << value << "] value"); + AICLI_LOG(Repo, Verbose, << "Setting checkpoint data [" << dataType << "] with value [" << value << "]"); - SQLite::Savepoint savepoint = SQLite::Savepoint::Create(m_dbconn, "checkpointindex_addcontextdata"); - SQLite::rowid_t rowId = m_interface->AddContextData(m_dbconn, checkpointName, contextData, name, value, index); - savepoint.Commit(); - return rowId; - } + SQLite::Savepoint savepoint = SQLite::Savepoint::Create(m_dbconn, "checkpointrecord_setdatafieldsinglevalue"); - std::string CheckpointRecord::GetLastCheckpoint() - { - return m_interface->GetLastCheckpoint(m_dbconn); + m_interface->SetCheckpointDataValue(m_dbconn, checkpointId, dataType, field, { value }); + + SetLastWriteTime(); + savepoint.Commit(); } - std::vector CheckpointRecord::GetContextData(std::string_view checkpointName, int contextData) + std::string CheckpointRecord::GetDataFieldSingleValue(IdType checkpointId, int dataType, std::string_view field) { - return m_interface->GetContextData(m_dbconn, checkpointName, contextData); + return m_interface->GetCheckpointDataValues(m_dbconn, checkpointId, dataType, field); } - std::vector CheckpointRecord::GetContextDataByName(std::string_view checkpointName, int contextData, std::string_view name) + std::vector CheckpointRecord::GetDataFieldMultiValue(IdType checkpointId, int dataType, std::string field) { - return m_interface->GetContextDataByName(m_dbconn, checkpointName, contextData, name); + return std::vector(); } - void CheckpointRecord::RemoveContextData(std::string_view checkpointName, int contextData) + void CheckpointRecord::SetDataFieldMultiValue(IdType checkpointId, int dataType, std::string field, std::vector values) { std::lock_guard lockInterface{ *m_interfaceLock }; - AICLI_LOG(Repo, Verbose, << "Removing context data [" << contextData << "]"); + AICLI_LOG(Repo, Verbose, << "Setting checkpoint data [" << dataType << "]"); - SQLite::Savepoint savepoint = SQLite::Savepoint::Create(m_dbconn, "checkpointindex_addcontextdata"); - m_interface->RemoveContextData(m_dbconn, checkpointName, contextData); + SQLite::Savepoint savepoint = SQLite::Savepoint::Create(m_dbconn, "checkpointrecord_setdatafieldmultivalue"); + + m_interface->SetCheckpointDataValue(m_dbconn, checkpointId, dataType, field, values); + + SetLastWriteTime(); savepoint.Commit(); } diff --git a/src/AppInstallerRepositoryCore/Microsoft/CheckpointRecord.h b/src/AppInstallerRepositoryCore/Microsoft/CheckpointRecord.h index fa07898dbf..68f801f9f8 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/CheckpointRecord.h +++ b/src/AppInstallerRepositoryCore/Microsoft/CheckpointRecord.h @@ -8,12 +8,6 @@ namespace AppInstaller::Repository::Microsoft { - enum AutomaticCheckpointData - { - ClientVersion, - CommandName - }; - struct CheckpointRecord : SQLiteStorageBase { // An id that refers to a specific Checkpoint. @@ -25,55 +19,51 @@ namespace AppInstaller::Repository::Microsoft CheckpointRecord(CheckpointRecord&&) = default; CheckpointRecord& operator=(CheckpointRecord&&) = default; + // Create a new CheckpointRecord database. + static CheckpointRecord CreateNew(const std::string& filePath, Schema::Version version = Schema::Version::Latest()); + // Opens an existing CheckpointRecord database. static CheckpointRecord Open(const std::string& filePath, OpenDisposition disposition = OpenDisposition::ReadWrite, Utility::ManagedFile&& indexFile = {}) { return { filePath, disposition, std::move(indexFile) }; } - // Create a new CheckpointRecord database. - static CheckpointRecord CreateNew(const std::string& filePath, Schema::Version version = Schema::Version::Latest()); - - // Gets the file path of the CheckpointRecord database. - static std::filesystem::path GetCheckpointRecordPath(GUID guid); - // Returns a value indicating whether the record is empty. bool IsEmpty(); - Checkpoint GetAutomaticCheckpoint(); - - std::map> GetCheckpoints(); + // Returns the corresponding id of the checkpoint. + std::optional GetCheckpointIdByName(std::string_view checkpointName); + // Adds a new checkpoint name to the checkpoint table. + IdType AddCheckpoint(std::string_view checkpointName); - // Gets all available context data for a checkpoint. - std::vector GetAvailableData(std::string_view checkpointName); + std::vector GetCheckpoints(); - // Gets the specified metadata value. - std::string GetMetadata(CheckpointMetadata checkpointMetadata); + bool HasDataField(IdType checkpointId, int type, std::string name); - // Sets the specified metadata value. - IdType SetMetadata(CheckpointMetadata checkpointMetadata, std::string_view value); + // Returns the available data types for a given checkpoint id. + std::vector GetDataTypes(IdType checkpointId); - // Adds a new checkpoint. - IdType AddCheckpoint(std::string_view checkpointName); + // Returns the available field names for a given checkpoint context data. + std::vector GetDataFieldNames(IdType checkpointId, int dataType); - // Gets the latest checkpoint. - std::string GetLastCheckpoint(); + // Returns a single value for the given data type. + std::string GetDataSingleValue(IdType checkpointId, int dataType); - // Returns a value indicating whether the checkpoint exists. - bool CheckpointExists(std::string_view checkpointName); + // Sets a single value for the given data type. + void SetDataSingleValue(IdType checkpointId, int dataType, std::string value); - // Adds a context data value. - IdType AddContextData(std::string_view checkpointName, int contextData, std::string_view name, std::string_view value, int index); + // Gets a single value for a given data type field. + std::string GetDataFieldSingleValue(IdType checkpointId, int dataType, std::string_view field); - // Gets the context data values. - std::vector GetContextData(std::string_view checkpointName, int contextData); + // Sets a single value for a given data type field. + void SetDataFieldSingleValue(IdType checkpointId, int dataType, std::string field, std::string value); - // Gets the context data values by property name. - std::vector GetContextDataByName(std::string_view checkpointName, int contextData, std::string_view name); + // Gets multiple values for a given data type field. + std::vector GetDataFieldMultiValue(IdType checkpointId, int dataType, std::string field); - // Removes the context data. - void RemoveContextData(std::string_view checkpointName, int contextData); + // Sets multiple values for a given data type field. + void SetDataFieldMultiValue(IdType checkpointId, int dataType, std::string field, std::vector values); private: // Constructor used to open an existing index. diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointContextTable.cpp b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointContextTable.cpp index 68a358b4fc..ce1bc12209 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointContextTable.cpp +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointContextTable.cpp @@ -112,19 +112,29 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 return connection.GetLastInsertRowID(); } - void CheckpointContextTable::RemoveContextData(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int contextData) + std::vector CheckpointContextTable::GetDataFields(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int type) { SQLite::Builder::StatementBuilder builder; - builder.DeleteFrom(s_CheckpointContextTable_Table_Name).Where(s_CheckpointContextTable_CheckpointId_Column).Equals(checkpointId) - .And(s_CheckpointContextTable_ContextData_Column).Equals(contextData); - builder.Execute(connection); + builder.Select(s_CheckpointContextTable_Name_Column).From(s_CheckpointContextTable_Table_Name).Where(s_CheckpointContextTable_CheckpointId_Column); + builder.Equals(checkpointId).And(s_CheckpointContextTable_ContextData_Column).Equals(type); + + SQLite::Statement select = builder.Prepare(connection); + + std::vector fields; + + while (select.Step()) + { + fields.emplace_back(select.GetColumn(0)); + } + + return fields; } - std::vector CheckpointContextTable::GetContextData(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int contextData) + std::vector CheckpointContextTable::GetDataValuesByName(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int contextData, std::string_view name) { SQLite::Builder::StatementBuilder builder; builder.Select(s_CheckpointContextTable_Value_Column).From(s_CheckpointContextTable_Table_Name).Where(s_CheckpointContextTable_CheckpointId_Column); - builder.Equals(checkpointId).And(s_CheckpointContextTable_ContextData_Column).Equals(contextData); + builder.Equals(checkpointId).And(s_CheckpointContextTable_ContextData_Column).Equals(contextData).And(s_CheckpointContextTable_Name_Column).Equals(name); SQLite::Statement select = builder.Prepare(connection); @@ -138,21 +148,42 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 return values; } - std::vector CheckpointContextTable::GetContextDataByName(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int contextData, std::string_view name) + bool CheckpointContextTable::HasDataField(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int type, std::string_view name) + { + SQLite::Builder::StatementBuilder builder; + builder.Select(SQLite::Builder::RowCount).From(s_CheckpointContextTable_Table_Name).Where(s_CheckpointContextTable_CheckpointId_Column); + builder.Equals(checkpointId).And(s_CheckpointContextTable_ContextData_Column).Equals(type).And(s_CheckpointContextTable_Name_Column).Equals(name); + + SQLite::Statement countStatement = builder.Prepare(connection); + + THROW_HR_IF(E_UNEXPECTED, !countStatement.Step()); + + return (countStatement.GetColumn(0) == 0); + } + + void CheckpointContextTable::RemoveContextData(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int contextData) + { + SQLite::Builder::StatementBuilder builder; + builder.DeleteFrom(s_CheckpointContextTable_Table_Name).Where(s_CheckpointContextTable_CheckpointId_Column).Equals(checkpointId) + .And(s_CheckpointContextTable_ContextData_Column).Equals(contextData); + builder.Execute(connection); + } + + std::string CheckpointContextTable::GetSingleDataField(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int type) { SQLite::Builder::StatementBuilder builder; builder.Select(s_CheckpointContextTable_Value_Column).From(s_CheckpointContextTable_Table_Name).Where(s_CheckpointContextTable_CheckpointId_Column); - builder.Equals(checkpointId).And(s_CheckpointContextTable_ContextData_Column).Equals(contextData).And(s_CheckpointContextTable_Name_Column).Equals(name); + builder.Equals(checkpointId).And(s_CheckpointContextTable_ContextData_Column).Equals(type); SQLite::Statement select = builder.Prepare(connection); - std::vector values; - - while (select.Step()) + if (select.Step()) { - values.emplace_back(select.GetColumn(0)); + return select.GetColumn(0); + } + else + { + return {}; } - - return values; } } \ No newline at end of file diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointContextTable.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointContextTable.h index e838d637e4..f9960411af 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointContextTable.h +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointContextTable.h @@ -2,9 +2,8 @@ // Licensed under the MIT License. #pragma once #include "SQLiteWrapper.h" -#include "SQLiteStatementBuilder.h" -#include "Microsoft/Schema/ICheckpointRecord.h" #include +#include namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 { @@ -25,13 +24,16 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 // Adds a context data for a checkpoint. Index is used to represent the item number if the context data has more than one value. static SQLite::rowid_t AddContextData(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int contextData, std::string_view name, std::string_view value, int index = 1); - // Gets the context data values from a checkpoint id. - static std::vector GetContextData(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int contextData); + static std::vector GetDataFields(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int type); // Gets the context data values by property name from a checkpoint id. - static std::vector GetContextDataByName(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int contextData, std::string_view name); + static std::vector GetDataValuesByName(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int contextData, std::string_view name); + + static bool HasDataField(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int type, std::string_view name); // Removes the context data by checkpoint id. static void RemoveContextData(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int contextData); + + static std::string GetSingleDataField(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int type); }; } \ No newline at end of file diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointMetadataTable.cpp b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointMetadataTable.cpp deleted file mode 100644 index ee67278a44..0000000000 --- a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointMetadataTable.cpp +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. -#include "pch.h" -#include "CheckpointMetadataTable.h" -#include "SQLiteStatementBuilder.h" - -namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 -{ - using namespace std::string_view_literals; - static constexpr std::string_view s_CheckpointMetadataTable_Table_Name = "CheckpointMetadata"sv; - static constexpr std::string_view s_CheckpointMetadataTable_Name_Column = "Name"; - static constexpr std::string_view s_CheckpointMetadataTable_Value_Column = "Value"; - - std::string_view CheckpointMetadataTable::TableName() - { - return s_CheckpointMetadataTable_Table_Name; - } - - void CheckpointMetadataTable::Create(SQLite::Connection& connection) - { - using namespace SQLite::Builder; - - SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "createCheckpointMetadataTable_v1_0"); - - StatementBuilder createTableBuilder; - createTableBuilder.CreateTable(s_CheckpointMetadataTable_Table_Name).BeginColumns(); - createTableBuilder.Column(ColumnBuilder(s_CheckpointMetadataTable_Name_Column, Type::Text)); - createTableBuilder.Column(ColumnBuilder(s_CheckpointMetadataTable_Value_Column, Type::Text)); - createTableBuilder.EndColumns(); - createTableBuilder.Execute(connection); - - savepoint.Commit(); - } - - std::string CheckpointMetadataTable::GetNamedValue(SQLite::Connection& connection, std::string_view name) - { - SQLite::Builder::StatementBuilder builder; - builder.Select({ s_CheckpointMetadataTable_Value_Column }) - .From(s_CheckpointMetadataTable_Table_Name).Where(s_CheckpointMetadataTable_Name_Column).Equals(name); - - SQLite::Statement statement = builder.Prepare(connection); - THROW_HR_IF(E_NOT_SET, !statement.Step()); - return statement.GetColumn(0); - } - - SQLite::rowid_t CheckpointMetadataTable::SetNamedValue(SQLite::Connection& connection, std::string_view name, std::string_view value) - { - SQLite::Builder::StatementBuilder builder; - builder.InsertInto(s_CheckpointMetadataTable_Table_Name) - .Columns({ s_CheckpointMetadataTable_Name_Column, - s_CheckpointMetadataTable_Value_Column }) - .Values(name, value); - - builder.Execute(connection); - return connection.GetLastInsertRowID(); - } -} \ No newline at end of file diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointMetadataTable.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointMetadataTable.h deleted file mode 100644 index 74756b1255..0000000000 --- a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointMetadataTable.h +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. -#pragma once -#include "SQLiteWrapper.h" -#include "SQLiteStatementBuilder.h" -#include "Microsoft/Schema/ICheckpointRecord.h" -#include - -namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 -{ - struct CheckpointMetadataTable - { - // Get the table name. - static std::string_view TableName(); - - // Creates the table with named indices. - static void Create(SQLite::Connection& connection); - - // Gets the metadata value of the checkpoint state. - static std::string GetNamedValue(SQLite::Connection& connection, std::string_view name); - - // Sets the metadata value of the checkpoint state. - static SQLite::rowid_t SetNamedValue(SQLite::Connection& connection, std::string_view name, std::string_view value); - }; -} \ No newline at end of file diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointRecordInterface.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointRecordInterface.h index 942a12d2bc..eb05ac2682 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointRecordInterface.h +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointRecordInterface.h @@ -13,30 +13,17 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 private: bool IsEmpty(SQLite::Connection& connection) override; - std::vector GetAvailableCheckpoints(SQLite::Connection& connection) override; - - std::map> GetContextDataByContextId(SQLite::Connection& connection, std::string checkpointName, int64_t dataId) override; - - std::vector GetAvailableDataTypes(SQLite::Connection& connection, std::string checkpointName) override; - - SQLite::rowid_t AddCheckpoint(SQLite::Connection& connection, std::string checkpointName) override; - - void AddContextData(SQLite::Connection& connection, std::string checkpointName, int dataId, std::string name, std::vector values) override; - - + SQLite::rowid_t AddCheckpoint(SQLite::Connection& connection, std::string_view checkpointName) override; + std::optional GetCheckpointIdByName(SQLite::Connection& connection, std::string_view checkpointName) override; + std::vector GetCheckpointDataTypes(SQLite::Connection& connection, SQLite::rowid_t checkpointId) override; + std::vector GetCheckpointDataFields(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int dataType) override; + std::vector GetCheckpointDataValues(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int dataType, std::string_view name) override; + bool HasCheckpointDataField(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int dataType, std::string_view name) override; + void SetCheckpointDataValue(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int dataType, std::string_view name, std::vector values) override; - //SQLite::rowid_t SetMetadata(SQLite::Connection& connection, std::string_view name, std::string_view value) override; - //std::string GetMetadata(SQLite::Connection& connection, std::string_view name) override; - //std::string GetLastCheckpoint(SQLite::Connection& connection) override; - //bool CheckpointExists(SQLite::Connection& connection, std::string_view checkpointName) override; - //std::vector GetAvailableContextData(SQLite::Connection& connection, std::string_view checkpointName) override; - //SQLite::rowid_t AddCheckpoint(SQLite::Connection& connection, std::string_view checkpointName) override; - //SQLite::rowid_t AddContextData(SQLite::Connection& connection, std::string_view checkpointName, int contextData, std::string_view name, std::string_view value, int index = 0) override; - //std::vector GetContextData(SQLite::Connection& connection, std::string_view checkpointName, int contextData) override; - //std::vector GetContextDataByName(SQLite::Connection& connection, std::string_view checkpointName, int contextData, std::string_view name) override; - //void RemoveContextData(SQLite::Connection& connection, std::string_view checkpointName, int contextData) override; + std::string GetDataSingleValue(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int dataType) override; }; } \ No newline at end of file diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointRecordInterface_1_0.cpp b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointRecordInterface_1_0.cpp index 9ae33c3dbc..0d99f8b467 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointRecordInterface_1_0.cpp +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointRecordInterface_1_0.cpp @@ -3,26 +3,10 @@ #include "pch.h" #include "Microsoft/Schema/Checkpoint_1_0/CheckpointRecordInterface.h" #include "Microsoft/Schema/Checkpoint_1_0/CheckpointContextTable.h" -#include "Microsoft/Schema/Checkpoint_1_0/CheckpointMetadataTable.h" #include "Microsoft/Schema/Checkpoint_1_0/CheckpointTable.h" namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 { - namespace - { - std::optional GetCheckpointIdByName(SQLite::Connection& connection, std::string_view checkpointName) - { - auto result = CheckpointTable::GetCheckpointId(connection, checkpointName); - - if (!result) - { - AICLI_LOG(Repo, Verbose, << "Did not find checkpoint " << checkpointName); - } - - return result; - } - } - Schema::Version CheckpointRecordInterface::GetVersion() const { return { 1, 0 }; @@ -31,9 +15,8 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 void CheckpointRecordInterface::CreateTables(SQLite::Connection& connection) { SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "createCheckpointTables_v1_0"); - Checkpoint_V1_0::CheckpointContextTable::Create(connection); - Checkpoint_V1_0::CheckpointMetadataTable::Create(connection); Checkpoint_V1_0::CheckpointTable::Create(connection); + Checkpoint_V1_0::CheckpointContextTable::Create(connection); savepoint.Commit(); } @@ -46,151 +29,64 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 { return CheckpointTable::GetCheckpoints(connection); } - - std::map> CheckpointRecordInterface::GetContextDataByContextId(SQLite::Connection& connection, std::string checkpointName, int64_t dataId) + + SQLite::rowid_t CheckpointRecordInterface::AddCheckpoint(SQLite::Connection& connection, std::string_view checkpointName) { - auto checkpointId = GetCheckpointIdByName(connection, checkpointName); - if (!checkpointId) - { - return {}; - } + SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "addcheckpoint_v1_0"); + SQLite::rowid_t checkpointId = CheckpointTable::AddCheckpoint(connection, checkpointName); + savepoint.Commit(); + return checkpointId; + } - const auto& availableDataTypes = CheckpointContextTable::GetAvailableData(connection, checkpointId.value()); - std::map> contextDataMap; + std::optional CheckpointRecordInterface::GetCheckpointIdByName(SQLite::Connection& connection, std::string_view checkpointName) + { + auto result = CheckpointTable::GetCheckpointId(connection, checkpointName); - for (auto dataTypes : availableDataTypes) + if (!result) { - const auto& values = CheckpointContextTable::GetContextData(connection, checkpointId.value(), dataTypes); - contextDataMap[dataTypes] = values; + AICLI_LOG(Repo, Verbose, << "Did not find checkpoint " << checkpointName); } - return contextDataMap; + return result; } - std::vector CheckpointRecordInterface::GetAvailableDataTypes(SQLite::Connection& connection, std::string checkpointName) + std::vector CheckpointRecordInterface::GetCheckpointDataTypes(SQLite::Connection& connection, SQLite::rowid_t checkpointId) { - auto checkpointId = GetCheckpointIdByName(connection, checkpointName); - if (!checkpointId) - { - return {}; - } - - return CheckpointContextTable::GetAvailableData(connection, checkpointId.value()); + return CheckpointContextTable::GetAvailableData(connection, checkpointId); } - SQLite::rowid_t CheckpointRecordInterface::AddCheckpoint(SQLite::Connection& connection, std::string checkpointName) + std::vector CheckpointRecordInterface::GetCheckpointDataFields(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int dataType) { - SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "addcheckpoint_v1_0"); - SQLite::rowid_t checkpointId = CheckpointTable::AddCheckpoint(connection, checkpointName); - savepoint.Commit(); - return checkpointId; + return CheckpointContextTable::GetDataFields(connection, checkpointId, dataType); } - void CheckpointRecordInterface::AddContextData(SQLite::Connection& connection, std::string checkpointName, int dataId, std::string name, std::vector values) + std::vector CheckpointRecordInterface::GetCheckpointDataValues(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int dataType, std::string_view name) { - auto checkpointId = GetCheckpointIdByName(connection, checkpointName); - THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_NOT_FOUND), !checkpointId); + // This sohuld check whether the field name even exists. + return CheckpointContextTable::GetDataValuesByName(connection, checkpointId, dataType, name); + } + bool CheckpointRecordInterface::HasCheckpointDataField(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int dataType, std::string_view name) + { + return CheckpointContextTable::HasDataField(connection, checkpointId, dataType, name); + } + + void CheckpointRecordInterface::SetCheckpointDataValue(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int dataType, std::string_view name, std::vector values) + { SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "addcontextdata_v1_0"); - - // This may have issues if the index is not properly correlated - // Get latest context data. int index = 0; for (const auto& value : values) { - CheckpointContextTable::AddContextData(connection, checkpointId.value(), dataId, name, value, index); + CheckpointContextTable::AddContextData(connection, checkpointId, dataType, name, value, index); index++; } savepoint.Commit(); } - //SQLite::rowid_t CheckpointRecordInterface::SetMetadata(SQLite::Connection& connection, std::string_view name, std::string_view value) - //{ - // SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "setMetadata_v1_0"); - // SQLite::rowid_t argumentId = CheckpointMetadataTable::SetNamedValue(connection, name, value); - // savepoint.Commit(); - // return argumentId; - //} - - //bool CheckpointRecordInterface::CheckpointExists(SQLite::Connection& connection, std::string_view checkpointName) - //{ - // return GetExistingCheckpointId(connection, checkpointName).has_value(); - //} - - //std::string CheckpointRecordInterface::GetMetadata(SQLite::Connection& connection, std::string_view name) - //{ - // return CheckpointMetadataTable::GetNamedValue(connection, name); - //} - - //std::string CheckpointRecordInterface::GetLastCheckpoint(SQLite::Connection& connection) - //{ - // return CheckpointTable::GetLastCheckpoint(connection); - //} - - //std::vector CheckpointRecordInterface::GetAvailableContextData(SQLite::Connection& connection, std::string_view checkpointName) - //{ - // auto existingCheckpointId = GetExistingCheckpointId(connection, checkpointName); - // if (!existingCheckpointId) - // { - // return {}; - // } - - // return CheckpointContextTable::GetAvailableData(connection, existingCheckpointId.value()); - //} - - //SQLite::rowid_t CheckpointRecordInterface::AddCheckpoint(SQLite::Connection& connection, std::string_view checkpointName) - //{ - // auto existingCheckpointId = GetExistingCheckpointId(connection, checkpointName); - // THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS), existingCheckpointId); - - // SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "addcheckpoint_v1_0"); - // SQLite::rowid_t checkpointId = CheckpointTable::AddCheckpoint(connection, checkpointName); - // savepoint.Commit(); - // return checkpointId; - //} - - //SQLite::rowid_t CheckpointRecordInterface::AddContextData(SQLite::Connection& connection, std::string_view checkpointName, int contextData, std::string_view name, std::string_view value, int index) - //{ - // auto existingCheckpointId = GetExistingCheckpointId(connection, checkpointName); - // THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_NOT_FOUND), !existingCheckpointId); - - // SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "addcontextdata_v1_0"); - // SQLite::rowid_t rowId = CheckpointContextTable::AddContextData(connection, existingCheckpointId.value(), contextData, name, value, index); - // savepoint.Commit(); - // return rowId; - //} - - //std::vector CheckpointRecordInterface::GetContextData(SQLite::Connection& connection, std::string_view checkpointName, int contextData) - //{ - // auto existingCheckpointId = GetExistingCheckpointId(connection, checkpointName); - // if (!existingCheckpointId) - // { - // return {}; - // } - - // return CheckpointContextTable::GetContextData(connection, existingCheckpointId.value(), contextData); - //} - - //std::vector CheckpointRecordInterface::GetContextDataByName(SQLite::Connection& connection, std::string_view checkpointName, int contextData, std::string_view name) - //{ - // auto existingCheckpointId = GetExistingCheckpointId(connection, checkpointName); - // if (!existingCheckpointId) - // { - // return {}; - // } - - // return CheckpointContextTable::GetContextDataByName(connection, existingCheckpointId.value(), contextData, name); - //} - - //void CheckpointRecordInterface::RemoveContextData(SQLite::Connection& connection, std::string_view checkpointName, int contextData) - //{ - // auto existingCheckpointId = GetExistingCheckpointId(connection, checkpointName); - // THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_NOT_FOUND), !existingCheckpointId); - - // SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "removecontextdata_v1_0"); - // CheckpointContextTable::RemoveContextData(connection, existingCheckpointId.value(), contextData); - // savepoint.Commit(); - //} + std::string CheckpointRecordInterface::GetDataSingleValue(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int dataType) + { + return CheckpointContextTable::GetSingleDataField(connection, checkpointId, dataType); + } } \ No newline at end of file diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointTable.cpp b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointTable.cpp index b353c7312a..80f0b6a6ac 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointTable.cpp +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointTable.cpp @@ -40,14 +40,13 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 SQLite::Statement select = builder.Prepare(connection); std::vector checkpoints; - if (select.Step()) + + while (select.Step()) { checkpoints.emplace_back(select.GetColumn(0)); } - else - { - return {}; - } + + return checkpoints; } std::string CheckpointTable::GetLastCheckpoint(SQLite::Connection& connection) diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointTable.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointTable.h index 1a537a041d..55d51fdf01 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointTable.h +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointTable.h @@ -2,9 +2,8 @@ // Licensed under the MIT License. #pragma once #include "SQLiteWrapper.h" -#include "SQLiteStatementBuilder.h" -#include "Microsoft/Schema/ICheckpointRecord.h" -#include +#include +#include namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 { diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/ICheckpointRecord.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/ICheckpointRecord.h index 03a7278efe..b7847e2d68 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/Schema/ICheckpointRecord.h +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/ICheckpointRecord.h @@ -22,42 +22,20 @@ namespace AppInstaller::Repository::Microsoft::Schema virtual std::vector GetAvailableCheckpoints(SQLite::Connection& connection) = 0; - virtual std::map> GetContextDataByContextId(SQLite::Connection& connection, std::string checkpointName, int64_t dataId) = 0; + virtual SQLite::rowid_t AddCheckpoint(SQLite::Connection& connection, std::string_view checkpointName) = 0; - virtual std::vector GetAvailableDataTypes(SQLite::Connection& connection, std::string checkpointName) = 0; + virtual std::optional GetCheckpointIdByName(SQLite::Connection& connection, std::string_view checkpointName) = 0; - virtual SQLite::rowid_t AddCheckpoint(SQLite::Connection& connection, std::string checkpointName) = 0; + virtual std::vector GetCheckpointDataTypes(SQLite::Connection& connection, SQLite::rowid_t checkpointId) = 0; - virtual void AddContextData(SQLite::Connection& connection, std::string checkpointName, int dataId, std::string name, std::vector values) = 0; + virtual std::string GetDataSingleValue(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int dataType) = 0; - // Sets the metadata value. - //virtual SQLite::rowid_t SetMetadata(SQLite::Connection& connection, std::string_view name, std::string_view value) = 0; + virtual std::vector GetCheckpointDataFields(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int dataType) = 0; - //// Gets the metadata value. - //virtual std::string GetMetadata(SQLite::Connection& connection, std::string_view name) = 0; + virtual std::vector GetCheckpointDataValues(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int dataType, std::string_view name) = 0; - //// Gets the latest checkpoint. - //virtual std::string GetLastCheckpoint(SQLite::Connection& connection) = 0; + virtual bool HasCheckpointDataField(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int dataType, std::string_view name) = 0; - //// Returns a value indicating whether the checkpoint exists. - //virtual bool CheckpointExists(SQLite::Connection& connection, std::string_view checkpointName) = 0; - - //// Gets the available context data from a checkpoint. - //virtual std::vector GetAvailableContextData(SQLite::Connection& connection, std::string_view checkpointName) = 0; - - //// Adds a checkpoint. - //virtual SQLite::rowid_t AddCheckpoint(SQLite::Connection& connection, std::string_view checkpointName) = 0; - - //// Adds a context data value for a checkpoint. - //virtual SQLite::rowid_t AddContextData(SQLite::Connection& connection, std::string_view checkpointName, int contextData, std::string_view name, std::string_view value, int index) = 0; - - //// Gets the context data values from a checkpoint. - //virtual std::vector GetContextData(SQLite::Connection& connection, std::string_view checkpointName, int contextData) = 0; - - //// Gets the context data values by property name from a checkpoint. - //virtual std::vector GetContextDataByName(SQLite::Connection& connection, std::string_view checkpointName, int contextData, std::string_view name) = 0; - - //// Removes the context data from a checkpoint. - //virtual void RemoveContextData(SQLite::Connection& connection, std::string_view checkpointName, int contextData) = 0; + virtual void SetCheckpointDataValue(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int dataType, std::string_view name, std::vector values) = 0; }; } \ No newline at end of file diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Version.cpp b/src/AppInstallerRepositoryCore/Microsoft/Schema/Version.cpp index f5a3049547..e845a2ac8f 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/Schema/Version.cpp +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/Version.cpp @@ -4,6 +4,8 @@ #include "Version.h" #include "MetadataTable.h" +#include + namespace AppInstaller::Repository::Microsoft::Schema { Version Version::GetSchemaVersion(SQLite::Connection& connection) @@ -39,4 +41,24 @@ namespace AppInstaller::Repository::Microsoft::Schema return out << version.MajorVersion << '.' << version.MinorVersion; } } + + Version Version::Latest() + { + return { std::numeric_limits::max(), std::numeric_limits::max() }; + } + + Version Version::LatestForMajor(uint32_t majorVersion) + { + return { majorVersion, std::numeric_limits::max() }; + } + + bool Version::IsLatest() const + { + return (MajorVersion == std::numeric_limits::max() && MinorVersion == std::numeric_limits::max()); + } + + bool Version::IsLatestForMajor(uint32_t majorVersion) const + { + return (MajorVersion == majorVersion && MinorVersion == std::numeric_limits::max()); + } } diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Version.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/Version.h index f435c08b62..d179dab481 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/Schema/Version.h +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/Version.h @@ -2,7 +2,6 @@ // Licensed under the MIT License. #pragma once #include "SQLiteWrapper.h" -#include #include namespace AppInstaller::Repository::Microsoft::Schema @@ -35,16 +34,16 @@ namespace AppInstaller::Repository::Microsoft::Schema } // Gets a version that represents the latest schema known to the implementation. - static constexpr Version Latest() { return { std::numeric_limits::max(), std::numeric_limits::max() }; } + static Version Latest(); // Gets a version that represents the latest schema known to the implementation for the given major version. - static constexpr Version LatestForMajor(uint32_t majorVersion) { return { majorVersion, std::numeric_limits::max() }; } + static Version LatestForMajor(uint32_t majorVersion); // Determines if this version represents the latest schema. - bool IsLatest() const { return (MajorVersion == std::numeric_limits::max() && MinorVersion == std::numeric_limits::max()); } + bool IsLatest() const; // Determines if this version represents the latest schema of the given major version. - bool IsLatestForMajor(uint32_t majorVersion) const { return (MajorVersion == majorVersion && MinorVersion == std::numeric_limits::max()); } + bool IsLatestForMajor(uint32_t majorVersion) const; // Determines the schema version of the opened index. static Version GetSchemaVersion(SQLite::Connection& connection); From 754cb8fe94031c0682a6c54c2be00e647218bd4b Mon Sep 17 00:00:00 2001 From: Ryan Fu Date: Mon, 11 Sep 2023 14:27:00 -0700 Subject: [PATCH 30/43] fix tests --- src/AppInstallerCLICore/Checkpoint.h | 41 ++++++------ src/AppInstallerCLICore/CheckpointManager.cpp | 41 ++++++++++-- src/AppInstallerCLICore/CheckpointManager.h | 2 + .../Commands/InstallCommand.cpp | 11 ++-- .../Commands/ResumeCommand.cpp | 17 +++-- src/AppInstallerCLICore/ExecutionContext.cpp | 32 +++++---- src/AppInstallerCLICore/ExecutionContext.h | 2 - .../Workflows/ResumeFlow.cpp | 8 +-- src/AppInstallerCLIE2ETests/ResumeCommand.cs | 11 ++++ src/AppInstallerCLITests/CheckpointRecord.cpp | 66 +++++++++++-------- src/AppInstallerCLITests/ResumeFlow.cpp | 33 +++------- .../Microsoft/CheckpointRecord.cpp | 47 ++++--------- .../Microsoft/CheckpointRecord.h | 14 ++-- .../Checkpoint_1_0/CheckpointContextTable.cpp | 45 ++++++------- .../Checkpoint_1_0/CheckpointContextTable.h | 4 +- .../CheckpointRecordInterface.h | 11 ++-- .../CheckpointRecordInterface_1_0.cpp | 33 ++++++---- .../Microsoft/Schema/ICheckpointRecord.h | 21 ++++-- 18 files changed, 234 insertions(+), 205 deletions(-) diff --git a/src/AppInstallerCLICore/Checkpoint.h b/src/AppInstallerCLICore/Checkpoint.h index a67178db68..286fe7b92a 100644 --- a/src/AppInstallerCLICore/Checkpoint.h +++ b/src/AppInstallerCLICore/Checkpoint.h @@ -22,9 +22,7 @@ namespace AppInstaller::Checkpoints template struct Checkpoint { - // call this here static_assert(dataType) - // static_assert() - + static_assert(std::is_enum::value); friend CheckpointManager; std::vector GetCheckpointDataTypes() @@ -35,9 +33,6 @@ namespace AppInstaller::Checkpoints // Returns a boolean value indicating whether the field name exists. bool Has(T dataType, std::string fieldName) { - // to integral wrapper around the data type - - // return a boolean to determine if the context has the field name that exists. return m_checkpointRecord->HasDataField(m_checkpointId, dataType, fieldName); } @@ -46,33 +41,41 @@ namespace AppInstaller::Checkpoints { return m_checkpointRecord->GetDataFieldNames(m_checkpointId, dataType); } - - std::string GetOne(T dataType) + + // Sets a single value for the a data type. + void Set(T dataType, std::string value) { - return m_checkpointRecord->GetDataSingleValue(m_checkpointId, dataType); - // For usage with a data types that only have a single file. + m_checkpointRecord->SetDataValue(m_checkpointId, dataType, {}, { value }); } - std::vector GetMany(T dataType) + // Sets a single field value for a data type. + void Set(T dataType, std::string fieldName, std::string value) { - + m_checkpointRecord->SetDataValue(m_checkpointId, dataType, fieldName, { value }); } - std::vector Get(T dataType, std::string fieldName) + // Sets multiple field values for a data type. + void SetMany(T dataType, std::string fieldName, std::vector values) { - // Dispatch function? - // Passing in the object you want it to fill and utilize overloads - return m_checkpointRecord->GetDataFieldValue(m_checkpointId, dataType, fieldName); + m_checkpointRecord->SetDataValue(m_checkpointId, dataType, fieldName, values); } - void SetOne(T dataType, std::string value) + // Gets a single value for a data type. + std::string Get(T dataType) { + return m_checkpointRecord->GetDataSingleValue(m_checkpointId, dataType); + } + // Gets a single field value for a data type. + std::string Get(T dataType, std::string fieldName) + { + return m_checkpointRecord->GetDataFieldSingleValue(m_checkpointId, dataType, fieldName); } - void Set(T dataType, std::string_view fieldName, std::vector values) + // Gets multiple field values for a data type. + std::vector GetMany(T dataType, std::string fieldName) { - m_checkpointRecord->SetDataFieldValue(m_checkpointId, dataType, fieldName, values); + return m_checkpointRecord->GetDataFieldMultiValue(m_checkpointId, dataType, fieldName); } private: diff --git a/src/AppInstallerCLICore/CheckpointManager.cpp b/src/AppInstallerCLICore/CheckpointManager.cpp index 68eddcb09a..c1e95ed1e0 100644 --- a/src/AppInstallerCLICore/CheckpointManager.cpp +++ b/src/AppInstallerCLICore/CheckpointManager.cpp @@ -15,7 +15,7 @@ namespace AppInstaller::Checkpoints constexpr std::string_view s_checkpoints_filename = "checkpoints.db"sv; // This checkpoint name is reserved for the starting checkpoint which captures the automatic metadata. - constexpr std::string_view s_StartCheckpoint = "start"sv; + constexpr std::string_view s_AutomaticCheckpoint = "automatic"sv; constexpr std::string_view s_Checkpoints = "Checkpoints"sv; constexpr std::string_view s_ClientVersion = "ClientVersion"sv; constexpr std::string_view s_CommandName = "CommandName"sv; @@ -71,7 +71,7 @@ namespace AppInstaller::Checkpoints Checkpoint CheckpointManager::CreateAutomaticCheckpoint() { - CheckpointRecord::IdType startCheckpointId = m_checkpointRecord->AddCheckpoint(s_StartCheckpoint); + CheckpointRecord::IdType startCheckpointId = m_checkpointRecord->AddCheckpoint(s_AutomaticCheckpoint); Checkpoint checkpoint{ m_checkpointRecord, startCheckpointId }; return checkpoint; } @@ -85,7 +85,7 @@ namespace AppInstaller::Checkpoints Checkpoint CheckpointManager::GetAutomaticCheckpoint() { - std::optional startCheckpointId = m_checkpointRecord->GetCheckpointIdByName(s_StartCheckpoint); + std::optional startCheckpointId = m_checkpointRecord->GetCheckpointIdByName(s_AutomaticCheckpoint); if (!startCheckpointId.has_value()) { @@ -98,18 +98,45 @@ namespace AppInstaller::Checkpoints std::vector> CheckpointManager::GetCheckpoints() { std::vector> checkpoints; - for (const auto& checkpoint : m_checkpointRecord->GetCheckpoints()) + for (const auto& checkpointName : m_checkpointRecord->GetCheckpoints()) { - // Exclude starting checkpoint from these context data related checkpoints. - if (checkpoint == s_StartCheckpoint) + if (checkpointName == s_AutomaticCheckpoint) { continue; } - CheckpointRecord::IdType checkpointId = m_checkpointRecord->GetCheckpointIdByName(checkpoint).value(); + CheckpointRecord::IdType checkpointId = m_checkpointRecord->GetCheckpointIdByName(checkpointName).value(); checkpoints.emplace_back(Checkpoint{ std::move(m_checkpointRecord), checkpointId }); } return checkpoints; } + + void CheckpointManager::CleanUpRecord() + { + if (m_checkpointRecord) + { + m_checkpointRecord.reset(); + } + + if (m_resumeId != GUID_NULL) + { + const auto& checkpointRecordPath = GetCheckpointRecordPath(m_resumeId); + + if (std::filesystem::exists(checkpointRecordPath)) + { + std::error_code error; + if (std::filesystem::remove(checkpointRecordPath, error)) + { + AICLI_LOG(CLI, Info, << "Checkpoint record deleted: " << checkpointRecordPath); + } + + const auto& checkpointRecordParentDirectory = checkpointRecordPath.parent_path(); + if (std::filesystem::is_empty(checkpointRecordParentDirectory) && std::filesystem::remove(checkpointRecordParentDirectory, error)) + { + AICLI_LOG(CLI, Info, << "Checkpoint record directory deleted: " << checkpointRecordParentDirectory); + } + } + } + } } diff --git a/src/AppInstallerCLICore/CheckpointManager.h b/src/AppInstallerCLICore/CheckpointManager.h index 112cbd14eb..2be9a47839 100644 --- a/src/AppInstallerCLICore/CheckpointManager.h +++ b/src/AppInstallerCLICore/CheckpointManager.h @@ -31,6 +31,8 @@ namespace AppInstaller::Checkpoints std::vector> GetCheckpoints(); + void CleanUpRecord(); + private: GUID m_resumeId = {}; std::shared_ptr m_checkpointRecord; diff --git a/src/AppInstallerCLICore/Commands/InstallCommand.cpp b/src/AppInstallerCLICore/Commands/InstallCommand.cpp index 05b84572d5..3b40565abf 100644 --- a/src/AppInstallerCLICore/Commands/InstallCommand.cpp +++ b/src/AppInstallerCLICore/Commands/InstallCommand.cpp @@ -106,15 +106,13 @@ namespace AppInstaller::CLI void InstallCommand::Resume(Context& context) const { // TODO: Load context data from checkpoint for install command. - context.SetFlags(Execution::ContextFlag::Resume); + ExecuteInternal(context); } void InstallCommand::ExecuteInternal(Context& context) const { - context.Checkpoint("exampleCheckpoint", {}); - - context.SetFlags(ContextFlag::ShowSearchResultsOnPartialFailure); + context.SetFlags(ContextFlag::ShowSearchResultsOnPartialFailure); if (context.Args.Contains(Execution::Args::Type::Manifest)) { @@ -123,6 +121,7 @@ namespace AppInstaller::CLI Workflow::GetManifestFromArg << Workflow::SelectInstaller << Workflow::EnsureApplicableInstaller << + Workflow::Checkpoint("exampleCheckpoint", {}) << // TODO: Checkpoint example Workflow::InstallSinglePackage; } else @@ -149,7 +148,9 @@ namespace AppInstaller::CLI } else { - context << Workflow::InstallOrUpgradeSinglePackage(OperationType::Install); + context << + Workflow::Checkpoint("exampleCheckpoint", {}) << // TODO: Checkpoint example + Workflow::InstallOrUpgradeSinglePackage(OperationType::Install); } } } diff --git a/src/AppInstallerCLICore/Commands/ResumeCommand.cpp b/src/AppInstallerCLICore/Commands/ResumeCommand.cpp index a8b6cff582..adea26eac8 100644 --- a/src/AppInstallerCLICore/Commands/ResumeCommand.cpp +++ b/src/AppInstallerCLICore/Commands/ResumeCommand.cpp @@ -53,14 +53,14 @@ namespace AppInstaller::CLI Checkpoint automaticCheckpoint = resumeContext.LoadCheckpoint(checkpointId); - const auto& checkpointClientVersion = automaticCheckpoint.GetOne(AutomaticCheckpointData::ClientVersion); + const auto& checkpointClientVersion = automaticCheckpoint.Get(AutomaticCheckpointData::ClientVersion); if (checkpointClientVersion != AppInstaller::Runtime::GetClientVersion().get()) { context.Reporter.Error() << Resource::String::ClientVersionMismatchError(Utility::LocIndView{ checkpointClientVersion }) << std::endl; AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_CLIENT_VERSION_MISMATCH); } - const auto& checkpointCommandName = automaticCheckpoint.GetOne(AutomaticCheckpointData::CommandName); + const auto& checkpointCommandName = automaticCheckpoint.Get(AutomaticCheckpointData::CommandName); std::unique_ptr commandToResume; AICLI_LOG(CLI, Info, << "Resuming command: " << checkpointCommandName); @@ -75,16 +75,17 @@ namespace AppInstaller::CLI THROW_HR_IF_MSG(E_UNEXPECTED, !commandToResume, "Command to resume not found."); - for (const auto& fieldNames : automaticCheckpoint.GetFieldNames(AutomaticCheckpointData::Arguments)) + for (const auto& fieldName : automaticCheckpoint.GetFieldNames(AutomaticCheckpointData::Arguments)) { - const auto& values = automaticCheckpoint.Get(AutomaticCheckpointData::Arguments, fieldNames); - Execution::Args::Type type = static_cast(std::stoi(fieldNames)); - if (values.empty()) + Execution::Args::Type type = static_cast(std::stoi(fieldName)); + auto argumentType = Argument::ForType(type).Type(); + if (argumentType == ArgumentType::Flag) { resumeContext.Args.AddArg(type); } else { + const auto& values = automaticCheckpoint.GetMany(AutomaticCheckpointData::Arguments, fieldName); for (const auto& value : values) { resumeContext.Args.AddArg(type, value); @@ -93,12 +94,10 @@ namespace AppInstaller::CLI } resumeContext.SetExecutingCommand(commandToResume.get()); - resumeContext.SetFlags(Execution::ContextFlag::Resume); // this should be captured by telemetry + resumeContext.SetFlags(Execution::ContextFlag::Resume); auto previousThreadGlobals = resumeContext.SetForCurrentThread(); - resumeContext.EnableSignalTerminationHandler(); - commandToResume->Resume(resumeContext); context.SetTerminationHR(resumeContext.GetTerminationHR()); } diff --git a/src/AppInstallerCLICore/ExecutionContext.cpp b/src/AppInstallerCLICore/ExecutionContext.cpp index 24ad0a9b18..5cf9a11872 100644 --- a/src/AppInstallerCLICore/ExecutionContext.cpp +++ b/src/AppInstallerCLICore/ExecutionContext.cpp @@ -258,6 +258,10 @@ namespace AppInstaller::CLI::Execution { if (Settings::ExperimentalFeature::IsEnabled(ExperimentalFeature::Feature::Resume) && !IsTerminated()) { + if (m_checkpointManager) + { + m_checkpointManager->CleanUpRecord(); + } } if (m_disableSignalTerminationHandlerOnExit) @@ -268,7 +272,6 @@ namespace AppInstaller::CLI::Execution Context Context::CreateEmptyContext() { - // make own thread globals return Context(Reporter, m_threadGlobals); } @@ -437,29 +440,34 @@ namespace AppInstaller::CLI::Execution if (!m_checkpointManager) { m_checkpointManager = std::make_unique(); + Checkpoints::Checkpoint automaticCheckpoint = m_checkpointManager->CreateAutomaticCheckpoint(); - Checkpoints::Checkpoint startingCheckpoint = m_checkpointManager->CreateAutomaticCheckpoint(); + automaticCheckpoint.Set(AutomaticCheckpointData::ClientVersion, AppInstaller::Runtime::GetClientVersion()); - // Set client version. - startingCheckpoint.Set(AutomaticCheckpointData::ClientVersion, "clientVersion"sv, { AppInstaller::Runtime::GetClientVersion() }); - - // Set command const auto& executingCommand = m_executingCommand; if (executingCommand != nullptr) { - startingCheckpoint.Set(AutomaticCheckpointData::CommandName, "commandName"sv, { std::string{m_executingCommand->Name()} }); + automaticCheckpoint.Set(AutomaticCheckpointData::CommandName, std::string{m_executingCommand->Name()}); } - // Set arguments const auto& argTypes = Args.GetTypes(); for (auto type : argTypes) { - //const auto& argName = Argument::ForType(type).Type.Name(); + const auto& argument = std::to_string(static_cast(type)); + auto argumentType = Argument::ForType(type).Type(); - // what the argument name is here:. - const auto& values = *Args.GetArgs(type); - startingCheckpoint.Set(AutomaticCheckpointData::Arguments, std::to_string(static_cast(type)), values); + if (argumentType == ArgumentType::Flag) + { + automaticCheckpoint.Set(AutomaticCheckpointData::Arguments, argument, {}); + } + else + { + const auto& values = *Args.GetArgs(type); + automaticCheckpoint.SetMany(AutomaticCheckpointData::Arguments, argument, values); + } } } + + // TODO: Capture context data for checkpoint. } } diff --git a/src/AppInstallerCLICore/ExecutionContext.h b/src/AppInstallerCLICore/ExecutionContext.h index 07fbe2366a..bf3a9abe10 100644 --- a/src/AppInstallerCLICore/ExecutionContext.h +++ b/src/AppInstallerCLICore/ExecutionContext.h @@ -190,8 +190,6 @@ namespace AppInstaller::CLI::Execution Workflow::ExecutionStage m_executionStage = Workflow::ExecutionStage::Initial; AppInstaller::ThreadLocalStorage::WingetThreadGlobals m_threadGlobals; AppInstaller::CLI::Command* m_executingCommand = nullptr; - - // Change this to unique pointer after you're done with implmementation. std::unique_ptr m_checkpointManager; }; } diff --git a/src/AppInstallerCLICore/Workflows/ResumeFlow.cpp b/src/AppInstallerCLICore/Workflows/ResumeFlow.cpp index 46840825cd..7ce25678de 100644 --- a/src/AppInstallerCLICore/Workflows/ResumeFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/ResumeFlow.cpp @@ -12,12 +12,6 @@ namespace AppInstaller::CLI::Workflow return; } - if (WI_IsFlagSet(context.GetFlags(), Execution::ContextFlag::Resume)) - { - // TODO: If the resume flag is set, resume workflow behavior. - } - else - { - } + context.Checkpoint(m_checkpointName, m_contextData); } } diff --git a/src/AppInstallerCLIE2ETests/ResumeCommand.cs b/src/AppInstallerCLIE2ETests/ResumeCommand.cs index 1d580c5fba..7506c9b2d5 100644 --- a/src/AppInstallerCLIE2ETests/ResumeCommand.cs +++ b/src/AppInstallerCLIE2ETests/ResumeCommand.cs @@ -50,6 +50,17 @@ public void InstallExe_VerifyIndexDoesNotExist() Assert.AreEqual(initialCheckpointsCount, actualCheckpointsCount); } + /// + /// Verifies that an error message is shown when an invalid resume id is provided. + /// + [Test] + public void ResumeInvalidId() + { + var invalidResumeId = "invalidResumeId"; + var resumeResult = TestCommon.RunAICLICommand("resume", $"-g {invalidResumeId}"); + Assert.AreEqual(Constants.ErrorCode.ERROR_INVALID_CL_ARGUMENTS, resumeResult.ExitCode); + } + /// /// Installs a test exe installer and verifies that the checkpoint index is cleaned up. /// diff --git a/src/AppInstallerCLITests/CheckpointRecord.cpp b/src/AppInstallerCLITests/CheckpointRecord.cpp index aed07cad97..6e504bf211 100644 --- a/src/AppInstallerCLITests/CheckpointRecord.cpp +++ b/src/AppInstallerCLITests/CheckpointRecord.cpp @@ -3,12 +3,14 @@ #include "pch.h" #include "TestCommon.h" #include +#include using namespace std::string_literals; using namespace TestCommon; using namespace AppInstaller::Repository::Microsoft; using namespace AppInstaller::Repository::SQLite; using namespace AppInstaller::Repository::Microsoft::Schema; +using namespace AppInstaller::Checkpoints; TEST_CASE("CheckpointRecordCreateLatestAndReopen", "[checkpointRecord]") { @@ -37,20 +39,23 @@ TEST_CASE("CheckpointRecord_WriteMetadata", "[checkpointRecord]") TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); - std::string_view testCommand = "install"sv; - std::string_view testClientVersion = "1.20.1234"sv; - std::string_view testArguments = "install --id Microsoft.PowerToys"; + std::string_view testCheckpointName = "testCheckpoint"sv; + std::string testCommand = "install"; + std::string testClientVersion = "1.20.1234"; { CheckpointRecord record = CheckpointRecord::CreateNew(tempFile, { 1, 0 }); - record.SetMetadata(CheckpointMetadata::CommandName, testCommand); - record.SetMetadata(CheckpointMetadata::ClientVersion, testClientVersion); + CheckpointRecord::IdType checkpointId = record.AddCheckpoint(testCheckpointName); + record.SetDataValue(checkpointId, static_cast(AutomaticCheckpointData::CommandName), {}, { testCommand }); + record.SetDataValue(checkpointId, static_cast(AutomaticCheckpointData::ClientVersion), {}, { testClientVersion }); } { - CheckpointRecord record = CheckpointRecord::Open(tempFile, SQLiteStorageBase::OpenDisposition::ReadWrite); - REQUIRE(testCommand == record.GetMetadata(CheckpointMetadata::CommandName)); - REQUIRE(testClientVersion == record.GetMetadata(CheckpointMetadata::ClientVersion)); + CheckpointRecord record = CheckpointRecord::Open(tempFile); + std::optional checkpointId = record.GetCheckpointIdByName(testCheckpointName); + REQUIRE(checkpointId.has_value()); + REQUIRE(testCommand == record.GetDataSingleValue(checkpointId.value(), static_cast(AutomaticCheckpointData::CommandName))); + REQUIRE(testClientVersion == record.GetDataSingleValue(checkpointId.value(), static_cast(AutomaticCheckpointData::ClientVersion))); } } @@ -60,31 +65,40 @@ TEST_CASE("CheckpointRecord_WriteContextData", "[checkpointRecord]") INFO("Using temporary file named: " << tempFile.GetPath()); std::string_view testCheckpoint = "testCheckpoint"sv; - int testContextData = 3; - std::string_view testName = "testName"; - std::string_view testValue = "testValue"; - std::string_view testName2 = "exampleName"; - std::string_view testValue2 = "exampleValue"; + std::string fieldName1 = "field1"; + std::string fieldName2 = "field2"; + + std::string testValue1 = "value1"; + std::string testValue2 = "value2"; + std::string testValue3 = "value3"; { CheckpointRecord record = CheckpointRecord::CreateNew(tempFile, { 1, 0 }); - record.AddCheckpoint(testCheckpoint); - record.AddContextData(testCheckpoint, testContextData, testName, testValue, 0); - record.AddContextData(testCheckpoint, testContextData, testName2, testValue2, 1); - } + CheckpointRecord::IdType checkpointId = record.AddCheckpoint(testCheckpoint); - { - CheckpointRecord record = CheckpointRecord::Open(tempFile, SQLiteStorageBase::OpenDisposition::ReadWrite); - auto contextData = record.GetContextData(testCheckpoint, testContextData); - REQUIRE(contextData.size() == 2); - REQUIRE(testValue == contextData[0]); - REQUIRE(testValue2 == contextData[1]); - record.RemoveContextData(testCheckpoint, testContextData); + // Add multiple fields. + record.SetDataValue(checkpointId, static_cast(AutomaticCheckpointData::Arguments), fieldName1, { testValue1 }); + record.SetDataValue(checkpointId, static_cast(AutomaticCheckpointData::Arguments), fieldName2, { testValue2, testValue3 }); } { - CheckpointRecord record = CheckpointRecord::Open(tempFile, SQLiteStorageBase::OpenDisposition::ReadWrite); - REQUIRE(record.IsEmpty()); + CheckpointRecord record = CheckpointRecord::Open(tempFile); + std::optional checkpointId = record.GetCheckpointIdByName(testCheckpoint); + REQUIRE(checkpointId.has_value()); + + const auto& fieldNames = record.GetDataFieldNames(checkpointId.value(), AutomaticCheckpointData::Arguments); + + REQUIRE(fieldNames[0] == fieldName1); + REQUIRE(fieldNames[1] == fieldName2); + + REQUIRE(testValue1 == record.GetDataFieldSingleValue(checkpointId.value(), AutomaticCheckpointData::Arguments, fieldName1)); + + const auto& multiValues = record.GetDataFieldMultiValue(checkpointId.value(), AutomaticCheckpointData::Arguments, fieldName2); + + REQUIRE(testValue2 == multiValues[0]); + REQUIRE(testValue3 == multiValues[1]); + + // REMOVE TEST CONTEXT FUNCTION. } } diff --git a/src/AppInstallerCLITests/ResumeFlow.cpp b/src/AppInstallerCLITests/ResumeFlow.cpp index e6c649ef58..910e04365c 100644 --- a/src/AppInstallerCLITests/ResumeFlow.cpp +++ b/src/AppInstallerCLITests/ResumeFlow.cpp @@ -17,23 +17,7 @@ using namespace AppInstaller::Repository::Microsoft; using namespace AppInstaller::Settings; using namespace AppInstaller::Runtime; using namespace TestCommon; - -TEST_CASE("ResumeFlow_InvalidGuid", "[Resume]") -{ - std::ostringstream resumeOutput; - TestContext context{ resumeOutput, std::cin }; - auto previousThreadGlobals = context.SetForCurrentThread(); - - context.Args.AddArg(Execution::Args::Type::ResumeId, "badGuid"sv); - - ResumeCommand resume({}); - context.SetExecutingCommand(&resume); - resume.Execute(context); - INFO(resumeOutput.str()); - - auto expectedMessage = Resource::String::InvalidResumeIdError(AppInstaller::Utility::LocIndString{ "badGuid"s }); - REQUIRE(resumeOutput.str().find(Resource::LocString(expectedMessage).get()) != std::string::npos); -} +using namespace AppInstaller::Checkpoints; TEST_CASE("ResumeFlow_IndexNotFound", "[Resume]") { @@ -62,16 +46,18 @@ TEST_CASE("ResumeFlow_InvalidClientVersion", "[Resume]") TestHook_SetPathOverride(PathName::CheckpointsLocation, tempCheckpointIndexDirectoryPath); // Create temp guid and populate with invalid client version. - std::string tempGuidString = "{b157d11f-4487-4e03-9447-9f9d50d66d8e}"; - std::string tempFileName = tempGuidString + ".db"; - auto tempIndexPath = tempCheckpointIndexDirectoryPath / tempFileName; + std::string tempGuidString = "{615339e9-3ac5-4e86-a5ab-c246657aca25}"; + auto tempIndexPath = tempCheckpointIndexDirectoryPath / tempGuidString / "checkpoints.db"; std::string_view invalidClientVersion = "1.2.3.4"sv; INFO("Using temporary file named: " << tempIndexPath); { - CheckpointRecord index = CheckpointRecord::CreateNew(tempIndexPath.u8string()); - index.SetMetadata(CheckpointMetadata::ClientVersion, invalidClientVersion); + // Manually set invalid client version + std::filesystem::create_directories(tempIndexPath.parent_path()); + CheckpointRecord checkpointRecord = CheckpointRecord::CreateNew(tempIndexPath.u8string()); + CheckpointRecord::IdType checkpointId = checkpointRecord.AddCheckpoint("automatic"sv); + checkpointRecord.SetDataValue(checkpointId, AutomaticCheckpointData::ClientVersion, {}, { "1.2.3.4" }); } std::ostringstream resumeOutput; @@ -104,8 +90,7 @@ TEST_CASE("ResumeFlow_EmptyIndex", "Resume") INFO("Using temporary file named: " << tempIndexPath); { - CheckpointRecord index = CheckpointRecord::CreateNew(tempGuidString); - index.SetMetadata(CheckpointMetadata::ClientVersion, AppInstaller::Runtime::GetClientVersion()); + CheckpointRecord checkpointRecord = CheckpointRecord::CreateNew(tempGuidString); } std::ostringstream resumeOutput; diff --git a/src/AppInstallerRepositoryCore/Microsoft/CheckpointRecord.cpp b/src/AppInstallerRepositoryCore/Microsoft/CheckpointRecord.cpp index 3ca55b4006..b94fff3766 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/CheckpointRecord.cpp +++ b/src/AppInstallerRepositoryCore/Microsoft/CheckpointRecord.cpp @@ -69,16 +69,14 @@ namespace AppInstaller::Repository::Microsoft return m_interface->GetCheckpointDataFields(m_dbconn, checkpointId, dataType); } - // Set data single value can be reused for all of these methods. - - void CheckpointRecord::SetDataSingleValue(IdType checkpointId, int dataType, std::string value) + void CheckpointRecord::SetDataValue(IdType checkpointId, int dataType, std::string field, std::vector values) { std::lock_guard lockInterface{ *m_interfaceLock }; - AICLI_LOG(Repo, Verbose, << "Setting checkpoint data [" << dataType << "] with value [" << value << "]"); + AICLI_LOG(Repo, Verbose, << "Setting checkpoint data [" << dataType << "]"); - SQLite::Savepoint savepoint = SQLite::Savepoint::Create(m_dbconn, "checkpointrecord_setdatasinglevalue"); + SQLite::Savepoint savepoint = SQLite::Savepoint::Create(m_dbconn, "checkpointrecord_setdatavalue"); - m_interface->SetCheckpointDataValue(m_dbconn, checkpointId, dataType, {}, { value }); + m_interface->SetCheckpointDataValues(m_dbconn, checkpointId, dataType, field, values); SetLastWriteTime(); savepoint.Commit(); @@ -86,43 +84,24 @@ namespace AppInstaller::Repository::Microsoft std::string CheckpointRecord::GetDataSingleValue(IdType checkpointId, int dataType) { - return m_interface->GetDataSingleValue(m_dbconn, checkpointId, dataType); + return m_interface->GetCheckpointDataValue(m_dbconn, checkpointId, dataType); } - void CheckpointRecord::SetDataFieldSingleValue(IdType checkpointId, int dataType, std::string field, std::string value) + std::string CheckpointRecord::GetDataFieldSingleValue(IdType checkpointId, int dataType, std::string_view field) { - std::lock_guard lockInterface{ *m_interfaceLock }; - AICLI_LOG(Repo, Verbose, << "Setting checkpoint data [" << dataType << "] with value [" << value << "]"); - - SQLite::Savepoint savepoint = SQLite::Savepoint::Create(m_dbconn, "checkpointrecord_setdatafieldsinglevalue"); + const auto& values = m_interface->GetCheckpointDataFieldValues(m_dbconn, checkpointId, dataType, field); - m_interface->SetCheckpointDataValue(m_dbconn, checkpointId, dataType, field, { value }); + if (values.empty()) + { + THROW_HR(E_UNEXPECTED); + } - SetLastWriteTime(); - savepoint.Commit(); - } - - std::string CheckpointRecord::GetDataFieldSingleValue(IdType checkpointId, int dataType, std::string_view field) - { - return m_interface->GetCheckpointDataValues(m_dbconn, checkpointId, dataType, field); + return values[0]; } std::vector CheckpointRecord::GetDataFieldMultiValue(IdType checkpointId, int dataType, std::string field) { - return std::vector(); - } - - void CheckpointRecord::SetDataFieldMultiValue(IdType checkpointId, int dataType, std::string field, std::vector values) - { - std::lock_guard lockInterface{ *m_interfaceLock }; - AICLI_LOG(Repo, Verbose, << "Setting checkpoint data [" << dataType << "]"); - - SQLite::Savepoint savepoint = SQLite::Savepoint::Create(m_dbconn, "checkpointrecord_setdatafieldmultivalue"); - - m_interface->SetCheckpointDataValue(m_dbconn, checkpointId, dataType, field, values); - - SetLastWriteTime(); - savepoint.Commit(); + return m_interface->GetCheckpointDataFieldValues(m_dbconn, checkpointId, dataType, field); } std::unique_ptr CheckpointRecord::CreateICheckpointRecord() const diff --git a/src/AppInstallerRepositoryCore/Microsoft/CheckpointRecord.h b/src/AppInstallerRepositoryCore/Microsoft/CheckpointRecord.h index 68f801f9f8..6743d75d9e 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/CheckpointRecord.h +++ b/src/AppInstallerRepositoryCore/Microsoft/CheckpointRecord.h @@ -47,24 +47,18 @@ namespace AppInstaller::Repository::Microsoft // Returns the available field names for a given checkpoint context data. std::vector GetDataFieldNames(IdType checkpointId, int dataType); - // Returns a single value for the given data type. - std::string GetDataSingleValue(IdType checkpointId, int dataType); + // Sets the value(s) for a given data type and field. + void SetDataValue(IdType checkpointId, int dataType, std::string field, std::vector values); - // Sets a single value for the given data type. - void SetDataSingleValue(IdType checkpointId, int dataType, std::string value); + // Gets a single value for the given data type. + std::string GetDataSingleValue(IdType checkpointId, int dataType); // Gets a single value for a given data type field. std::string GetDataFieldSingleValue(IdType checkpointId, int dataType, std::string_view field); - // Sets a single value for a given data type field. - void SetDataFieldSingleValue(IdType checkpointId, int dataType, std::string field, std::string value); - // Gets multiple values for a given data type field. std::vector GetDataFieldMultiValue(IdType checkpointId, int dataType, std::string field); - // Sets multiple values for a given data type field. - void SetDataFieldMultiValue(IdType checkpointId, int dataType, std::string field, std::vector values); - private: // Constructor used to open an existing index. CheckpointRecord(const std::string& target, SQLiteStorageBase::OpenDisposition disposition, Utility::ManagedFile&& indexFile); diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointContextTable.cpp b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointContextTable.cpp index ce1bc12209..05c811fcaa 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointContextTable.cpp +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointContextTable.cpp @@ -112,6 +112,19 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 return connection.GetLastInsertRowID(); } + bool CheckpointContextTable::HasDataField(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int type, std::string_view name) + { + SQLite::Builder::StatementBuilder builder; + builder.Select(SQLite::Builder::RowCount).From(s_CheckpointContextTable_Table_Name).Where(s_CheckpointContextTable_CheckpointId_Column); + builder.Equals(checkpointId).And(s_CheckpointContextTable_ContextData_Column).Equals(type).And(s_CheckpointContextTable_Name_Column).Equals(name); + + SQLite::Statement countStatement = builder.Prepare(connection); + + THROW_HR_IF(E_UNEXPECTED, !countStatement.Step()); + + return (countStatement.GetColumn(0) == 0); + } + std::vector CheckpointContextTable::GetDataFields(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int type) { SQLite::Builder::StatementBuilder builder; @@ -130,7 +143,7 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 return fields; } - std::vector CheckpointContextTable::GetDataValuesByName(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int contextData, std::string_view name) + std::vector CheckpointContextTable::GetDataValuesByFieldName(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int contextData, std::string_view name) { SQLite::Builder::StatementBuilder builder; builder.Select(s_CheckpointContextTable_Value_Column).From(s_CheckpointContextTable_Table_Name).Where(s_CheckpointContextTable_CheckpointId_Column); @@ -148,28 +161,8 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 return values; } - bool CheckpointContextTable::HasDataField(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int type, std::string_view name) - { - SQLite::Builder::StatementBuilder builder; - builder.Select(SQLite::Builder::RowCount).From(s_CheckpointContextTable_Table_Name).Where(s_CheckpointContextTable_CheckpointId_Column); - builder.Equals(checkpointId).And(s_CheckpointContextTable_ContextData_Column).Equals(type).And(s_CheckpointContextTable_Name_Column).Equals(name); - - SQLite::Statement countStatement = builder.Prepare(connection); - THROW_HR_IF(E_UNEXPECTED, !countStatement.Step()); - - return (countStatement.GetColumn(0) == 0); - } - - void CheckpointContextTable::RemoveContextData(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int contextData) - { - SQLite::Builder::StatementBuilder builder; - builder.DeleteFrom(s_CheckpointContextTable_Table_Name).Where(s_CheckpointContextTable_CheckpointId_Column).Equals(checkpointId) - .And(s_CheckpointContextTable_ContextData_Column).Equals(contextData); - builder.Execute(connection); - } - - std::string CheckpointContextTable::GetSingleDataField(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int type) + std::string CheckpointContextTable::GetDataValue(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int type) { SQLite::Builder::StatementBuilder builder; builder.Select(s_CheckpointContextTable_Value_Column).From(s_CheckpointContextTable_Table_Name).Where(s_CheckpointContextTable_CheckpointId_Column); @@ -186,4 +179,12 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 return {}; } } + + void CheckpointContextTable::RemoveContextData(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int contextData) + { + SQLite::Builder::StatementBuilder builder; + builder.DeleteFrom(s_CheckpointContextTable_Table_Name).Where(s_CheckpointContextTable_CheckpointId_Column).Equals(checkpointId) + .And(s_CheckpointContextTable_ContextData_Column).Equals(contextData); + builder.Execute(connection); + } } \ No newline at end of file diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointContextTable.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointContextTable.h index f9960411af..fe7b6cc700 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointContextTable.h +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointContextTable.h @@ -27,13 +27,13 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 static std::vector GetDataFields(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int type); // Gets the context data values by property name from a checkpoint id. - static std::vector GetDataValuesByName(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int contextData, std::string_view name); + static std::vector GetDataValuesByFieldName(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int contextData, std::string_view name); static bool HasDataField(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int type, std::string_view name); // Removes the context data by checkpoint id. static void RemoveContextData(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int contextData); - static std::string GetSingleDataField(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int type); + static std::string GetDataValue(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int type); }; } \ No newline at end of file diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointRecordInterface.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointRecordInterface.h index eb05ac2682..3265c9973c 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointRecordInterface.h +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointRecordInterface.h @@ -17,13 +17,10 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 SQLite::rowid_t AddCheckpoint(SQLite::Connection& connection, std::string_view checkpointName) override; std::optional GetCheckpointIdByName(SQLite::Connection& connection, std::string_view checkpointName) override; std::vector GetCheckpointDataTypes(SQLite::Connection& connection, SQLite::rowid_t checkpointId) override; - std::vector GetCheckpointDataFields(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int dataType) override; - std::vector GetCheckpointDataValues(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int dataType, std::string_view name) override; bool HasCheckpointDataField(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int dataType, std::string_view name) override; - void SetCheckpointDataValue(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int dataType, std::string_view name, std::vector values) override; - - - - std::string GetDataSingleValue(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int dataType) override; + void SetCheckpointDataValues(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int dataType, std::string_view name, std::vector values) override; + std::vector GetCheckpointDataFields(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int dataType) override; + std::vector GetCheckpointDataFieldValues(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int dataType, std::string_view name) override; + std::string GetCheckpointDataValue(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int dataType) override; }; } \ No newline at end of file diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointRecordInterface_1_0.cpp b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointRecordInterface_1_0.cpp index 0d99f8b467..ba2950bab3 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointRecordInterface_1_0.cpp +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointRecordInterface_1_0.cpp @@ -60,33 +60,40 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 return CheckpointContextTable::GetDataFields(connection, checkpointId, dataType); } - std::vector CheckpointRecordInterface::GetCheckpointDataValues(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int dataType, std::string_view name) - { - // This sohuld check whether the field name even exists. - return CheckpointContextTable::GetDataValuesByName(connection, checkpointId, dataType, name); - } - bool CheckpointRecordInterface::HasCheckpointDataField(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int dataType, std::string_view name) { return CheckpointContextTable::HasDataField(connection, checkpointId, dataType, name); } - void CheckpointRecordInterface::SetCheckpointDataValue(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int dataType, std::string_view name, std::vector values) + void CheckpointRecordInterface::SetCheckpointDataValues(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int dataType, std::string_view name, std::vector values) { SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "addcontextdata_v1_0"); - int index = 0; - for (const auto& value : values) + if (values.empty()) { - CheckpointContextTable::AddContextData(connection, checkpointId, dataType, name, value, index); - index++; + CheckpointContextTable::AddContextData(connection, checkpointId, dataType, name, {}, 0); + } + else + { + int index = 0; + + for (const auto& value : values) + { + CheckpointContextTable::AddContextData(connection, checkpointId, dataType, name, value, index); + index++; + } } savepoint.Commit(); } - std::string CheckpointRecordInterface::GetDataSingleValue(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int dataType) + std::string CheckpointRecordInterface::GetCheckpointDataValue(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int dataType) + { + return CheckpointContextTable::GetDataValue(connection, checkpointId, dataType); + } + + std::vector CheckpointRecordInterface::GetCheckpointDataFieldValues(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int dataType, std::string_view name) { - return CheckpointContextTable::GetSingleDataField(connection, checkpointId, dataType); + return CheckpointContextTable::GetDataValuesByFieldName(connection, checkpointId, dataType, name); } } \ No newline at end of file diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/ICheckpointRecord.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/ICheckpointRecord.h index b7847e2d68..0444c229ae 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/Schema/ICheckpointRecord.h +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/ICheckpointRecord.h @@ -20,22 +20,31 @@ namespace AppInstaller::Repository::Microsoft::Schema // Returns a bool value indicating whether all checkpoint tables are empty. virtual bool IsEmpty(SQLite::Connection& connection) = 0; + // Returns all recorded checkpoint names. virtual std::vector GetAvailableCheckpoints(SQLite::Connection& connection) = 0; - + + // Adds a new checkpoint to the Checkpoint table. virtual SQLite::rowid_t AddCheckpoint(SQLite::Connection& connection, std::string_view checkpointName) = 0; + // Gets the id of a checkpoint by name. virtual std::optional GetCheckpointIdByName(SQLite::Connection& connection, std::string_view checkpointName) = 0; + // Gets the data types associated with a checkpoint id. virtual std::vector GetCheckpointDataTypes(SQLite::Connection& connection, SQLite::rowid_t checkpointId) = 0; - virtual std::string GetDataSingleValue(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int dataType) = 0; + // Returns a bool value indicating whether a field exists for a checkpoint data type. + virtual bool HasCheckpointDataField(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int dataType, std::string_view name) = 0; - virtual std::vector GetCheckpointDataFields(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int dataType) = 0; + // Sets the field values for a checkpoint data type. + virtual void SetCheckpointDataValues(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int dataType, std::string_view name, std::vector values) = 0; - virtual std::vector GetCheckpointDataValues(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int dataType, std::string_view name) = 0; + // Gets a single value for a checkpoint data type. + virtual std::string GetCheckpointDataValue(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int dataType) = 0; - virtual bool HasCheckpointDataField(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int dataType, std::string_view name) = 0; + // Gets all field names for a checkpoint data type. + virtual std::vector GetCheckpointDataFields(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int dataType) = 0; - virtual void SetCheckpointDataValue(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int dataType, std::string_view name, std::vector values) = 0; + // Gets all field values for a checkpoint data type. + virtual std::vector GetCheckpointDataFieldValues(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int dataType, std::string_view name) = 0; }; } \ No newline at end of file From 5a9168109cd8eef3546f4fc2e6226b1068b027b1 Mon Sep 17 00:00:00 2001 From: Ryan Fu Date: Mon, 11 Sep 2023 15:17:04 -0700 Subject: [PATCH 31/43] clean up --- src/AppInstallerCLICore/COMContext.cpp | 2 - src/AppInstallerCLICore/COMContext.h | 2 +- src/AppInstallerCLICore/CheckpointManager.cpp | 35 +--- src/AppInstallerCLICore/CheckpointManager.h | 14 +- src/AppInstallerCLICore/Command.cpp | 2 - .../Commands/ResumeCommand.cpp | 2 + src/AppInstallerCLICore/ExecutionContext.cpp | 1 + src/AppInstallerCLICore/ExecutionContext.h | 4 +- .../Workflows/WorkflowBase.cpp | 8 - src/AppInstallerCLIE2ETests/ResumeCommand.cs | 2 +- src/AppInstallerCLITests/CheckpointRecord.cpp | 14 +- .../AppInstallerRepositoryCore.vcxproj | 4 +- ...AppInstallerRepositoryCore.vcxproj.filters | 4 +- .../Microsoft/CheckpointRecord.h | 14 +- .../Checkpoint_1_0/CheckpointContextTable.cpp | 190 ------------------ .../Checkpoint_1_0/CheckpointDataTable.cpp | 190 ++++++++++++++++++ ...ntContextTable.h => CheckpointDataTable.h} | 6 +- .../CheckpointRecordInterface_1_0.cpp | 22 +- .../Schema/Checkpoint_1_0/CheckpointTable.cpp | 18 -- .../Schema/Checkpoint_1_0/CheckpointTable.h | 3 +- 20 files changed, 250 insertions(+), 287 deletions(-) delete mode 100644 src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointContextTable.cpp create mode 100644 src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointDataTable.cpp rename src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/{CheckpointContextTable.h => CheckpointDataTable.h} (81%) diff --git a/src/AppInstallerCLICore/COMContext.cpp b/src/AppInstallerCLICore/COMContext.cpp index 936a46a7d2..17ae73ba86 100644 --- a/src/AppInstallerCLICore/COMContext.cpp +++ b/src/AppInstallerCLICore/COMContext.cpp @@ -9,8 +9,6 @@ namespace AppInstaller::CLI::Execution { static constexpr std::string_view s_comLogFileNamePrefix = "WinGetCOM"sv; - COMContext::~COMContext() = default; - NullStream::NullStream() { m_nullOut.reset(new std::ostream(&m_nullStreamBuf)); diff --git a/src/AppInstallerCLICore/COMContext.h b/src/AppInstallerCLICore/COMContext.h index a20ffd2b79..472907d76e 100644 --- a/src/AppInstallerCLICore/COMContext.h +++ b/src/AppInstallerCLICore/COMContext.h @@ -49,7 +49,7 @@ namespace AppInstaller::CLI::Execution SetFlags(CLI::Execution::ContextFlag::DisableInteractivity); } - ~COMContext(); + ~COMContext() = default; // IProgressSink void BeginProgress() override; diff --git a/src/AppInstallerCLICore/CheckpointManager.cpp b/src/AppInstallerCLICore/CheckpointManager.cpp index c1e95ed1e0..4ea89ed777 100644 --- a/src/AppInstallerCLICore/CheckpointManager.cpp +++ b/src/AppInstallerCLICore/CheckpointManager.cpp @@ -12,26 +12,9 @@ namespace AppInstaller::Checkpoints namespace { - constexpr std::string_view s_checkpoints_filename = "checkpoints.db"sv; - // This checkpoint name is reserved for the starting checkpoint which captures the automatic metadata. constexpr std::string_view s_AutomaticCheckpoint = "automatic"sv; - constexpr std::string_view s_Checkpoints = "Checkpoints"sv; - constexpr std::string_view s_ClientVersion = "ClientVersion"sv; - constexpr std::string_view s_CommandName = "CommandName"sv; - - std::string_view GetCheckpointMetadataString(AutomaticCheckpointData checkpointMetadata) - { - switch (checkpointMetadata) - { - case AutomaticCheckpointData::ClientVersion: - return s_ClientVersion; - case AutomaticCheckpointData::CommandName: - return s_CommandName; - default: - return "unknown"sv; - } - } + constexpr std::string_view s_CheckpointsFileName = "checkpoints.db"sv; } std::filesystem::path CheckpointManager::GetCheckpointRecordPath(GUID guid) @@ -51,7 +34,7 @@ namespace AppInstaller::Checkpoints THROW_HR_IF(ERROR_CANNOT_MAKE, !std::filesystem::is_directory(checkpointsDirectory)); } - auto indexPath = checkpointsDirectory / s_checkpoints_filename; + auto indexPath = checkpointsDirectory / s_CheckpointsFileName; return indexPath; } @@ -76,13 +59,6 @@ namespace AppInstaller::Checkpoints return checkpoint; } - Checkpoint CheckpointManager::CreateCheckpoint(std::string_view checkpointName) - { - CheckpointRecord::IdType startCheckpointId = m_checkpointRecord->AddCheckpoint(checkpointName); - Checkpoint checkpoint{ m_checkpointRecord, startCheckpointId }; - return checkpoint; - } - Checkpoint CheckpointManager::GetAutomaticCheckpoint() { std::optional startCheckpointId = m_checkpointRecord->GetCheckpointIdByName(s_AutomaticCheckpoint); @@ -95,6 +71,13 @@ namespace AppInstaller::Checkpoints return Checkpoint{ std::move(m_checkpointRecord), startCheckpointId.value() }; } + Checkpoint CheckpointManager::CreateCheckpoint(std::string_view checkpointName) + { + CheckpointRecord::IdType startCheckpointId = m_checkpointRecord->AddCheckpoint(checkpointName); + Checkpoint checkpoint{ m_checkpointRecord, startCheckpointId }; + return checkpoint; + } + std::vector> CheckpointManager::GetCheckpoints() { std::vector> checkpoints; diff --git a/src/AppInstallerCLICore/CheckpointManager.h b/src/AppInstallerCLICore/CheckpointManager.h index 2be9a47839..af5ea886f0 100644 --- a/src/AppInstallerCLICore/CheckpointManager.h +++ b/src/AppInstallerCLICore/CheckpointManager.h @@ -15,22 +15,30 @@ namespace AppInstaller::Checkpoints // Owns the lifetime of a checkpoint data base and creates the checkpoints. struct CheckpointManager { + // Constructor that generates a new resume id and creates the checkpoint record. CheckpointManager(); + + // Constructor that loads the resume id and opens an existing checkpoint record. CheckpointManager(GUID resumeId); ~CheckpointManager() = default; - // Gets the file path of the CheckpointRecord database. + // Gets the file path of the checkpoint record. static std::filesystem::path GetCheckpointRecordPath(GUID guid); - Checkpoint CreateCheckpoint(std::string_view checkpointName); - + // Creates a new automatic checkpoint. Checkpoint CreateAutomaticCheckpoint(); + // Gets the automatic checkpoint. Checkpoint GetAutomaticCheckpoint(); + // Creates a new data checkpoint. + Checkpoint CreateCheckpoint(std::string_view checkpointName); + + // Gets all data checkpoints. std::vector> GetCheckpoints(); + // Cleans up the checkpoint record. void CleanUpRecord(); private: diff --git a/src/AppInstallerCLICore/Command.cpp b/src/AppInstallerCLICore/Command.cpp index 8ea43bc564..6f8bd0cbb1 100644 --- a/src/AppInstallerCLICore/Command.cpp +++ b/src/AppInstallerCLICore/Command.cpp @@ -605,8 +605,6 @@ namespace AppInstaller::CLI ParseArgumentsStateMachine stateMachine{ inv, execArgs, std::move(definedArgs) }; - // Add function to save all of the execArgs, store in context Args data, store it in the record. - while (stateMachine.Step()) { stateMachine.ThrowIfError(); diff --git a/src/AppInstallerCLICore/Commands/ResumeCommand.cpp b/src/AppInstallerCLICore/Commands/ResumeCommand.cpp index adea26eac8..31e348d8e1 100644 --- a/src/AppInstallerCLICore/Commands/ResumeCommand.cpp +++ b/src/AppInstallerCLICore/Commands/ResumeCommand.cpp @@ -94,6 +94,8 @@ namespace AppInstaller::CLI } resumeContext.SetExecutingCommand(commandToResume.get()); + + // TODO: Ensure telemetry is properly handled for resume context. resumeContext.SetFlags(Execution::ContextFlag::Resume); auto previousThreadGlobals = resumeContext.SetForCurrentThread(); diff --git a/src/AppInstallerCLICore/ExecutionContext.cpp b/src/AppInstallerCLICore/ExecutionContext.cpp index 5cf9a11872..ca53d0236c 100644 --- a/src/AppInstallerCLICore/ExecutionContext.cpp +++ b/src/AppInstallerCLICore/ExecutionContext.cpp @@ -436,6 +436,7 @@ namespace AppInstaller::CLI::Execution void Context::Checkpoint(std::string_view checkpointName, std::vector contextData) { UNREFERENCED_PARAMETER(checkpointName); + UNREFERENCED_PARAMETER(contextData); if (!m_checkpointManager) { diff --git a/src/AppInstallerCLICore/ExecutionContext.h b/src/AppInstallerCLICore/ExecutionContext.h index bf3a9abe10..7fe4caa1f0 100644 --- a/src/AppInstallerCLICore/ExecutionContext.h +++ b/src/AppInstallerCLICore/ExecutionContext.h @@ -90,7 +90,7 @@ namespace AppInstaller::CLI::Execution Reporter(reporter, Execution::Reporter::clone_t{}), m_threadGlobals(threadGlobals, ThreadLocalStorage::WingetThreadGlobals::create_sub_thread_globals_t{}) {} - virtual ~Context(); + virtual ~Context(); // The path for console input/output for all functionality. Reporter Reporter; @@ -166,7 +166,7 @@ namespace AppInstaller::CLI::Execution bool ShouldExecuteWorkflowTask(const Workflow::WorkflowTask& task); #endif - // This should only be called by the resume , Sets the resume id for the checkpoint manager. + // Called by the resume command. Loads the checkpoint manager with the resume id and returns the automatic checkpoint. AppInstaller::Checkpoints::Checkpoint LoadCheckpoint(GUID resumeId); // Creates a checkpoint for the provided context data. diff --git a/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp b/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp index 74330070d7..cdd114a516 100644 --- a/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp +++ b/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp @@ -1275,14 +1275,6 @@ AppInstaller::CLI::Execution::Context& operator<<(AppInstaller::CLI::Execution:: AppInstaller::CLI::Execution::Context& operator<<(AppInstaller::CLI::Execution::Context& context, const AppInstaller::CLI::Workflow::WorkflowTask& task) { - if (AppInstaller::Settings::ExperimentalFeature::IsEnabled(ExperimentalFeature::Feature::Resume)) - { - //if (WI_IsFlagSet(context.GetFlags(), AppInstaller::CLI::Execution::ContextFlag::Resume) && !context.IsCurrentCheckpointAtTarget()) - //{ - // return context; - //} - } - if (!context.IsTerminated()) { #ifndef AICLI_DISABLE_TEST_HOOKS diff --git a/src/AppInstallerCLIE2ETests/ResumeCommand.cs b/src/AppInstallerCLIE2ETests/ResumeCommand.cs index 7506c9b2d5..f0878081fc 100644 --- a/src/AppInstallerCLIE2ETests/ResumeCommand.cs +++ b/src/AppInstallerCLIE2ETests/ResumeCommand.cs @@ -54,7 +54,7 @@ public void InstallExe_VerifyIndexDoesNotExist() /// Verifies that an error message is shown when an invalid resume id is provided. /// [Test] - public void ResumeInvalidId() + public void InvalidResumeId() { var invalidResumeId = "invalidResumeId"; var resumeResult = TestCommon.RunAICLICommand("resume", $"-g {invalidResumeId}"); diff --git a/src/AppInstallerCLITests/CheckpointRecord.cpp b/src/AppInstallerCLITests/CheckpointRecord.cpp index 6e504bf211..0459d2ad13 100644 --- a/src/AppInstallerCLITests/CheckpointRecord.cpp +++ b/src/AppInstallerCLITests/CheckpointRecord.cpp @@ -46,16 +46,16 @@ TEST_CASE("CheckpointRecord_WriteMetadata", "[checkpointRecord]") { CheckpointRecord record = CheckpointRecord::CreateNew(tempFile, { 1, 0 }); CheckpointRecord::IdType checkpointId = record.AddCheckpoint(testCheckpointName); - record.SetDataValue(checkpointId, static_cast(AutomaticCheckpointData::CommandName), {}, { testCommand }); - record.SetDataValue(checkpointId, static_cast(AutomaticCheckpointData::ClientVersion), {}, { testClientVersion }); + record.SetDataValue(checkpointId, AutomaticCheckpointData::CommandName, {}, { testCommand }); + record.SetDataValue(checkpointId, AutomaticCheckpointData::ClientVersion, {}, { testClientVersion }); } { CheckpointRecord record = CheckpointRecord::Open(tempFile); std::optional checkpointId = record.GetCheckpointIdByName(testCheckpointName); REQUIRE(checkpointId.has_value()); - REQUIRE(testCommand == record.GetDataSingleValue(checkpointId.value(), static_cast(AutomaticCheckpointData::CommandName))); - REQUIRE(testClientVersion == record.GetDataSingleValue(checkpointId.value(), static_cast(AutomaticCheckpointData::ClientVersion))); + REQUIRE(testCommand == record.GetDataSingleValue(checkpointId.value(), AutomaticCheckpointData::CommandName)); + REQUIRE(testClientVersion == record.GetDataSingleValue(checkpointId.value(), AutomaticCheckpointData::ClientVersion)); } } @@ -78,8 +78,8 @@ TEST_CASE("CheckpointRecord_WriteContextData", "[checkpointRecord]") CheckpointRecord::IdType checkpointId = record.AddCheckpoint(testCheckpoint); // Add multiple fields. - record.SetDataValue(checkpointId, static_cast(AutomaticCheckpointData::Arguments), fieldName1, { testValue1 }); - record.SetDataValue(checkpointId, static_cast(AutomaticCheckpointData::Arguments), fieldName2, { testValue2, testValue3 }); + record.SetDataValue(checkpointId, AutomaticCheckpointData::Arguments, fieldName1, { testValue1 }); + record.SetDataValue(checkpointId, AutomaticCheckpointData::Arguments, fieldName2, { testValue2, testValue3 }); } { @@ -98,7 +98,5 @@ TEST_CASE("CheckpointRecord_WriteContextData", "[checkpointRecord]") REQUIRE(testValue2 == multiValues[0]); REQUIRE(testValue3 == multiValues[1]); - - // REMOVE TEST CONTEXT FUNCTION. } } diff --git a/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj b/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj index fffa7be19e..7ce4454b0e 100644 --- a/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj +++ b/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj @@ -395,7 +395,7 @@ - + @@ -499,7 +499,7 @@ - + diff --git a/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj.filters b/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj.filters index e8cb926b2e..8e985bd21d 100644 --- a/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj.filters +++ b/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj.filters @@ -396,7 +396,7 @@ Microsoft - + Microsoft\Schema\Checkpoint_1_0 @@ -635,7 +635,7 @@ Source Files - + Microsoft\Schema\Checkpoint_1_0 diff --git a/src/AppInstallerRepositoryCore/Microsoft/CheckpointRecord.h b/src/AppInstallerRepositoryCore/Microsoft/CheckpointRecord.h index 6743d75d9e..6c8f9223ff 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/CheckpointRecord.h +++ b/src/AppInstallerRepositoryCore/Microsoft/CheckpointRecord.h @@ -37,26 +37,28 @@ namespace AppInstaller::Repository::Microsoft // Adds a new checkpoint name to the checkpoint table. IdType AddCheckpoint(std::string_view checkpointName); + // Gets the name of all checkpoints. std::vector GetCheckpoints(); + // Returns a boolean value indicating a field exists for a checkpoint data type. bool HasDataField(IdType checkpointId, int type, std::string name); - // Returns the available data types for a given checkpoint id. + // Returns the available data types for a checkpoint id. std::vector GetDataTypes(IdType checkpointId); - // Returns the available field names for a given checkpoint context data. + // Returns the available field names for a checkpoint data. std::vector GetDataFieldNames(IdType checkpointId, int dataType); - // Sets the value(s) for a given data type and field. + // Sets the value(s) for a data type and field. void SetDataValue(IdType checkpointId, int dataType, std::string field, std::vector values); - // Gets a single value for the given data type. + // Gets a single value for the data type. std::string GetDataSingleValue(IdType checkpointId, int dataType); - // Gets a single value for a given data type field. + // Gets a single value for a data type field. std::string GetDataFieldSingleValue(IdType checkpointId, int dataType, std::string_view field); - // Gets multiple values for a given data type field. + // Gets multiple values for a data type field. std::vector GetDataFieldMultiValue(IdType checkpointId, int dataType, std::string field); private: diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointContextTable.cpp b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointContextTable.cpp deleted file mode 100644 index 05c811fcaa..0000000000 --- a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointContextTable.cpp +++ /dev/null @@ -1,190 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. -#include "pch.h" -#include "CheckpointContextTable.h" -#include "SQLiteStatementBuilder.h" - -namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 -{ - using namespace SQLite; - using namespace std::string_view_literals; - static constexpr std::string_view s_CheckpointContextTable_Table_Name = "CheckpointContext"sv; - static constexpr std::string_view s_CheckpointContextTable_CheckpointId_Column = "CheckpointId"sv; - static constexpr std::string_view s_CheckpointContextTable_ContextData_Column = "ContextData"sv; - static constexpr std::string_view s_CheckpointContextTable_Name_Column = "Name"sv; - static constexpr std::string_view s_CheckpointContextTable_Value_Column = "Value"sv; - static constexpr std::string_view s_CheckpointContextTable_Index_Column = "Index"sv; - - namespace - { - SQLite::rowid_t SetNamedValue(SQLite::Connection& connection, std::string_view name, std::string_view value) - { - SQLite::Builder::StatementBuilder builder; - builder.InsertInto(s_CheckpointContextTable_Table_Name) - .Columns({ s_CheckpointContextTable_CheckpointId_Column, - s_CheckpointContextTable_ContextData_Column, - s_CheckpointContextTable_Name_Column, - s_CheckpointContextTable_Value_Column, - s_CheckpointContextTable_Index_Column}) - .Values(name, value); - - builder.Execute(connection); - return connection.GetLastInsertRowID(); - } - - std::string GetNamedValue(SQLite::Connection& connection, std::string_view name) - { - SQLite::Builder::StatementBuilder builder; - builder.Select({ s_CheckpointContextTable_Value_Column }) - .From(s_CheckpointContextTable_Table_Name).Where(s_CheckpointContextTable_Name_Column).Equals(name); - - SQLite::Statement statement = builder.Prepare(connection); - THROW_HR_IF(E_NOT_SET, !statement.Step()); - return statement.GetColumn(0); - } - } - - std::string_view CheckpointContextTable::TableName() - { - return s_CheckpointContextTable_Table_Name; - } - - void CheckpointContextTable::Create(SQLite::Connection& connection) - { - using namespace SQLite::Builder; - - SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "createCheckpointContextTable_v1_0"); - - StatementBuilder createTableBuilder; - createTableBuilder.CreateTable(s_CheckpointContextTable_Table_Name).BeginColumns(); - createTableBuilder.Column(ColumnBuilder(s_CheckpointContextTable_CheckpointId_Column, Type::Int).NotNull()); - createTableBuilder.Column(ColumnBuilder(s_CheckpointContextTable_ContextData_Column, Type::Int)); - createTableBuilder.Column(ColumnBuilder(s_CheckpointContextTable_Name_Column, Type::Text)); - createTableBuilder.Column(ColumnBuilder(s_CheckpointContextTable_Value_Column, Type::Text)); - createTableBuilder.Column(ColumnBuilder(s_CheckpointContextTable_Index_Column, Type::Int)); - createTableBuilder.EndColumns(); - createTableBuilder.Execute(connection); - savepoint.Commit(); - } - - bool CheckpointContextTable::IsEmpty(SQLite::Connection& connection) - { - SQLite::Builder::StatementBuilder builder; - builder.Select(SQLite::Builder::RowCount).From(s_CheckpointContextTable_Table_Name); - - SQLite::Statement countStatement = builder.Prepare(connection); - - THROW_HR_IF(E_UNEXPECTED, !countStatement.Step()); - - return (countStatement.GetColumn(0) == 0); - } - - std::vector CheckpointContextTable::GetAvailableData(SQLite::Connection& connection, SQLite::rowid_t checkpointId) - { - SQLite::Builder::StatementBuilder builder; - builder.Select(s_CheckpointContextTable_ContextData_Column).From(s_CheckpointContextTable_Table_Name).Where(s_CheckpointContextTable_CheckpointId_Column); - builder.Equals(checkpointId); - - SQLite::Statement select = builder.Prepare(connection); - - std::vector availableData; - - while (select.Step()) - { - availableData.emplace_back(select.GetColumn(0)); - } - - return availableData; - } - - SQLite::rowid_t CheckpointContextTable::AddContextData(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int contextData, std::string_view name, std::string_view value, int index) - { - SQLite::Builder::StatementBuilder builder; - builder.InsertInto(s_CheckpointContextTable_Table_Name) - .Columns({ s_CheckpointContextTable_CheckpointId_Column, - s_CheckpointContextTable_ContextData_Column, - s_CheckpointContextTable_Name_Column, - s_CheckpointContextTable_Value_Column, - s_CheckpointContextTable_Index_Column}) - .Values(checkpointId, contextData, name, value, index); - - builder.Execute(connection); - return connection.GetLastInsertRowID(); - } - - bool CheckpointContextTable::HasDataField(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int type, std::string_view name) - { - SQLite::Builder::StatementBuilder builder; - builder.Select(SQLite::Builder::RowCount).From(s_CheckpointContextTable_Table_Name).Where(s_CheckpointContextTable_CheckpointId_Column); - builder.Equals(checkpointId).And(s_CheckpointContextTable_ContextData_Column).Equals(type).And(s_CheckpointContextTable_Name_Column).Equals(name); - - SQLite::Statement countStatement = builder.Prepare(connection); - - THROW_HR_IF(E_UNEXPECTED, !countStatement.Step()); - - return (countStatement.GetColumn(0) == 0); - } - - std::vector CheckpointContextTable::GetDataFields(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int type) - { - SQLite::Builder::StatementBuilder builder; - builder.Select(s_CheckpointContextTable_Name_Column).From(s_CheckpointContextTable_Table_Name).Where(s_CheckpointContextTable_CheckpointId_Column); - builder.Equals(checkpointId).And(s_CheckpointContextTable_ContextData_Column).Equals(type); - - SQLite::Statement select = builder.Prepare(connection); - - std::vector fields; - - while (select.Step()) - { - fields.emplace_back(select.GetColumn(0)); - } - - return fields; - } - - std::vector CheckpointContextTable::GetDataValuesByFieldName(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int contextData, std::string_view name) - { - SQLite::Builder::StatementBuilder builder; - builder.Select(s_CheckpointContextTable_Value_Column).From(s_CheckpointContextTable_Table_Name).Where(s_CheckpointContextTable_CheckpointId_Column); - builder.Equals(checkpointId).And(s_CheckpointContextTable_ContextData_Column).Equals(contextData).And(s_CheckpointContextTable_Name_Column).Equals(name); - - SQLite::Statement select = builder.Prepare(connection); - - std::vector values; - - while (select.Step()) - { - values.emplace_back(select.GetColumn(0)); - } - - return values; - } - - - std::string CheckpointContextTable::GetDataValue(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int type) - { - SQLite::Builder::StatementBuilder builder; - builder.Select(s_CheckpointContextTable_Value_Column).From(s_CheckpointContextTable_Table_Name).Where(s_CheckpointContextTable_CheckpointId_Column); - builder.Equals(checkpointId).And(s_CheckpointContextTable_ContextData_Column).Equals(type); - - SQLite::Statement select = builder.Prepare(connection); - - if (select.Step()) - { - return select.GetColumn(0); - } - else - { - return {}; - } - } - - void CheckpointContextTable::RemoveContextData(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int contextData) - { - SQLite::Builder::StatementBuilder builder; - builder.DeleteFrom(s_CheckpointContextTable_Table_Name).Where(s_CheckpointContextTable_CheckpointId_Column).Equals(checkpointId) - .And(s_CheckpointContextTable_ContextData_Column).Equals(contextData); - builder.Execute(connection); - } -} \ No newline at end of file diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointDataTable.cpp b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointDataTable.cpp new file mode 100644 index 0000000000..10bf31d5bb --- /dev/null +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointDataTable.cpp @@ -0,0 +1,190 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#include "pch.h" +#include "CheckpointDataTable.h" +#include "SQLiteStatementBuilder.h" + +namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 +{ + using namespace SQLite; + using namespace std::string_view_literals; + static constexpr std::string_view s_CheckpointDataTable_Table_Name = "CheckpointData"sv; + static constexpr std::string_view s_CheckpointDataTable_CheckpointId_Column = "CheckpointId"sv; + static constexpr std::string_view s_CheckpointDataTable_ContextData_Column = "ContextData"sv; + static constexpr std::string_view s_CheckpointDataTable_Name_Column = "Name"sv; + static constexpr std::string_view s_CheckpointDataTable_Value_Column = "Value"sv; + static constexpr std::string_view s_CheckpointDataTable_Index_Column = "Index"sv; + + namespace + { + SQLite::rowid_t SetNamedValue(SQLite::Connection& connection, std::string_view name, std::string_view value) + { + SQLite::Builder::StatementBuilder builder; + builder.InsertInto(s_CheckpointDataTable_Table_Name) + .Columns({ s_CheckpointDataTable_CheckpointId_Column, + s_CheckpointDataTable_ContextData_Column, + s_CheckpointDataTable_Name_Column, + s_CheckpointDataTable_Value_Column, + s_CheckpointDataTable_Index_Column}) + .Values(name, value); + + builder.Execute(connection); + return connection.GetLastInsertRowID(); + } + + std::string GetNamedValue(SQLite::Connection& connection, std::string_view name) + { + SQLite::Builder::StatementBuilder builder; + builder.Select({ s_CheckpointDataTable_Value_Column }) + .From(s_CheckpointDataTable_Table_Name).Where(s_CheckpointDataTable_Name_Column).Equals(name); + + SQLite::Statement statement = builder.Prepare(connection); + THROW_HR_IF(E_NOT_SET, !statement.Step()); + return statement.GetColumn(0); + } + } + + std::string_view CheckpointDataTable::TableName() + { + return s_CheckpointDataTable_Table_Name; + } + + void CheckpointDataTable::Create(SQLite::Connection& connection) + { + using namespace SQLite::Builder; + + SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "createCheckpointDataTable_v1_0"); + + StatementBuilder createTableBuilder; + createTableBuilder.CreateTable(s_CheckpointDataTable_Table_Name).BeginColumns(); + createTableBuilder.Column(ColumnBuilder(s_CheckpointDataTable_CheckpointId_Column, Type::Int).NotNull()); + createTableBuilder.Column(ColumnBuilder(s_CheckpointDataTable_ContextData_Column, Type::Int)); + createTableBuilder.Column(ColumnBuilder(s_CheckpointDataTable_Name_Column, Type::Text)); + createTableBuilder.Column(ColumnBuilder(s_CheckpointDataTable_Value_Column, Type::Text)); + createTableBuilder.Column(ColumnBuilder(s_CheckpointDataTable_Index_Column, Type::Int)); + createTableBuilder.EndColumns(); + createTableBuilder.Execute(connection); + savepoint.Commit(); + } + + bool CheckpointDataTable::IsEmpty(SQLite::Connection& connection) + { + SQLite::Builder::StatementBuilder builder; + builder.Select(SQLite::Builder::RowCount).From(s_CheckpointDataTable_Table_Name); + + SQLite::Statement countStatement = builder.Prepare(connection); + + THROW_HR_IF(E_UNEXPECTED, !countStatement.Step()); + + return (countStatement.GetColumn(0) == 0); + } + + std::vector CheckpointDataTable::GetAvailableData(SQLite::Connection& connection, SQLite::rowid_t checkpointId) + { + SQLite::Builder::StatementBuilder builder; + builder.Select(s_CheckpointDataTable_ContextData_Column).From(s_CheckpointDataTable_Table_Name).Where(s_CheckpointDataTable_CheckpointId_Column); + builder.Equals(checkpointId); + + SQLite::Statement select = builder.Prepare(connection); + + std::vector availableData; + + while (select.Step()) + { + availableData.emplace_back(select.GetColumn(0)); + } + + return availableData; + } + + SQLite::rowid_t CheckpointDataTable::AddCheckpointData(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int contextData, std::string_view name, std::string_view value, int index) + { + SQLite::Builder::StatementBuilder builder; + builder.InsertInto(s_CheckpointDataTable_Table_Name) + .Columns({ s_CheckpointDataTable_CheckpointId_Column, + s_CheckpointDataTable_ContextData_Column, + s_CheckpointDataTable_Name_Column, + s_CheckpointDataTable_Value_Column, + s_CheckpointDataTable_Index_Column}) + .Values(checkpointId, contextData, name, value, index); + + builder.Execute(connection); + return connection.GetLastInsertRowID(); + } + + bool CheckpointDataTable::HasDataField(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int type, std::string_view name) + { + SQLite::Builder::StatementBuilder builder; + builder.Select(SQLite::Builder::RowCount).From(s_CheckpointDataTable_Table_Name).Where(s_CheckpointDataTable_CheckpointId_Column); + builder.Equals(checkpointId).And(s_CheckpointDataTable_ContextData_Column).Equals(type).And(s_CheckpointDataTable_Name_Column).Equals(name); + + SQLite::Statement countStatement = builder.Prepare(connection); + + THROW_HR_IF(E_UNEXPECTED, !countStatement.Step()); + + return (countStatement.GetColumn(0) == 0); + } + + std::vector CheckpointDataTable::GetDataFields(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int type) + { + SQLite::Builder::StatementBuilder builder; + builder.Select(s_CheckpointDataTable_Name_Column).From(s_CheckpointDataTable_Table_Name).Where(s_CheckpointDataTable_CheckpointId_Column); + builder.Equals(checkpointId).And(s_CheckpointDataTable_ContextData_Column).Equals(type); + + SQLite::Statement select = builder.Prepare(connection); + + std::vector fields; + + while (select.Step()) + { + fields.emplace_back(select.GetColumn(0)); + } + + return fields; + } + + std::vector CheckpointDataTable::GetDataValuesByFieldName(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int contextData, std::string_view name) + { + SQLite::Builder::StatementBuilder builder; + builder.Select(s_CheckpointDataTable_Value_Column).From(s_CheckpointDataTable_Table_Name).Where(s_CheckpointDataTable_CheckpointId_Column); + builder.Equals(checkpointId).And(s_CheckpointDataTable_ContextData_Column).Equals(contextData).And(s_CheckpointDataTable_Name_Column).Equals(name); + + SQLite::Statement select = builder.Prepare(connection); + + std::vector values; + + while (select.Step()) + { + values.emplace_back(select.GetColumn(0)); + } + + return values; + } + + + std::string CheckpointDataTable::GetDataValue(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int type) + { + SQLite::Builder::StatementBuilder builder; + builder.Select(s_CheckpointDataTable_Value_Column).From(s_CheckpointDataTable_Table_Name).Where(s_CheckpointDataTable_CheckpointId_Column); + builder.Equals(checkpointId).And(s_CheckpointDataTable_ContextData_Column).Equals(type); + + SQLite::Statement select = builder.Prepare(connection); + + if (select.Step()) + { + return select.GetColumn(0); + } + else + { + return {}; + } + } + + void CheckpointDataTable::RemoveData(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int contextData) + { + SQLite::Builder::StatementBuilder builder; + builder.DeleteFrom(s_CheckpointDataTable_Table_Name).Where(s_CheckpointDataTable_CheckpointId_Column).Equals(checkpointId) + .And(s_CheckpointDataTable_ContextData_Column).Equals(contextData); + builder.Execute(connection); + } +} \ No newline at end of file diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointContextTable.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointDataTable.h similarity index 81% rename from src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointContextTable.h rename to src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointDataTable.h index fe7b6cc700..a3daf281fa 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointContextTable.h +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointDataTable.h @@ -7,7 +7,7 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 { - struct CheckpointContextTable + struct CheckpointDataTable { // Get the table name. static std::string_view TableName(); @@ -22,7 +22,7 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 static std::vector GetAvailableData(SQLite::Connection& connection, SQLite::rowid_t checkpointId); // Adds a context data for a checkpoint. Index is used to represent the item number if the context data has more than one value. - static SQLite::rowid_t AddContextData(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int contextData, std::string_view name, std::string_view value, int index = 1); + static SQLite::rowid_t AddCheckpointData(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int contextData, std::string_view name, std::string_view value, int index = 1); static std::vector GetDataFields(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int type); @@ -32,7 +32,7 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 static bool HasDataField(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int type, std::string_view name); // Removes the context data by checkpoint id. - static void RemoveContextData(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int contextData); + static void RemoveData(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int contextData); static std::string GetDataValue(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int type); }; diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointRecordInterface_1_0.cpp b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointRecordInterface_1_0.cpp index ba2950bab3..388caa08e4 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointRecordInterface_1_0.cpp +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointRecordInterface_1_0.cpp @@ -2,7 +2,7 @@ // Licensed under the MIT License. #include "pch.h" #include "Microsoft/Schema/Checkpoint_1_0/CheckpointRecordInterface.h" -#include "Microsoft/Schema/Checkpoint_1_0/CheckpointContextTable.h" +#include "Microsoft/Schema/Checkpoint_1_0/CheckpointDataTable.h" #include "Microsoft/Schema/Checkpoint_1_0/CheckpointTable.h" namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 @@ -16,13 +16,13 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 { SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "createCheckpointTables_v1_0"); Checkpoint_V1_0::CheckpointTable::Create(connection); - Checkpoint_V1_0::CheckpointContextTable::Create(connection); + Checkpoint_V1_0::CheckpointDataTable::Create(connection); savepoint.Commit(); } bool CheckpointRecordInterface::IsEmpty(SQLite::Connection& connection) { - return CheckpointContextTable::IsEmpty(connection); + return CheckpointDataTable::IsEmpty(connection); } std::vector CheckpointRecordInterface::GetAvailableCheckpoints(SQLite::Connection& connection) @@ -52,26 +52,26 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 std::vector CheckpointRecordInterface::GetCheckpointDataTypes(SQLite::Connection& connection, SQLite::rowid_t checkpointId) { - return CheckpointContextTable::GetAvailableData(connection, checkpointId); + return CheckpointDataTable::GetAvailableData(connection, checkpointId); } std::vector CheckpointRecordInterface::GetCheckpointDataFields(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int dataType) { - return CheckpointContextTable::GetDataFields(connection, checkpointId, dataType); + return CheckpointDataTable::GetDataFields(connection, checkpointId, dataType); } bool CheckpointRecordInterface::HasCheckpointDataField(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int dataType, std::string_view name) { - return CheckpointContextTable::HasDataField(connection, checkpointId, dataType, name); + return CheckpointDataTable::HasDataField(connection, checkpointId, dataType, name); } void CheckpointRecordInterface::SetCheckpointDataValues(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int dataType, std::string_view name, std::vector values) { - SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "addcontextdata_v1_0"); + SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "setcheckpointdata_v1_0"); if (values.empty()) { - CheckpointContextTable::AddContextData(connection, checkpointId, dataType, name, {}, 0); + CheckpointDataTable::AddCheckpointData(connection, checkpointId, dataType, name, {}, 0); } else { @@ -79,7 +79,7 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 for (const auto& value : values) { - CheckpointContextTable::AddContextData(connection, checkpointId, dataType, name, value, index); + CheckpointDataTable::AddCheckpointData(connection, checkpointId, dataType, name, value, index); index++; } } @@ -89,11 +89,11 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 std::string CheckpointRecordInterface::GetCheckpointDataValue(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int dataType) { - return CheckpointContextTable::GetDataValue(connection, checkpointId, dataType); + return CheckpointDataTable::GetDataValue(connection, checkpointId, dataType); } std::vector CheckpointRecordInterface::GetCheckpointDataFieldValues(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int dataType, std::string_view name) { - return CheckpointContextTable::GetDataValuesByFieldName(connection, checkpointId, dataType, name); + return CheckpointDataTable::GetDataValuesByFieldName(connection, checkpointId, dataType, name); } } \ No newline at end of file diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointTable.cpp b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointTable.cpp index 80f0b6a6ac..14d8a9f950 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointTable.cpp +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointTable.cpp @@ -49,24 +49,6 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 return checkpoints; } - std::string CheckpointTable::GetLastCheckpoint(SQLite::Connection& connection) - { - // Sort by descending and get last checkpoint. - SQLite::Builder::StatementBuilder builder; - builder.Select(s_CheckpointTable_Name_Column).From(s_CheckpointTable_Table_Name).OrderBy(SQLite::RowIDName).Descending(); - - SQLite::Statement select = builder.Prepare(connection); - - if (select.Step()) - { - return select.GetColumn(0); - } - else - { - return {}; - } - } - SQLite::rowid_t CheckpointTable::AddCheckpoint(SQLite::Connection& connection, std::string_view checkpointName) { SQLite::Builder::StatementBuilder builder; diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointTable.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointTable.h index 55d51fdf01..daefe73bdd 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointTable.h +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointTable.h @@ -15,10 +15,9 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 // Creates the table with named indices. static void Create(SQLite::Connection& connection); + // Gets the names of all checkpoints. static std::vector GetCheckpoints(SQLite::Connection& connection); - static std::string GetLastCheckpoint(SQLite::Connection& connection); - // Adds a checkpoint. static SQLite::rowid_t AddCheckpoint(SQLite::Connection& connection, std::string_view checkpointName); From 6076097ea2e4cf8d316164ff83cf7827d121a28f Mon Sep 17 00:00:00 2001 From: Ryan Fu Date: Tue, 12 Sep 2023 08:22:31 -0700 Subject: [PATCH 32/43] fix tests and fix spelling --- .github/actions/spelling/allow.txt | 1 + src/AppInstallerCLICore/CheckpointManager.cpp | 22 +++++++-------- src/AppInstallerCLICore/CheckpointManager.h | 10 +++---- .../Commands/InstallCommand.cpp | 1 - .../Commands/ResumeCommand.cpp | 8 +++++- src/AppInstallerCLICore/ExecutionContext.cpp | 7 ++++- src/AppInstallerCLICore/ExecutionContext.h | 5 +++- src/AppInstallerCLITests/CheckpointRecord.cpp | 27 +++++++++++++++++++ src/AppInstallerCLITests/ResumeFlow.cpp | 18 +++++++------ src/AppInstallerCommonCore/Runtime.cpp | 2 ++ 10 files changed, 72 insertions(+), 29 deletions(-) diff --git a/.github/actions/spelling/allow.txt b/.github/actions/spelling/allow.txt index 8b19c940ef..fa3130ccb0 100644 --- a/.github/actions/spelling/allow.txt +++ b/.github/actions/spelling/allow.txt @@ -499,6 +499,7 @@ selectany SERVICEPACKMAJOR SERVICEPACKMINOR setfill +setcheckpointdata setdatavalue setschemaversion setupexitcodes diff --git a/src/AppInstallerCLICore/CheckpointManager.cpp b/src/AppInstallerCLICore/CheckpointManager.cpp index 4ea89ed777..17a5014f79 100644 --- a/src/AppInstallerCLICore/CheckpointManager.cpp +++ b/src/AppInstallerCLICore/CheckpointManager.cpp @@ -10,12 +10,9 @@ namespace AppInstaller::Checkpoints using namespace AppInstaller::Repository::Microsoft; using namespace AppInstaller::Repository::SQLite; - namespace - { - // This checkpoint name is reserved for the starting checkpoint which captures the automatic metadata. - constexpr std::string_view s_AutomaticCheckpoint = "automatic"sv; - constexpr std::string_view s_CheckpointsFileName = "checkpoints.db"sv; - } + // This checkpoint name is reserved for the starting checkpoint which captures the automatic metadata. + constexpr std::string_view s_AutomaticCheckpoint = "automatic"sv; + constexpr std::string_view s_CheckpointsFileName = "checkpoints.db"sv; std::filesystem::path CheckpointManager::GetCheckpointRecordPath(GUID guid) { @@ -59,16 +56,17 @@ namespace AppInstaller::Checkpoints return checkpoint; } - Checkpoint CheckpointManager::GetAutomaticCheckpoint() + std::optional> CheckpointManager::GetAutomaticCheckpoint() { std::optional startCheckpointId = m_checkpointRecord->GetCheckpointIdByName(s_AutomaticCheckpoint); - - if (!startCheckpointId.has_value()) + if (startCheckpointId.has_value()) { - THROW_HR(E_UNEXPECTED); + return Checkpoint{ std::move(m_checkpointRecord), startCheckpointId.value() }; + } + else + { + return {}; } - - return Checkpoint{ std::move(m_checkpointRecord), startCheckpointId.value() }; } Checkpoint CheckpointManager::CreateCheckpoint(std::string_view checkpointName) diff --git a/src/AppInstallerCLICore/CheckpointManager.h b/src/AppInstallerCLICore/CheckpointManager.h index af5ea886f0..4fd1a12935 100644 --- a/src/AppInstallerCLICore/CheckpointManager.h +++ b/src/AppInstallerCLICore/CheckpointManager.h @@ -26,18 +26,18 @@ namespace AppInstaller::Checkpoints // Gets the file path of the checkpoint record. static std::filesystem::path GetCheckpointRecordPath(GUID guid); + // Gets the automatic checkpoint. + std::optional> GetAutomaticCheckpoint(); + // Creates a new automatic checkpoint. Checkpoint CreateAutomaticCheckpoint(); - // Gets the automatic checkpoint. - Checkpoint GetAutomaticCheckpoint(); + // Gets all data checkpoints. + std::vector> GetCheckpoints(); // Creates a new data checkpoint. Checkpoint CreateCheckpoint(std::string_view checkpointName); - // Gets all data checkpoints. - std::vector> GetCheckpoints(); - // Cleans up the checkpoint record. void CleanUpRecord(); diff --git a/src/AppInstallerCLICore/Commands/InstallCommand.cpp b/src/AppInstallerCLICore/Commands/InstallCommand.cpp index 418c23c477..d4be057675 100644 --- a/src/AppInstallerCLICore/Commands/InstallCommand.cpp +++ b/src/AppInstallerCLICore/Commands/InstallCommand.cpp @@ -107,7 +107,6 @@ namespace AppInstaller::CLI void InstallCommand::Resume(Context& context) const { // TODO: Load context data from checkpoint for install command. - ExecuteInternal(context); } diff --git a/src/AppInstallerCLICore/Commands/ResumeCommand.cpp b/src/AppInstallerCLICore/Commands/ResumeCommand.cpp index 31e348d8e1..f9313602f1 100644 --- a/src/AppInstallerCLICore/Commands/ResumeCommand.cpp +++ b/src/AppInstallerCLICore/Commands/ResumeCommand.cpp @@ -50,8 +50,14 @@ namespace AppInstaller::CLI } Execution::Context resumeContext = context.CreateEmptyContext(); + std::optional> foundAutomaticCheckpoint = resumeContext.LoadCheckpoint(checkpointId); + if (!foundAutomaticCheckpoint.has_value()) + { + context.Reporter.Error() << Resource::String::ResumeStateDataNotFoundError << std::endl; + AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_INVALID_RESUME_STATE); + } - Checkpoint automaticCheckpoint = resumeContext.LoadCheckpoint(checkpointId); + Checkpoint automaticCheckpoint = foundAutomaticCheckpoint.value(); const auto& checkpointClientVersion = automaticCheckpoint.Get(AutomaticCheckpointData::ClientVersion); if (checkpointClientVersion != AppInstaller::Runtime::GetClientVersion().get()) diff --git a/src/AppInstallerCLICore/ExecutionContext.cpp b/src/AppInstallerCLICore/ExecutionContext.cpp index ca53d0236c..e3e7118b95 100644 --- a/src/AppInstallerCLICore/ExecutionContext.cpp +++ b/src/AppInstallerCLICore/ExecutionContext.cpp @@ -427,12 +427,17 @@ namespace AppInstaller::CLI::Execution } #endif - Checkpoint Context::LoadCheckpoint(GUID resumeId) + std::optional> Context::LoadCheckpoint(GUID resumeId) { m_checkpointManager = std::make_unique(resumeId); return m_checkpointManager->GetAutomaticCheckpoint(); } + std::vector> Context::GetCheckpoints() + { + return m_checkpointManager->GetCheckpoints(); + } + void Context::Checkpoint(std::string_view checkpointName, std::vector contextData) { UNREFERENCED_PARAMETER(checkpointName); diff --git a/src/AppInstallerCLICore/ExecutionContext.h b/src/AppInstallerCLICore/ExecutionContext.h index 7fe4caa1f0..dd90e2c58f 100644 --- a/src/AppInstallerCLICore/ExecutionContext.h +++ b/src/AppInstallerCLICore/ExecutionContext.h @@ -167,7 +167,10 @@ namespace AppInstaller::CLI::Execution #endif // Called by the resume command. Loads the checkpoint manager with the resume id and returns the automatic checkpoint. - AppInstaller::Checkpoints::Checkpoint LoadCheckpoint(GUID resumeId); + std::optional> LoadCheckpoint(GUID resumeId); + + // Returns data checkpoints in the order of latest checkpoint to earliest. + std::vector> GetCheckpoints(); // Creates a checkpoint for the provided context data. void Checkpoint(std::string_view checkpointName, std::vector contextData); diff --git a/src/AppInstallerCLITests/CheckpointRecord.cpp b/src/AppInstallerCLITests/CheckpointRecord.cpp index 0459d2ad13..024058d699 100644 --- a/src/AppInstallerCLITests/CheckpointRecord.cpp +++ b/src/AppInstallerCLITests/CheckpointRecord.cpp @@ -100,3 +100,30 @@ TEST_CASE("CheckpointRecord_WriteContextData", "[checkpointRecord]") REQUIRE(testValue3 == multiValues[1]); } } + +TEST_CASE("CheckpointRecord_CheckpointOrder", "[checkpointRecord]") +{ + // Verifies that the checkpoints are shown in reverse order (latest first). + TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; + INFO("Using temporary file named: " << tempFile.GetPath()); + + std::string_view firstCheckpoint = "firstCheckpoint"sv; + std::string_view secondCheckpoint = "secondCheckpoint"sv; + std::string_view thirdCheckpoint = "thirdCheckpoint"sv; + + { + CheckpointRecord record = CheckpointRecord::CreateNew(tempFile, { 1, 0 }); + record.AddCheckpoint(firstCheckpoint); + record.AddCheckpoint(secondCheckpoint); + record.AddCheckpoint(thirdCheckpoint); + } + + { + CheckpointRecord record = CheckpointRecord::Open(tempFile); + const auto& checkpoints = record.GetCheckpoints(); + + REQUIRE(checkpoints[0] == thirdCheckpoint); + REQUIRE(checkpoints[1] == secondCheckpoint); + REQUIRE(checkpoints[2] == firstCheckpoint); + } +} diff --git a/src/AppInstallerCLITests/ResumeFlow.cpp b/src/AppInstallerCLITests/ResumeFlow.cpp index 910e04365c..50ebeca934 100644 --- a/src/AppInstallerCLITests/ResumeFlow.cpp +++ b/src/AppInstallerCLITests/ResumeFlow.cpp @@ -19,6 +19,9 @@ using namespace AppInstaller::Runtime; using namespace TestCommon; using namespace AppInstaller::Checkpoints; +constexpr std::string_view s_AutomaticCheckpoint = "automatic"sv; +constexpr std::string_view s_CheckpointsFileName = "checkpoints.db"sv; + TEST_CASE("ResumeFlow_IndexNotFound", "[Resume]") { std::string tempGuidString = "{ec3a098c-a815-4d52-8866-946c03093a37}"; @@ -47,7 +50,7 @@ TEST_CASE("ResumeFlow_InvalidClientVersion", "[Resume]") // Create temp guid and populate with invalid client version. std::string tempGuidString = "{615339e9-3ac5-4e86-a5ab-c246657aca25}"; - auto tempIndexPath = tempCheckpointIndexDirectoryPath / tempGuidString / "checkpoints.db"; + auto tempIndexPath = tempCheckpointIndexDirectoryPath / tempGuidString / s_CheckpointsFileName; std::string_view invalidClientVersion = "1.2.3.4"sv; INFO("Using temporary file named: " << tempIndexPath); @@ -56,7 +59,7 @@ TEST_CASE("ResumeFlow_InvalidClientVersion", "[Resume]") // Manually set invalid client version std::filesystem::create_directories(tempIndexPath.parent_path()); CheckpointRecord checkpointRecord = CheckpointRecord::CreateNew(tempIndexPath.u8string()); - CheckpointRecord::IdType checkpointId = checkpointRecord.AddCheckpoint("automatic"sv); + CheckpointRecord::IdType checkpointId = checkpointRecord.AddCheckpoint(s_AutomaticCheckpoint); checkpointRecord.SetDataValue(checkpointId, AutomaticCheckpointData::ClientVersion, {}, { "1.2.3.4" }); } @@ -75,22 +78,21 @@ TEST_CASE("ResumeFlow_InvalidClientVersion", "[Resume]") REQUIRE(resumeOutput.str().find(Resource::LocString(expectedMessage).get()) != std::string::npos); } -TEST_CASE("ResumeFlow_EmptyIndex", "Resume") +TEST_CASE("ResumeFlow_EmptyIndex", "[Resume]") { - TestCommon::TempDirectory tempCheckpointIndexDirectory("TempCheckpointIndexDirectory", false); + TestCommon::TempDirectory tempCheckpointIndexDirectory("TempCheckpointIndexDirectory", true); const auto& tempCheckpointIndexDirectoryPath = tempCheckpointIndexDirectory.GetPath(); TestHook_SetPathOverride(PathName::CheckpointsLocation, tempCheckpointIndexDirectoryPath); - // Create temp guid and populate with invalid client version. std::string tempGuidString = "{43ca664c-3eae-4f73-99ee-18cf83912c02}"; - std::string tempFileName = tempGuidString + ".db"; - auto tempIndexPath = tempCheckpointIndexDirectoryPath / tempFileName; + auto tempIndexPath = tempCheckpointIndexDirectoryPath / tempGuidString / s_CheckpointsFileName; INFO("Using temporary file named: " << tempIndexPath); { - CheckpointRecord checkpointRecord = CheckpointRecord::CreateNew(tempGuidString); + std::filesystem::create_directories(tempIndexPath.parent_path()); + CheckpointRecord checkpointRecord = CheckpointRecord::CreateNew(tempIndexPath.u8string()); } std::ostringstream resumeOutput; diff --git a/src/AppInstallerCommonCore/Runtime.cpp b/src/AppInstallerCommonCore/Runtime.cpp index 3a2fbe1733..54f7c92f27 100644 --- a/src/AppInstallerCommonCore/Runtime.cpp +++ b/src/AppInstallerCommonCore/Runtime.cpp @@ -391,6 +391,7 @@ namespace AppInstaller::Runtime case PathName::CheckpointsLocation: result.Path = GetPathDetailsFor(PathName::LocalState).Path; result.Path /= s_CheckpointsDirectory; + mayBeInProfilePath = true; break; default: THROW_HR(E_UNEXPECTED); @@ -466,6 +467,7 @@ namespace AppInstaller::Runtime case PathName::PortableLinksUserLocation: case PathName::PortablePackageUserRoot: case PathName::UserProfileDownloads: + case PathName::CheckpointsLocation: result = GetPathDetailsCommon(path, forDisplay); break; case PathName::SelfPackageRoot: From 5107a85ec6e4a7cd58d490e05023fcaec5eb03ec Mon Sep 17 00:00:00 2001 From: Ryan Fu Date: Tue, 12 Sep 2023 11:03:05 -0700 Subject: [PATCH 33/43] fix path for checkpoints directory --- .../Commands/ResumeCommand.cpp | 1 + src/AppInstallerCLIE2ETests/ResumeCommand.cs | 8 +++----- src/AppInstallerCommonCore/Runtime.cpp | 17 ++++++++++------- 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/src/AppInstallerCLICore/Commands/ResumeCommand.cpp b/src/AppInstallerCLICore/Commands/ResumeCommand.cpp index f9313602f1..bdf58a8f08 100644 --- a/src/AppInstallerCLICore/Commands/ResumeCommand.cpp +++ b/src/AppInstallerCLICore/Commands/ResumeCommand.cpp @@ -83,6 +83,7 @@ namespace AppInstaller::CLI for (const auto& fieldName : automaticCheckpoint.GetFieldNames(AutomaticCheckpointData::Arguments)) { + // Command arguments are represented as integer strings in the checkpoint record. Execution::Args::Type type = static_cast(std::stoi(fieldName)); auto argumentType = Argument::ForType(type).Type(); if (argumentType == ArgumentType::Flag) diff --git a/src/AppInstallerCLIE2ETests/ResumeCommand.cs b/src/AppInstallerCLIE2ETests/ResumeCommand.cs index f0878081fc..cdae67a6d8 100644 --- a/src/AppInstallerCLIE2ETests/ResumeCommand.cs +++ b/src/AppInstallerCLIE2ETests/ResumeCommand.cs @@ -23,8 +23,6 @@ public class ResumeCommand : BaseCommand public void OneTimeSetup() { WinGetSettingsHelper.ConfigureFeature("resume", true); - WinGetSettingsHelper.ConfigureFeature("dependencies", true); - WinGetSettingsHelper.ConfigureFeature("windowsFeature", true); } /// @@ -62,10 +60,10 @@ public void InvalidResumeId() } /// - /// Installs a test exe installer and verifies that the checkpoint index is cleaned up. + /// Verifies that a checkpoint record persists after a failed install. /// [Test] - public void ResumeIndex() + public void ResumeRecordPreserved() { var checkpointsDir = TestCommon.GetCheckpointsDirectory(); @@ -78,7 +76,7 @@ public void ResumeIndex() int actualCheckpointsCount = Directory.GetFiles(checkpointsDir).Length; - // One new index file should be created after running the install command. + // One new checkpoint record should be created after running the install command. Assert.AreEqual(initialCheckpointsCount + 1, actualCheckpointsCount); var checkpointsDirectoryInfo = new DirectoryInfo(checkpointsDir); diff --git a/src/AppInstallerCommonCore/Runtime.cpp b/src/AppInstallerCommonCore/Runtime.cpp index 54f7c92f27..1d575f59e7 100644 --- a/src/AppInstallerCommonCore/Runtime.cpp +++ b/src/AppInstallerCommonCore/Runtime.cpp @@ -388,11 +388,6 @@ namespace AppInstaller::Runtime result.Path = GetKnownFolderPath(FOLDERID_Downloads); mayBeInProfilePath = true; break; - case PathName::CheckpointsLocation: - result.Path = GetPathDetailsFor(PathName::LocalState).Path; - result.Path /= s_CheckpointsDirectory; - mayBeInProfilePath = true; - break; default: THROW_HR(E_UNEXPECTED); } @@ -423,10 +418,15 @@ namespace AppInstaller::Runtime result.ACL[ACEPrincipal::System] = ACEPermissions::All; result.ACL[ACEPrincipal::Admins] = ACEPermissions::All; break; + case PathName::CheckpointsLocation: case PathName::LocalState: case PathName::UserFileSettings: result.Path.assign(appStorage.LocalFolder().Path().c_str()); mayBeInProfilePath = true; + if (path == PathName::CheckpointsLocation) + { + result.Path /= s_CheckpointsDirectory; + } break; case PathName::DefaultLogLocation: // To enable UIF collection through Feedback hub, we must put our logs here. @@ -467,7 +467,6 @@ namespace AppInstaller::Runtime case PathName::PortableLinksUserLocation: case PathName::PortablePackageUserRoot: case PathName::UserProfileDownloads: - case PathName::CheckpointsLocation: result = GetPathDetailsCommon(path, forDisplay); break; case PathName::SelfPackageRoot: @@ -509,12 +508,17 @@ namespace AppInstaller::Runtime } } break; + case PathName::CheckpointsLocation: case PathName::LocalState: result.Path = GetPathToAppDataDir(s_AppDataDir_State, forDisplay); result.Path /= GetRuntimePathStateName(); result.SetOwner(ACEPrincipal::CurrentUser); result.ACL[ACEPrincipal::System] = ACEPermissions::All; result.ACL[ACEPrincipal::Admins] = ACEPermissions::All; + if (path == PathName::CheckpointsLocation) + { + result.Path /= s_CheckpointsDirectory; + } break; case PathName::StandardSettings: case PathName::UserFileSettings: @@ -554,7 +558,6 @@ namespace AppInstaller::Runtime case PathName::PortableLinksUserLocation: case PathName::PortablePackageUserRoot: case PathName::UserProfileDownloads: - case PathName::CheckpointsLocation: result = GetPathDetailsCommon(path, forDisplay); break; case PathName::SelfPackageRoot: From 16ac252b769144c70cd2e0c589d450c94f22181f Mon Sep 17 00:00:00 2001 From: Ryan Fu Date: Tue, 12 Sep 2023 11:19:31 -0700 Subject: [PATCH 34/43] remove all references to index --- src/AppInstallerCLICore/CheckpointManager.cpp | 6 +-- src/AppInstallerCLITests/ResumeFlow.cpp | 48 +++++++++---------- 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/src/AppInstallerCLICore/CheckpointManager.cpp b/src/AppInstallerCLICore/CheckpointManager.cpp index 17a5014f79..c7c23050a6 100644 --- a/src/AppInstallerCLICore/CheckpointManager.cpp +++ b/src/AppInstallerCLICore/CheckpointManager.cpp @@ -24,15 +24,15 @@ namespace AppInstaller::Checkpoints if (!std::filesystem::exists(checkpointsDirectory)) { std::filesystem::create_directories(checkpointsDirectory); - AICLI_LOG(Repo, Info, << "Creating checkpoint index directory: " << checkpointsDirectory); + AICLI_LOG(Repo, Info, << "Creating checkpoint record directory: " << checkpointsDirectory); } else { THROW_HR_IF(ERROR_CANNOT_MAKE, !std::filesystem::is_directory(checkpointsDirectory)); } - auto indexPath = checkpointsDirectory / s_CheckpointsFileName; - return indexPath; + auto recordPath = checkpointsDirectory / s_CheckpointsFileName; + return recordPath; } CheckpointManager::CheckpointManager() diff --git a/src/AppInstallerCLITests/ResumeFlow.cpp b/src/AppInstallerCLITests/ResumeFlow.cpp index 50ebeca934..8ebec7ec10 100644 --- a/src/AppInstallerCLITests/ResumeFlow.cpp +++ b/src/AppInstallerCLITests/ResumeFlow.cpp @@ -43,22 +43,22 @@ TEST_CASE("ResumeFlow_IndexNotFound", "[Resume]") TEST_CASE("ResumeFlow_InvalidClientVersion", "[Resume]") { - TestCommon::TempDirectory tempCheckpointIndexDirectory("TempCheckpointIndexDirectory", true); + TestCommon::TempDirectory tempCheckpointRecordDirectory("TempCheckpointRecordDirectory", true); - const auto& tempCheckpointIndexDirectoryPath = tempCheckpointIndexDirectory.GetPath(); - TestHook_SetPathOverride(PathName::CheckpointsLocation, tempCheckpointIndexDirectoryPath); + const auto& tempCheckpointRecordDirectoryPath = tempCheckpointRecordDirectory.GetPath(); + TestHook_SetPathOverride(PathName::CheckpointsLocation, tempCheckpointRecordDirectoryPath); // Create temp guid and populate with invalid client version. std::string tempGuidString = "{615339e9-3ac5-4e86-a5ab-c246657aca25}"; - auto tempIndexPath = tempCheckpointIndexDirectoryPath / tempGuidString / s_CheckpointsFileName; + auto tempRecordPath = tempCheckpointRecordDirectoryPath / tempGuidString / s_CheckpointsFileName; std::string_view invalidClientVersion = "1.2.3.4"sv; - INFO("Using temporary file named: " << tempIndexPath); + INFO("Using temporary file named: " << tempRecordPath); { // Manually set invalid client version - std::filesystem::create_directories(tempIndexPath.parent_path()); - CheckpointRecord checkpointRecord = CheckpointRecord::CreateNew(tempIndexPath.u8string()); + std::filesystem::create_directories(tempRecordPath.parent_path()); + CheckpointRecord checkpointRecord = CheckpointRecord::CreateNew(tempRecordPath.u8string()); CheckpointRecord::IdType checkpointId = checkpointRecord.AddCheckpoint(s_AutomaticCheckpoint); checkpointRecord.SetDataValue(checkpointId, AutomaticCheckpointData::ClientVersion, {}, { "1.2.3.4" }); } @@ -80,19 +80,19 @@ TEST_CASE("ResumeFlow_InvalidClientVersion", "[Resume]") TEST_CASE("ResumeFlow_EmptyIndex", "[Resume]") { - TestCommon::TempDirectory tempCheckpointIndexDirectory("TempCheckpointIndexDirectory", true); + TestCommon::TempDirectory tempCheckpointRecordDirectory("TempCheckpointRecordDirectory", true); - const auto& tempCheckpointIndexDirectoryPath = tempCheckpointIndexDirectory.GetPath(); - TestHook_SetPathOverride(PathName::CheckpointsLocation, tempCheckpointIndexDirectoryPath); + const auto& tempCheckpointRecordDirectoryPath = tempCheckpointRecordDirectory.GetPath(); + TestHook_SetPathOverride(PathName::CheckpointsLocation, tempCheckpointRecordDirectoryPath); std::string tempGuidString = "{43ca664c-3eae-4f73-99ee-18cf83912c02}"; - auto tempIndexPath = tempCheckpointIndexDirectoryPath / tempGuidString / s_CheckpointsFileName; + auto tempRecordPath = tempCheckpointRecordDirectoryPath / tempGuidString / s_CheckpointsFileName; - INFO("Using temporary file named: " << tempIndexPath); + INFO("Using temporary file named: " << tempRecordPath); { - std::filesystem::create_directories(tempIndexPath.parent_path()); - CheckpointRecord checkpointRecord = CheckpointRecord::CreateNew(tempIndexPath.u8string()); + std::filesystem::create_directories(tempRecordPath.parent_path()); + CheckpointRecord checkpointRecord = CheckpointRecord::CreateNew(tempRecordPath.u8string()); } std::ostringstream resumeOutput; @@ -111,10 +111,10 @@ TEST_CASE("ResumeFlow_EmptyIndex", "[Resume]") TEST_CASE("ResumeFlow_InstallSuccess", "[Resume]") { - TestCommon::TempDirectory tempCheckpointIndexDirectory("TempCheckpointIndexDirectory", false); + TestCommon::TempDirectory tempCheckpointRecordDirectory("TempCheckpointRecordDirectory", false); - const auto& tempCheckpointIndexDirectoryPath = tempCheckpointIndexDirectory.GetPath(); - TestHook_SetPathOverride(PathName::CheckpointsLocation, tempCheckpointIndexDirectoryPath); + const auto& tempCheckpointRecordDirectoryPath = tempCheckpointRecordDirectory.GetPath(); + TestHook_SetPathOverride(PathName::CheckpointsLocation, tempCheckpointRecordDirectoryPath); TestCommon::TestUserSettings testSettings; testSettings.Set(true); @@ -147,7 +147,7 @@ TEST_CASE("ResumeFlow_InstallSuccess", "[Resume]") // The checkpoint index should not exist if the context succeeded. std::vector checkpointFiles; - for (const auto& entry : std::filesystem::directory_iterator(tempCheckpointIndexDirectoryPath)) + for (const auto& entry : std::filesystem::directory_iterator(tempCheckpointRecordDirectoryPath)) { checkpointFiles.emplace_back(entry.path()); } @@ -158,10 +158,10 @@ TEST_CASE("ResumeFlow_InstallSuccess", "[Resume]") // TODO: This test will need to be updated once saving the resume state is restricted to certain HRs. TEST_CASE("ResumeFlow_InstallFailure", "[Resume]") { - TestCommon::TempDirectory tempCheckpointIndexDirectory("TempCheckpointIndexDirectory", false); + TestCommon::TempDirectory tempCheckpointRecordDirectory("TempCheckpointRecordDirectory", false); - const auto& tempCheckpointIndexDirectoryPath = tempCheckpointIndexDirectory.GetPath(); - TestHook_SetPathOverride(PathName::CheckpointsLocation, tempCheckpointIndexDirectoryPath); + const auto& tempCheckpointRecordDirectoryPath = tempCheckpointRecordDirectory.GetPath(); + TestHook_SetPathOverride(PathName::CheckpointsLocation, tempCheckpointRecordDirectoryPath); TestCommon::TestUserSettings testSettings; testSettings.Set(true); @@ -186,13 +186,13 @@ TEST_CASE("ResumeFlow_InstallFailure", "[Resume]") // Only one checkpoint file should be created. std::vector checkpointFiles; - for (const auto& entry : std::filesystem::directory_iterator(tempCheckpointIndexDirectoryPath)) + for (const auto& entry : std::filesystem::directory_iterator(tempCheckpointRecordDirectoryPath)) { checkpointFiles.emplace_back(entry.path()); } REQUIRE(checkpointFiles.size() == 1); - std::filesystem::path checkpointIndexPath = checkpointFiles[0]; - REQUIRE(std::filesystem::exists(checkpointIndexPath)); + std::filesystem::path checkpointRecordPath = checkpointFiles[0]; + REQUIRE(std::filesystem::exists(checkpointRecordPath)); } From 1022bc29f5f8d501cf942c2a2fa720304728bd30 Mon Sep 17 00:00:00 2001 From: Ryan Fu Date: Wed, 13 Sep 2023 08:47:35 -0700 Subject: [PATCH 35/43] actually fix e2e tests --- src/AppInstallerCLIE2ETests/ResumeCommand.cs | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/src/AppInstallerCLIE2ETests/ResumeCommand.cs b/src/AppInstallerCLIE2ETests/ResumeCommand.cs index cdae67a6d8..b633210ffc 100644 --- a/src/AppInstallerCLIE2ETests/ResumeCommand.cs +++ b/src/AppInstallerCLIE2ETests/ResumeCommand.cs @@ -25,6 +25,15 @@ public void OneTimeSetup() WinGetSettingsHelper.ConfigureFeature("resume", true); } + /// + /// One time teardown. + /// + [OneTimeTearDown] + public void OneTimeTearDown() + { + WinGetSettingsHelper.ConfigureFeature("resume", false); + } + /// /// Installs a test exe installer and verifies that the checkpoint index is cleaned up. /// @@ -67,28 +76,25 @@ public void ResumeRecordPreserved() { var checkpointsDir = TestCommon.GetCheckpointsDirectory(); - // If the checkpoints directory does not yet exist, set to 0. The directory should be created when the command is invoked. - int initialCheckpointsCount = Directory.Exists(checkpointsDir) ? Directory.GetFiles(checkpointsDir).Length : 0; + int initialCheckpointsCount = Directory.Exists(checkpointsDir) ? Directory.GetDirectories(checkpointsDir).Length : 0; var installResult = TestCommon.RunAICLICommand("install", "--id AppInstallerTest.WindowsFeature"); Assert.AreEqual(Constants.ErrorCode.ERROR_INSTALL_MISSING_DEPENDENCY, installResult.ExitCode); Assert.True(installResult.StdOut.Contains("The feature [invalidFeature] was not found.")); - int actualCheckpointsCount = Directory.GetFiles(checkpointsDir).Length; + int actualCheckpointsCount = Directory.GetDirectories(checkpointsDir).Length; // One new checkpoint record should be created after running the install command. Assert.AreEqual(initialCheckpointsCount + 1, actualCheckpointsCount); var checkpointsDirectoryInfo = new DirectoryInfo(checkpointsDir); - var indexFile = checkpointsDirectoryInfo.GetFiles() + var checkpoint = checkpointsDirectoryInfo.GetDirectories() .OrderByDescending(f => f.LastWriteTime) .First(); - var indexFileName = Path.GetFileNameWithoutExtension(indexFile.Name); - // Resume output should be the same as the install result. - var resumeResult = TestCommon.RunAICLICommand("resume", $"-g {indexFileName}"); + var resumeResult = TestCommon.RunAICLICommand("resume", $"-g {checkpoint.Name}"); Assert.AreEqual(Constants.ErrorCode.ERROR_INSTALL_MISSING_DEPENDENCY, resumeResult.ExitCode); Assert.True(resumeResult.StdOut.Contains("The feature [invalidFeature] was not found.")); } From e04f6bbdb7a2d50633e53c187693ce4ea9dfb031 Mon Sep 17 00:00:00 2001 From: Ryan Fu Date: Mon, 18 Sep 2023 21:23:35 -0700 Subject: [PATCH 36/43] rename checkpointRecord to checkpointDatabase --- .github/actions/spelling/allow.txt | 4 - .../AppInstallerCLICore.vcxproj | 1 - .../AppInstallerCLICore.vcxproj.filters | 3 - src/AppInstallerCLICore/CheckpointManager.cpp | 117 +++++++++++------- src/AppInstallerCLICore/CheckpointManager.h | 23 ++-- .../Commands/ResumeCommand.cpp | 7 +- src/AppInstallerCLICore/ExecutionContext.cpp | 31 +---- src/AppInstallerCLICore/ExecutionContext.h | 1 + src/AppInstallerCLITests/ResumeFlow.cpp | 6 +- .../AppInstallerRepositoryCore.vcxproj | 11 +- ...AppInstallerRepositoryCore.vcxproj.filters | 13 +- ...pointRecord.cpp => CheckpointDatabase.cpp} | 75 ++++++----- .../Microsoft/CheckpointDatabase.h | 71 +++++++++++ .../Microsoft/CheckpointRecord.h | 76 ------------ ...erface.h => CheckpointDatabaseInterface.h} | 11 +- .../CheckpointDatabaseInterface_1_0.cpp | 77 ++++++++++++ .../CheckpointRecordInterface_1_0.cpp | 99 --------------- .../Schema/Checkpoint_1_0/CheckpointTable.cpp | 33 ++--- .../Schema/Checkpoint_1_0/CheckpointTable.h | 7 +- ...eckpointRecord.h => ICheckpointDatabase.h} | 19 +-- .../Public/winget}/Checkpoint.h | 37 ++---- 21 files changed, 323 insertions(+), 399 deletions(-) rename src/AppInstallerRepositoryCore/Microsoft/{CheckpointRecord.cpp => CheckpointDatabase.cpp} (50%) create mode 100644 src/AppInstallerRepositoryCore/Microsoft/CheckpointDatabase.h delete mode 100644 src/AppInstallerRepositoryCore/Microsoft/CheckpointRecord.h rename src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/{CheckpointRecordInterface.h => CheckpointDatabaseInterface.h} (54%) create mode 100644 src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointDatabaseInterface_1_0.cpp delete mode 100644 src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointRecordInterface_1_0.cpp rename src/AppInstallerRepositoryCore/Microsoft/Schema/{ICheckpointRecord.h => ICheckpointDatabase.h} (60%) rename src/{AppInstallerCLICore => AppInstallerRepositoryCore/Public/winget}/Checkpoint.h (51%) diff --git a/.github/actions/spelling/allow.txt b/.github/actions/spelling/allow.txt index fa3130ccb0..1eb064b53d 100644 --- a/.github/actions/spelling/allow.txt +++ b/.github/actions/spelling/allow.txt @@ -3,8 +3,6 @@ ACCESSDENIED ACTIONDATA ACTIONSTART activatable -addcheckpoint -addcheckpointdata addfile addmanifest addpin @@ -61,8 +59,6 @@ cdecl cdn cer certutil -cguid -checkpointrecord chrono cin cla diff --git a/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj b/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj index c050507350..83f0410c2c 100644 --- a/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj +++ b/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj @@ -350,7 +350,6 @@ - diff --git a/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj.filters b/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj.filters index 285bdcf24f..e7231db654 100644 --- a/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj.filters +++ b/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj.filters @@ -239,9 +239,6 @@ Header Files - - Header Files - diff --git a/src/AppInstallerCLICore/CheckpointManager.cpp b/src/AppInstallerCLICore/CheckpointManager.cpp index c7c23050a6..58f226f181 100644 --- a/src/AppInstallerCLICore/CheckpointManager.cpp +++ b/src/AppInstallerCLICore/CheckpointManager.cpp @@ -2,33 +2,38 @@ // Licensed under the MIT License. #include "pch.h" #include "CheckpointManager.h" -#include +#include "Command.h" #include "ExecutionContextData.h" +#include namespace AppInstaller::Checkpoints { using namespace AppInstaller::Repository::Microsoft; using namespace AppInstaller::Repository::SQLite; + using namespace AppInstaller::CLI; // This checkpoint name is reserved for the starting checkpoint which captures the automatic metadata. constexpr std::string_view s_AutomaticCheckpoint = "automatic"sv; constexpr std::string_view s_CheckpointsFileName = "checkpoints.db"sv; - std::filesystem::path CheckpointManager::GetCheckpointRecordPath(GUID guid) + std::filesystem::path CheckpointManager::GetCheckpointDatabasePath(GUID guid, bool createCheckpointDirectory) { wchar_t checkpointGuid[256]; THROW_HR_IF(E_UNEXPECTED, !StringFromGUID2(guid, checkpointGuid, ARRAYSIZE(checkpointGuid))); const auto checkpointsDirectory = Runtime::GetPathTo(Runtime::PathName::CheckpointsLocation) / checkpointGuid; - if (!std::filesystem::exists(checkpointsDirectory)) - { - std::filesystem::create_directories(checkpointsDirectory); - AICLI_LOG(Repo, Info, << "Creating checkpoint record directory: " << checkpointsDirectory); - } - else + if (createCheckpointDirectory) { - THROW_HR_IF(ERROR_CANNOT_MAKE, !std::filesystem::is_directory(checkpointsDirectory)); + if (!std::filesystem::exists(checkpointsDirectory)) + { + AICLI_LOG(Repo, Info, << "Creating checkpoint database directory: " << checkpointsDirectory); + std::filesystem::create_directories(checkpointsDirectory); + } + else + { + THROW_HR_IF(ERROR_CANNOT_MAKE, !std::filesystem::is_directory(checkpointsDirectory)); + } } auto recordPath = checkpointsDirectory / s_CheckpointsFileName; @@ -38,84 +43,106 @@ namespace AppInstaller::Checkpoints CheckpointManager::CheckpointManager() { std::ignore = CoCreateGuid(&m_resumeId); - const auto& checkpointRecordPath = GetCheckpointRecordPath(m_resumeId); - m_checkpointRecord = std::make_shared(CheckpointRecord::CreateNew(checkpointRecordPath.u8string())); + const auto& checkpointDatabasePath = GetCheckpointDatabasePath(m_resumeId); + m_checkpointDatabase = CheckpointDatabase::CreateNew(checkpointDatabasePath.u8string()); } CheckpointManager::CheckpointManager(GUID resumeId) { m_resumeId = resumeId; - const auto& checkpointRecordPath = GetCheckpointRecordPath(m_resumeId); - m_checkpointRecord = std::make_shared(CheckpointRecord::Open(checkpointRecordPath.u8string())); + const auto& checkpointDatabasePath = GetCheckpointDatabasePath(m_resumeId); + m_checkpointDatabase = CheckpointDatabase::Open(checkpointDatabasePath.u8string()); } - Checkpoint CheckpointManager::CreateAutomaticCheckpoint() + void CheckpointManager::CreateAutomaticCheckpoint(CLI::Execution::Context& context) { - CheckpointRecord::IdType startCheckpointId = m_checkpointRecord->AddCheckpoint(s_AutomaticCheckpoint); - Checkpoint checkpoint{ m_checkpointRecord, startCheckpointId }; - return checkpoint; - } + CheckpointDatabase::IdType startCheckpointId = m_checkpointDatabase->AddCheckpoint(s_AutomaticCheckpoint); + Checkpoint automaticCheckpoint{ m_checkpointDatabase, startCheckpointId }; - std::optional> CheckpointManager::GetAutomaticCheckpoint() - { - std::optional startCheckpointId = m_checkpointRecord->GetCheckpointIdByName(s_AutomaticCheckpoint); - if (startCheckpointId.has_value()) + automaticCheckpoint.Set(AutomaticCheckpointData::ClientVersion, {}, AppInstaller::Runtime::GetClientVersion()); + + const auto& executingCommand = context.GetExecutingCommand(); + if (executingCommand != nullptr) { - return Checkpoint{ std::move(m_checkpointRecord), startCheckpointId.value() }; + automaticCheckpoint.Set(AutomaticCheckpointData::CommandName, {}, std::string{ executingCommand->FullName() }); } - else + + const auto& argTypes = context.Args.GetTypes(); + for (auto type : argTypes) { - return {}; + const auto& argument = std::to_string(static_cast(type)); + auto argumentType = Argument::ForType(type).Type(); + + if (argumentType == ArgumentType::Flag) + { + automaticCheckpoint.Set(AutomaticCheckpointData::Arguments, argument, {}); + } + else + { + const auto& values = *context.Args.GetArgs(type); + automaticCheckpoint.SetMany(AutomaticCheckpointData::Arguments, argument, values); + } + } + } + + Checkpoint CheckpointManager::GetAutomaticCheckpoint() + { + const auto& checkpointIds = m_checkpointDatabase->GetCheckpointIds(); + if (checkpointIds.empty()) + { + THROW_HR(E_UNEXPECTED); } + + const auto& automaticCheckpointId = checkpointIds.back(); + return Checkpoint{ std::move(m_checkpointDatabase), automaticCheckpointId }; } Checkpoint CheckpointManager::CreateCheckpoint(std::string_view checkpointName) { - CheckpointRecord::IdType startCheckpointId = m_checkpointRecord->AddCheckpoint(checkpointName); - Checkpoint checkpoint{ m_checkpointRecord, startCheckpointId }; + CheckpointDatabase::IdType startCheckpointId = m_checkpointDatabase->AddCheckpoint(checkpointName); + Checkpoint checkpoint{ m_checkpointDatabase, startCheckpointId }; return checkpoint; } std::vector> CheckpointManager::GetCheckpoints() { + auto checkpointIds = m_checkpointDatabase->GetCheckpointIds(); + + // Remove the last checkpoint (automatic) + checkpointIds.pop_back(); + std::vector> checkpoints; - for (const auto& checkpointName : m_checkpointRecord->GetCheckpoints()) + for (const auto& checkpointId : checkpointIds) { - if (checkpointName == s_AutomaticCheckpoint) - { - continue; - } - - CheckpointRecord::IdType checkpointId = m_checkpointRecord->GetCheckpointIdByName(checkpointName).value(); - checkpoints.emplace_back(Checkpoint{ std::move(m_checkpointRecord), checkpointId }); + checkpoints.emplace_back(Checkpoint{ std::move(m_checkpointDatabase), checkpointId }); } return checkpoints; } - void CheckpointManager::CleanUpRecord() + void CheckpointManager::CleanUpDatabase() { - if (m_checkpointRecord) + if (m_checkpointDatabase) { - m_checkpointRecord.reset(); + m_checkpointDatabase.reset(); } if (m_resumeId != GUID_NULL) { - const auto& checkpointRecordPath = GetCheckpointRecordPath(m_resumeId); + const auto& checkpointDatabasePath = GetCheckpointDatabasePath(m_resumeId); - if (std::filesystem::exists(checkpointRecordPath)) + if (std::filesystem::exists(checkpointDatabasePath)) { std::error_code error; - if (std::filesystem::remove(checkpointRecordPath, error)) + if (std::filesystem::remove(checkpointDatabasePath, error)) { - AICLI_LOG(CLI, Info, << "Checkpoint record deleted: " << checkpointRecordPath); + AICLI_LOG(CLI, Info, << "Checkpoint database deleted: " << checkpointDatabasePath); } - const auto& checkpointRecordParentDirectory = checkpointRecordPath.parent_path(); - if (std::filesystem::is_empty(checkpointRecordParentDirectory) && std::filesystem::remove(checkpointRecordParentDirectory, error)) + const auto& checkpointDatabaseParentDirectory = checkpointDatabasePath.parent_path(); + if (std::filesystem::remove(checkpointDatabaseParentDirectory, error)) { - AICLI_LOG(CLI, Info, << "Checkpoint record directory deleted: " << checkpointRecordParentDirectory); + AICLI_LOG(CLI, Info, << "Checkpoint database directory deleted: " << checkpointDatabaseParentDirectory); } } } diff --git a/src/AppInstallerCLICore/CheckpointManager.h b/src/AppInstallerCLICore/CheckpointManager.h index 4fd1a12935..39277d6206 100644 --- a/src/AppInstallerCLICore/CheckpointManager.h +++ b/src/AppInstallerCLICore/CheckpointManager.h @@ -2,12 +2,13 @@ // Licensed under the MIT License. #pragma once #include "ExecutionContextData.h" -#include "Checkpoint.h" +#include "ExecutionContext.h" +#include "Public/winget/Checkpoint.h" #include namespace AppInstaller::Repository::Microsoft { - struct CheckpointRecord; + struct CheckpointDatabase; } namespace AppInstaller::Checkpoints @@ -15,22 +16,22 @@ namespace AppInstaller::Checkpoints // Owns the lifetime of a checkpoint data base and creates the checkpoints. struct CheckpointManager { - // Constructor that generates a new resume id and creates the checkpoint record. + // Constructor that generates a new resume id and creates the checkpoint database. CheckpointManager(); - // Constructor that loads the resume id and opens an existing checkpoint record. + // Constructor that loads the resume id and opens an existing checkpoint database. CheckpointManager(GUID resumeId); ~CheckpointManager() = default; - // Gets the file path of the checkpoint record. - static std::filesystem::path GetCheckpointRecordPath(GUID guid); + // Gets the file path of the checkpoint database. + static std::filesystem::path GetCheckpointDatabasePath(GUID guid, bool createCheckpointDirectory = false); // Gets the automatic checkpoint. - std::optional> GetAutomaticCheckpoint(); + Checkpoint GetAutomaticCheckpoint(); // Creates a new automatic checkpoint. - Checkpoint CreateAutomaticCheckpoint(); + void CreateAutomaticCheckpoint(CLI::Execution::Context& context); // Gets all data checkpoints. std::vector> GetCheckpoints(); @@ -38,11 +39,11 @@ namespace AppInstaller::Checkpoints // Creates a new data checkpoint. Checkpoint CreateCheckpoint(std::string_view checkpointName); - // Cleans up the checkpoint record. - void CleanUpRecord(); + // Cleans up the checkpoint database. + void CleanUpDatabase(); private: GUID m_resumeId = {}; - std::shared_ptr m_checkpointRecord; + std::shared_ptr m_checkpointDatabase; }; } \ No newline at end of file diff --git a/src/AppInstallerCLICore/Commands/ResumeCommand.cpp b/src/AppInstallerCLICore/Commands/ResumeCommand.cpp index bdf58a8f08..d80baa46c9 100644 --- a/src/AppInstallerCLICore/Commands/ResumeCommand.cpp +++ b/src/AppInstallerCLICore/Commands/ResumeCommand.cpp @@ -7,7 +7,6 @@ #include "RootCommand.h" #include "CheckpointManager.h" #include "Workflows/ResumeFlow.h" -#include "Checkpoint.h" using namespace AppInstaller::Checkpoints; @@ -43,7 +42,7 @@ namespace AppInstaller::CLI std::string resumeGuidString{ context.Args.GetArg(Execution::Args::Type::ResumeId) }; GUID checkpointId = Utility::ConvertToGuid(resumeGuidString); - if (!std::filesystem::exists(Checkpoints::CheckpointManager::GetCheckpointRecordPath(checkpointId))) + if (!std::filesystem::exists(Checkpoints::CheckpointManager::GetCheckpointDatabasePath(checkpointId))) { context.Reporter.Error() << Resource::String::ResumeIdNotFoundError(Utility::LocIndView{ resumeGuidString }) << std::endl; AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_RESUME_ID_NOT_FOUND); @@ -59,14 +58,14 @@ namespace AppInstaller::CLI Checkpoint automaticCheckpoint = foundAutomaticCheckpoint.value(); - const auto& checkpointClientVersion = automaticCheckpoint.Get(AutomaticCheckpointData::ClientVersion); + const auto& checkpointClientVersion = automaticCheckpoint.Get(AutomaticCheckpointData::ClientVersion, {}); if (checkpointClientVersion != AppInstaller::Runtime::GetClientVersion().get()) { context.Reporter.Error() << Resource::String::ClientVersionMismatchError(Utility::LocIndView{ checkpointClientVersion }) << std::endl; AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_CLIENT_VERSION_MISMATCH); } - const auto& checkpointCommandName = automaticCheckpoint.Get(AutomaticCheckpointData::CommandName); + const auto& checkpointCommandName = automaticCheckpoint.Get(AutomaticCheckpointData::CommandName, {}); std::unique_ptr commandToResume; AICLI_LOG(CLI, Info, << "Resuming command: " << checkpointCommandName); diff --git a/src/AppInstallerCLICore/ExecutionContext.cpp b/src/AppInstallerCLICore/ExecutionContext.cpp index e3e7118b95..b9adb8fcee 100644 --- a/src/AppInstallerCLICore/ExecutionContext.cpp +++ b/src/AppInstallerCLICore/ExecutionContext.cpp @@ -8,7 +8,7 @@ #include "AppInstallerRuntime.h" #include "Command.h" #include "CheckpointManager.h" -#include "Checkpoint.h" +#include "Public/winget/Checkpoint.h" using namespace AppInstaller::Checkpoints; @@ -260,7 +260,7 @@ namespace AppInstaller::CLI::Execution { if (m_checkpointManager) { - m_checkpointManager->CleanUpRecord(); + m_checkpointManager->CleanUpDatabase(); } } @@ -446,32 +446,7 @@ namespace AppInstaller::CLI::Execution if (!m_checkpointManager) { m_checkpointManager = std::make_unique(); - Checkpoints::Checkpoint automaticCheckpoint = m_checkpointManager->CreateAutomaticCheckpoint(); - - automaticCheckpoint.Set(AutomaticCheckpointData::ClientVersion, AppInstaller::Runtime::GetClientVersion()); - - const auto& executingCommand = m_executingCommand; - if (executingCommand != nullptr) - { - automaticCheckpoint.Set(AutomaticCheckpointData::CommandName, std::string{m_executingCommand->Name()}); - } - - const auto& argTypes = Args.GetTypes(); - for (auto type : argTypes) - { - const auto& argument = std::to_string(static_cast(type)); - auto argumentType = Argument::ForType(type).Type(); - - if (argumentType == ArgumentType::Flag) - { - automaticCheckpoint.Set(AutomaticCheckpointData::Arguments, argument, {}); - } - else - { - const auto& values = *Args.GetArgs(type); - automaticCheckpoint.SetMany(AutomaticCheckpointData::Arguments, argument, values); - } - } + m_checkpointManager->CreateAutomaticCheckpoint(*this); } // TODO: Capture context data for checkpoint. diff --git a/src/AppInstallerCLICore/ExecutionContext.h b/src/AppInstallerCLICore/ExecutionContext.h index dd90e2c58f..31b32ce757 100644 --- a/src/AppInstallerCLICore/ExecutionContext.h +++ b/src/AppInstallerCLICore/ExecutionContext.h @@ -7,6 +7,7 @@ #include "ExecutionContextData.h" #include "CompletionData.h" #include "CheckpointManager.h" +#include "Public/winget/Checkpoint.h" #include diff --git a/src/AppInstallerCLITests/ResumeFlow.cpp b/src/AppInstallerCLITests/ResumeFlow.cpp index 8ebec7ec10..2ed647396d 100644 --- a/src/AppInstallerCLITests/ResumeFlow.cpp +++ b/src/AppInstallerCLITests/ResumeFlow.cpp @@ -58,8 +58,8 @@ TEST_CASE("ResumeFlow_InvalidClientVersion", "[Resume]") { // Manually set invalid client version std::filesystem::create_directories(tempRecordPath.parent_path()); - CheckpointRecord checkpointRecord = CheckpointRecord::CreateNew(tempRecordPath.u8string()); - CheckpointRecord::IdType checkpointId = checkpointRecord.AddCheckpoint(s_AutomaticCheckpoint); + CheckpointDatabase checkpointRecord = CheckpointDatabase::CreateNew(tempRecordPath.u8string()); + CheckpointDatabase::IdType checkpointId = checkpointRecord.AddCheckpoint(s_AutomaticCheckpoint); checkpointRecord.SetDataValue(checkpointId, AutomaticCheckpointData::ClientVersion, {}, { "1.2.3.4" }); } @@ -92,7 +92,7 @@ TEST_CASE("ResumeFlow_EmptyIndex", "[Resume]") { std::filesystem::create_directories(tempRecordPath.parent_path()); - CheckpointRecord checkpointRecord = CheckpointRecord::CreateNew(tempRecordPath.u8string()); + CheckpointDatabase checkpointRecord = CheckpointDatabase::CreateNew(tempRecordPath.u8string()); } std::ostringstream resumeOutput; diff --git a/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj b/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj index 7ce4454b0e..5dcd861930 100644 --- a/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj +++ b/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj @@ -353,7 +353,7 @@ - + @@ -388,7 +388,7 @@ - + @@ -396,7 +396,7 @@ - + @@ -407,6 +407,7 @@ + @@ -476,7 +477,7 @@ - + @@ -500,7 +501,7 @@ - + diff --git a/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj.filters b/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj.filters index 8e985bd21d..e8ee9b65f3 100644 --- a/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj.filters +++ b/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj.filters @@ -390,21 +390,24 @@ Rest\Schema\1_6 - + Microsoft\Schema - + Microsoft Microsoft\Schema\Checkpoint_1_0 - + Microsoft\Schema\Checkpoint_1_0 Microsoft\Schema\Checkpoint_1_0 + + Public\winget + @@ -632,13 +635,13 @@ Rest\Schema\1_6 - + Source Files Microsoft\Schema\Checkpoint_1_0 - + Source Files diff --git a/src/AppInstallerRepositoryCore/Microsoft/CheckpointRecord.cpp b/src/AppInstallerRepositoryCore/Microsoft/CheckpointDatabase.cpp similarity index 50% rename from src/AppInstallerRepositoryCore/Microsoft/CheckpointRecord.cpp rename to src/AppInstallerRepositoryCore/Microsoft/CheckpointDatabase.cpp index b94fff3766..93073366d9 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/CheckpointRecord.cpp +++ b/src/AppInstallerRepositoryCore/Microsoft/CheckpointDatabase.cpp @@ -1,17 +1,17 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" -#include "CheckpointRecord.h" -#include "Schema/Checkpoint_1_0/CheckpointRecordInterface.h" +#include "CheckpointDatabase.h" +#include "Schema/Checkpoint_1_0/CheckpointDatabaseInterface.h" namespace AppInstaller::Repository::Microsoft { - CheckpointRecord CheckpointRecord::CreateNew(const std::string& filePath, Schema::Version version) + std::shared_ptr CheckpointDatabase::CreateNew(const std::string& filePath, Schema::Version version) { - AICLI_LOG(Repo, Info, << "Creating new Checkpoint Index with version [" << version << "] at '" << filePath << "'"); - CheckpointRecord result{ filePath, version }; + AICLI_LOG(Repo, Info, << "Creating new Checkpoint database with version [" << version << "] at '" << filePath << "'"); + CheckpointDatabase result{ filePath, version }; - SQLite::Savepoint savepoint = SQLite::Savepoint::Create(result.m_dbconn, "CheckpointRecord_createnew"); + SQLite::Savepoint savepoint = SQLite::Savepoint::Create(result.m_dbconn, "CheckpointDatabase_CreateNew"); // Use calculated version, as incoming version could be 'latest' result.m_version.SetSchemaVersion(result.m_dbconn); @@ -22,25 +22,20 @@ namespace AppInstaller::Repository::Microsoft savepoint.Commit(); - return result; + return std::make_shared(std::move(result)); } - bool CheckpointRecord::IsEmpty() + bool CheckpointDatabase::IsEmpty() { return m_interface->IsEmpty(m_dbconn); } - std::optional CheckpointRecord::GetCheckpointIdByName(std::string_view checkpointName) - { - return m_interface->GetCheckpointIdByName(m_dbconn, checkpointName); - } - - CheckpointRecord::IdType CheckpointRecord::AddCheckpoint(std::string_view checkpointName) + CheckpointDatabase::IdType CheckpointDatabase::AddCheckpoint(std::string_view checkpointName) { std::lock_guard lockInterface{ *m_interfaceLock }; AICLI_LOG(Repo, Verbose, << "Adding checkpoint [" << checkpointName << "]"); - SQLite::Savepoint savepoint = SQLite::Savepoint::Create(m_dbconn, "checkpointrecord_addcheckpoint"); + SQLite::Savepoint savepoint = SQLite::Savepoint::Create(m_dbconn, "CheckpointDatabase_addcheckpoint"); IdType result = m_interface->AddCheckpoint(m_dbconn, checkpointName); @@ -49,32 +44,32 @@ namespace AppInstaller::Repository::Microsoft return result; } - std::vector CheckpointRecord::GetCheckpoints() + std::vector CheckpointDatabase::GetCheckpointIds() { - return m_interface->GetAvailableCheckpoints(m_dbconn); + return m_interface->GetCheckpointIds(m_dbconn); } - bool CheckpointRecord::HasDataField(IdType checkpointId, int type, std::string name) + bool CheckpointDatabase::HasDataField(IdType checkpointId, int type, std::string name) { - return m_interface->HasCheckpointDataField(m_dbconn, checkpointId, type, name); + return m_interface->GetCheckpointDataFieldValues(m_dbconn, checkpointId, type, name).has_value(); } - std::vector CheckpointRecord::GetDataTypes(IdType checkpointId) + std::vector CheckpointDatabase::GetDataTypes(IdType checkpointId) { return m_interface->GetCheckpointDataTypes(m_dbconn, checkpointId); } - std::vector CheckpointRecord::GetDataFieldNames(IdType checkpointId, int dataType) + std::vector CheckpointDatabase::GetDataFieldNames(IdType checkpointId, int dataType) { return m_interface->GetCheckpointDataFields(m_dbconn, checkpointId, dataType); } - void CheckpointRecord::SetDataValue(IdType checkpointId, int dataType, std::string field, std::vector values) + void CheckpointDatabase::SetDataValue(IdType checkpointId, int dataType, std::string field, std::vector values) { std::lock_guard lockInterface{ *m_interfaceLock }; AICLI_LOG(Repo, Verbose, << "Setting checkpoint data [" << dataType << "]"); - SQLite::Savepoint savepoint = SQLite::Savepoint::Create(m_dbconn, "checkpointrecord_setdatavalue"); + SQLite::Savepoint savepoint = SQLite::Savepoint::Create(m_dbconn, "CheckpointDatabase_setdatavalue"); m_interface->SetCheckpointDataValues(m_dbconn, checkpointId, dataType, field, values); @@ -82,51 +77,53 @@ namespace AppInstaller::Repository::Microsoft savepoint.Commit(); } - std::string CheckpointRecord::GetDataSingleValue(IdType checkpointId, int dataType) - { - return m_interface->GetCheckpointDataValue(m_dbconn, checkpointId, dataType); - } - - std::string CheckpointRecord::GetDataFieldSingleValue(IdType checkpointId, int dataType, std::string_view field) + std::string CheckpointDatabase::GetDataFieldSingleValue(IdType checkpointId, int dataType, std::string_view field) { const auto& values = m_interface->GetCheckpointDataFieldValues(m_dbconn, checkpointId, dataType, field); - if (values.empty()) + if (!values.has_value()) { THROW_HR(E_UNEXPECTED); } - return values[0]; + return values.value()[0]; } - std::vector CheckpointRecord::GetDataFieldMultiValue(IdType checkpointId, int dataType, std::string field) + std::vector CheckpointDatabase::GetDataFieldMultiValue(IdType checkpointId, int dataType, std::string field) { - return m_interface->GetCheckpointDataFieldValues(m_dbconn, checkpointId, dataType, field); + const auto& values = m_interface->GetCheckpointDataFieldValues(m_dbconn, checkpointId, dataType, field); + + if (!values.has_value()) + { + THROW_HR(E_UNEXPECTED); + } + + return values.value(); } - std::unique_ptr CheckpointRecord::CreateICheckpointRecord() const + std::unique_ptr CheckpointDatabase::CreateICheckpointDatabase() const { if (m_version == Schema::Version{ 1, 0 } || m_version.MajorVersion == 1 || m_version.IsLatest()) { - return std::make_unique(); + return std::make_unique(); } THROW_HR(HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED)); } - CheckpointRecord::CheckpointRecord(const std::string& target, SQLiteStorageBase::OpenDisposition disposition, Utility::ManagedFile&& indexFile) : + CheckpointDatabase::CheckpointDatabase(const std::string& target, SQLiteStorageBase::OpenDisposition disposition, Utility::ManagedFile&& indexFile) : SQLiteStorageBase(target, disposition, std::move(indexFile)) { AICLI_LOG(Repo, Info, << "Opened Checkpoint Index with version [" << m_version << "], last write [" << GetLastWriteTime() << "]"); - m_interface = CreateICheckpointRecord(); + m_interface = CreateICheckpointDatabase(); THROW_HR_IF(APPINSTALLER_CLI_ERROR_CANNOT_WRITE_TO_UPLEVEL_INDEX, disposition == SQLiteStorageBase::OpenDisposition::ReadWrite && m_version != m_interface->GetVersion()); } - CheckpointRecord::CheckpointRecord(const std::string& target, Schema::Version version) : SQLiteStorageBase(target, version) + CheckpointDatabase::CheckpointDatabase(const std::string& target, Schema::Version version) : SQLiteStorageBase(target, version) { - m_interface = CreateICheckpointRecord(); + m_interface = CreateICheckpointDatabase(); m_version = m_interface->GetVersion(); } } \ No newline at end of file diff --git a/src/AppInstallerRepositoryCore/Microsoft/CheckpointDatabase.h b/src/AppInstallerRepositoryCore/Microsoft/CheckpointDatabase.h new file mode 100644 index 0000000000..d5f0b4698a --- /dev/null +++ b/src/AppInstallerRepositoryCore/Microsoft/CheckpointDatabase.h @@ -0,0 +1,71 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once +#include "SQLiteWrapper.h" +#include "Microsoft/Schema/ICheckpointDatabase.h" +#include "Microsoft/SQLiteStorageBase.h" +#include + +namespace AppInstaller::Repository::Microsoft +{ + struct CheckpointDatabase : SQLiteStorageBase + { + // An id that refers to a specific Checkpoint. + using IdType = SQLite::rowid_t; + + CheckpointDatabase(const CheckpointDatabase&) = delete; + CheckpointDatabase& operator=(const CheckpointDatabase&) = delete; + + CheckpointDatabase(CheckpointDatabase&&) = default; + CheckpointDatabase& operator=(CheckpointDatabase&&) = default; + + // Create a new checkpoint database. + static std::shared_ptr CreateNew(const std::string& filePath, Schema::Version version = Schema::Version::Latest()); + + // Opens an existing checkpoint database. + static std::shared_ptr Open(const std::string& filePath, OpenDisposition disposition = OpenDisposition::ReadWrite, Utility::ManagedFile&& indexFile = {}) + { + CheckpointDatabase result{ filePath, disposition, std::move(indexFile) }; + return std::make_shared(std::move(result)); + } + + // Returns a value indicating whether the database is empty. + bool IsEmpty(); + + // Adds a new checkpoint name to the checkpoint table. + IdType AddCheckpoint(std::string_view checkpointName); + + // Returns all checkpoint ids in descending (newest at the front) order. + std::vector GetCheckpointIds(); + + // Returns a boolean value indicating a field exists for a checkpoint data type. + bool HasDataField(IdType checkpointId, int type, std::string name); + + // Returns the available data types for a checkpoint id. + std::vector GetDataTypes(IdType checkpointId); + + // Returns the available field names for a checkpoint data. + std::vector GetDataFieldNames(IdType checkpointId, int dataType); + + // Sets the value(s) for a data type and field. + void SetDataValue(IdType checkpointId, int dataType, std::string field, std::vector values); + + // Gets a single value for a data type field. + std::string GetDataFieldSingleValue(IdType checkpointId, int dataType, std::string_view field); + + // Gets multiple values for a data type field. + std::vector GetDataFieldMultiValue(IdType checkpointId, int dataType, std::string field); + + private: + // Constructor used to open an existing index. + CheckpointDatabase(const std::string& target, SQLiteStorageBase::OpenDisposition disposition, Utility::ManagedFile&& indexFile); + + // Constructor used to create a new index. + CheckpointDatabase(const std::string& target, Schema::Version version); + + // Creates the ICheckpointDatabase interface object for this version. + std::unique_ptr CreateICheckpointDatabase() const; + + std::unique_ptr m_interface; + }; +} \ No newline at end of file diff --git a/src/AppInstallerRepositoryCore/Microsoft/CheckpointRecord.h b/src/AppInstallerRepositoryCore/Microsoft/CheckpointRecord.h deleted file mode 100644 index 6c8f9223ff..0000000000 --- a/src/AppInstallerRepositoryCore/Microsoft/CheckpointRecord.h +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. -#pragma once -#include "SQLiteWrapper.h" -#include "Microsoft/Schema/ICheckpointRecord.h" -#include "Microsoft/SQLiteStorageBase.h" -#include - -namespace AppInstaller::Repository::Microsoft -{ - struct CheckpointRecord : SQLiteStorageBase - { - // An id that refers to a specific Checkpoint. - using IdType = SQLite::rowid_t; - - CheckpointRecord(const CheckpointRecord&) = delete; - CheckpointRecord& operator=(const CheckpointRecord&) = delete; - - CheckpointRecord(CheckpointRecord&&) = default; - CheckpointRecord& operator=(CheckpointRecord&&) = default; - - // Create a new CheckpointRecord database. - static CheckpointRecord CreateNew(const std::string& filePath, Schema::Version version = Schema::Version::Latest()); - - // Opens an existing CheckpointRecord database. - static CheckpointRecord Open(const std::string& filePath, OpenDisposition disposition = OpenDisposition::ReadWrite, Utility::ManagedFile&& indexFile = {}) - { - return { filePath, disposition, std::move(indexFile) }; - } - - // Returns a value indicating whether the record is empty. - bool IsEmpty(); - - // Returns the corresponding id of the checkpoint. - std::optional GetCheckpointIdByName(std::string_view checkpointName); - - // Adds a new checkpoint name to the checkpoint table. - IdType AddCheckpoint(std::string_view checkpointName); - - // Gets the name of all checkpoints. - std::vector GetCheckpoints(); - - // Returns a boolean value indicating a field exists for a checkpoint data type. - bool HasDataField(IdType checkpointId, int type, std::string name); - - // Returns the available data types for a checkpoint id. - std::vector GetDataTypes(IdType checkpointId); - - // Returns the available field names for a checkpoint data. - std::vector GetDataFieldNames(IdType checkpointId, int dataType); - - // Sets the value(s) for a data type and field. - void SetDataValue(IdType checkpointId, int dataType, std::string field, std::vector values); - - // Gets a single value for the data type. - std::string GetDataSingleValue(IdType checkpointId, int dataType); - - // Gets a single value for a data type field. - std::string GetDataFieldSingleValue(IdType checkpointId, int dataType, std::string_view field); - - // Gets multiple values for a data type field. - std::vector GetDataFieldMultiValue(IdType checkpointId, int dataType, std::string field); - - private: - // Constructor used to open an existing index. - CheckpointRecord(const std::string& target, SQLiteStorageBase::OpenDisposition disposition, Utility::ManagedFile&& indexFile); - - // Constructor used to create a new index. - CheckpointRecord(const std::string& target, Schema::Version version); - - // Creates the ICheckpointRecord interface object for this version. - std::unique_ptr CreateICheckpointRecord() const; - - std::unique_ptr m_interface; - }; -} \ No newline at end of file diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointRecordInterface.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointDatabaseInterface.h similarity index 54% rename from src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointRecordInterface.h rename to src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointDatabaseInterface.h index 3265c9973c..c05a217ec3 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointRecordInterface.h +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointDatabaseInterface.h @@ -1,11 +1,11 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once -#include "Microsoft/Schema/ICheckpointRecord.h" +#include "Microsoft/Schema/ICheckpointDatabase.h" namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 { - struct CheckpointRecordInterface : public ICheckpointRecord + struct CheckpointDatabaseInterface : public ICheckpointDatabase { // Version 1.0 Schema::Version GetVersion() const override; @@ -13,14 +13,11 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 private: bool IsEmpty(SQLite::Connection& connection) override; - std::vector GetAvailableCheckpoints(SQLite::Connection& connection) override; SQLite::rowid_t AddCheckpoint(SQLite::Connection& connection, std::string_view checkpointName) override; - std::optional GetCheckpointIdByName(SQLite::Connection& connection, std::string_view checkpointName) override; + std::vector GetCheckpointIds(SQLite::Connection& connection) override; std::vector GetCheckpointDataTypes(SQLite::Connection& connection, SQLite::rowid_t checkpointId) override; - bool HasCheckpointDataField(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int dataType, std::string_view name) override; void SetCheckpointDataValues(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int dataType, std::string_view name, std::vector values) override; std::vector GetCheckpointDataFields(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int dataType) override; - std::vector GetCheckpointDataFieldValues(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int dataType, std::string_view name) override; - std::string GetCheckpointDataValue(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int dataType) override; + std::optional> GetCheckpointDataFieldValues(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int dataType, std::string_view name) override; }; } \ No newline at end of file diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointDatabaseInterface_1_0.cpp b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointDatabaseInterface_1_0.cpp new file mode 100644 index 0000000000..9487eb4f91 --- /dev/null +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointDatabaseInterface_1_0.cpp @@ -0,0 +1,77 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#include "pch.h" +#include "Microsoft/Schema/Checkpoint_1_0/CheckpointDatabaseInterface.h" +#include "Microsoft/Schema/Checkpoint_1_0/CheckpointDataTable.h" +#include "Microsoft/Schema/Checkpoint_1_0/CheckpointTable.h" + +namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 +{ + Schema::Version CheckpointDatabaseInterface::GetVersion() const + { + return { 1, 0 }; + } + + void CheckpointDatabaseInterface::CreateTables(SQLite::Connection& connection) + { + SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "createCheckpointTables_v1_0"); + Checkpoint_V1_0::CheckpointTable::Create(connection); + Checkpoint_V1_0::CheckpointDataTable::Create(connection); + savepoint.Commit(); + } + + bool CheckpointDatabaseInterface::IsEmpty(SQLite::Connection& connection) + { + return CheckpointDataTable::IsEmpty(connection); + } + + SQLite::rowid_t CheckpointDatabaseInterface::AddCheckpoint(SQLite::Connection& connection, std::string_view checkpointName) + { + SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "addcheckpoint_v1_0"); + SQLite::rowid_t checkpointId = CheckpointTable::AddCheckpoint(connection, checkpointName); + savepoint.Commit(); + return checkpointId; + } + + std::vector CheckpointDatabaseInterface::GetCheckpointIds(SQLite::Connection& connection) + { + return CheckpointTable::GetCheckpointIds(connection); + } + + std::vector CheckpointDatabaseInterface::GetCheckpointDataTypes(SQLite::Connection& connection, SQLite::rowid_t checkpointId) + { + return CheckpointDataTable::GetAvailableData(connection, checkpointId); + } + + std::vector CheckpointDatabaseInterface::GetCheckpointDataFields(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int dataType) + { + return CheckpointDataTable::GetDataFields(connection, checkpointId, dataType); + } + + void CheckpointDatabaseInterface::SetCheckpointDataValues(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int dataType, std::string_view name, std::vector values) + { + SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "setCheckpointData_v1_0"); + + if (values.empty()) + { + CheckpointDataTable::AddCheckpointData(connection, checkpointId, dataType, name, {}, 0); + } + else + { + int index = 0; + + for (const auto& value : values) + { + CheckpointDataTable::AddCheckpointData(connection, checkpointId, dataType, name, value, index); + index++; + } + } + + savepoint.Commit(); + } + + std::optional> CheckpointDatabaseInterface::GetCheckpointDataFieldValues(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int dataType, std::string_view name) + { + return CheckpointDataTable::GetDataValuesByFieldName(connection, checkpointId, dataType, name); + } +} \ No newline at end of file diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointRecordInterface_1_0.cpp b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointRecordInterface_1_0.cpp deleted file mode 100644 index 388caa08e4..0000000000 --- a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointRecordInterface_1_0.cpp +++ /dev/null @@ -1,99 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. -#include "pch.h" -#include "Microsoft/Schema/Checkpoint_1_0/CheckpointRecordInterface.h" -#include "Microsoft/Schema/Checkpoint_1_0/CheckpointDataTable.h" -#include "Microsoft/Schema/Checkpoint_1_0/CheckpointTable.h" - -namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 -{ - Schema::Version CheckpointRecordInterface::GetVersion() const - { - return { 1, 0 }; - } - - void CheckpointRecordInterface::CreateTables(SQLite::Connection& connection) - { - SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "createCheckpointTables_v1_0"); - Checkpoint_V1_0::CheckpointTable::Create(connection); - Checkpoint_V1_0::CheckpointDataTable::Create(connection); - savepoint.Commit(); - } - - bool CheckpointRecordInterface::IsEmpty(SQLite::Connection& connection) - { - return CheckpointDataTable::IsEmpty(connection); - } - - std::vector CheckpointRecordInterface::GetAvailableCheckpoints(SQLite::Connection& connection) - { - return CheckpointTable::GetCheckpoints(connection); - } - - SQLite::rowid_t CheckpointRecordInterface::AddCheckpoint(SQLite::Connection& connection, std::string_view checkpointName) - { - SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "addcheckpoint_v1_0"); - SQLite::rowid_t checkpointId = CheckpointTable::AddCheckpoint(connection, checkpointName); - savepoint.Commit(); - return checkpointId; - } - - std::optional CheckpointRecordInterface::GetCheckpointIdByName(SQLite::Connection& connection, std::string_view checkpointName) - { - auto result = CheckpointTable::GetCheckpointId(connection, checkpointName); - - if (!result) - { - AICLI_LOG(Repo, Verbose, << "Did not find checkpoint " << checkpointName); - } - - return result; - } - - std::vector CheckpointRecordInterface::GetCheckpointDataTypes(SQLite::Connection& connection, SQLite::rowid_t checkpointId) - { - return CheckpointDataTable::GetAvailableData(connection, checkpointId); - } - - std::vector CheckpointRecordInterface::GetCheckpointDataFields(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int dataType) - { - return CheckpointDataTable::GetDataFields(connection, checkpointId, dataType); - } - - bool CheckpointRecordInterface::HasCheckpointDataField(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int dataType, std::string_view name) - { - return CheckpointDataTable::HasDataField(connection, checkpointId, dataType, name); - } - - void CheckpointRecordInterface::SetCheckpointDataValues(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int dataType, std::string_view name, std::vector values) - { - SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "setcheckpointdata_v1_0"); - - if (values.empty()) - { - CheckpointDataTable::AddCheckpointData(connection, checkpointId, dataType, name, {}, 0); - } - else - { - int index = 0; - - for (const auto& value : values) - { - CheckpointDataTable::AddCheckpointData(connection, checkpointId, dataType, name, value, index); - index++; - } - } - - savepoint.Commit(); - } - - std::string CheckpointRecordInterface::GetCheckpointDataValue(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int dataType) - { - return CheckpointDataTable::GetDataValue(connection, checkpointId, dataType); - } - - std::vector CheckpointRecordInterface::GetCheckpointDataFieldValues(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int dataType, std::string_view name) - { - return CheckpointDataTable::GetDataValuesByFieldName(connection, checkpointId, dataType, name); - } -} \ No newline at end of file diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointTable.cpp b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointTable.cpp index 14d8a9f950..86ccb08b27 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointTable.cpp +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointTable.cpp @@ -9,7 +9,7 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 using namespace std::string_view_literals; static constexpr std::string_view s_CheckpointTable_Table_Name = "Checkpoints"sv; static constexpr std::string_view s_CheckpointTable_Name_Column = "Name"; - static constexpr std::string_view s_CheckpointTable_WriteTime_Column = "WriteTime"; + static constexpr std::string_view s_CheckpointTable_CreationTime_Column = "CreationTime"; std::string_view CheckpointTable::TableName() { @@ -24,26 +24,26 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 StatementBuilder createTableBuilder; createTableBuilder.CreateTable(s_CheckpointTable_Table_Name).BeginColumns(); - createTableBuilder.Column(ColumnBuilder(s_CheckpointTable_Name_Column, Type::Text).Unique()); - createTableBuilder.Column(ColumnBuilder(s_CheckpointTable_WriteTime_Column, Type::Int64)); + createTableBuilder.Column(ColumnBuilder(s_CheckpointTable_Name_Column, Type::Text)); + createTableBuilder.Column(ColumnBuilder(s_CheckpointTable_CreationTime_Column, Type::Int64)); createTableBuilder.EndColumns(); createTableBuilder.Execute(connection); savepoint.Commit(); } - std::vector CheckpointTable::GetCheckpoints(SQLite::Connection& connection) + std::vector CheckpointTable::GetCheckpointIds(SQLite::Connection& connection) { SQLite::Builder::StatementBuilder builder; - builder.Select(s_CheckpointTable_Name_Column).From(s_CheckpointTable_Table_Name).OrderBy(SQLite::RowIDName).Descending(); + builder.Select(SQLite::RowIDName).From(s_CheckpointTable_Table_Name).OrderBy(SQLite::RowIDName).Descending(); SQLite::Statement select = builder.Prepare(connection); - std::vector checkpoints; + std::vector checkpoints; while (select.Step()) { - checkpoints.emplace_back(select.GetColumn(0)); + checkpoints.emplace_back(select.GetColumn(0)); } return checkpoints; @@ -54,27 +54,10 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 SQLite::Builder::StatementBuilder builder; builder.InsertInto(s_CheckpointTable_Table_Name) .Columns({ s_CheckpointTable_Name_Column, - s_CheckpointTable_WriteTime_Column }) + s_CheckpointTable_CreationTime_Column }) .Values(checkpointName, Utility::GetCurrentUnixEpoch()); builder.Execute(connection); return connection.GetLastInsertRowID(); } - - std::optional CheckpointTable::GetCheckpointId(SQLite::Connection& connection, std::string_view checkpointName) - { - SQLite::Builder::StatementBuilder builder; - builder.Select(SQLite::RowIDName).From(s_CheckpointTable_Table_Name).Where(s_CheckpointTable_Name_Column).Equals(checkpointName); - - SQLite::Statement select = builder.Prepare(connection); - - if (select.Step()) - { - return select.GetColumn(0); - } - else - { - return {}; - } - } } \ No newline at end of file diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointTable.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointTable.h index daefe73bdd..6b7e2edd93 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointTable.h +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointTable.h @@ -15,13 +15,10 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 // Creates the table with named indices. static void Create(SQLite::Connection& connection); - // Gets the names of all checkpoints. - static std::vector GetCheckpoints(SQLite::Connection& connection); + // Returns all checkpoint ids in descending (newest at the front) order. + static std::vector GetCheckpointIds(SQLite::Connection& connection); // Adds a checkpoint. static SQLite::rowid_t AddCheckpoint(SQLite::Connection& connection, std::string_view checkpointName); - - // Gets the id of a checkpoint. - static std::optional GetCheckpointId(SQLite::Connection& connection, std::string_view checkpointName); }; } \ No newline at end of file diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/ICheckpointRecord.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/ICheckpointDatabase.h similarity index 60% rename from src/AppInstallerRepositoryCore/Microsoft/Schema/ICheckpointRecord.h rename to src/AppInstallerRepositoryCore/Microsoft/Schema/ICheckpointDatabase.h index 0444c229ae..392d7bcd22 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/Schema/ICheckpointRecord.h +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/ICheckpointDatabase.h @@ -6,9 +6,9 @@ namespace AppInstaller::Repository::Microsoft::Schema { - struct ICheckpointRecord + struct ICheckpointDatabase { - virtual ~ICheckpointRecord() = default; + virtual ~ICheckpointDatabase() = default; // Gets the schema version that this index interface is built for. virtual Schema::Version GetVersion() const = 0; @@ -20,31 +20,22 @@ namespace AppInstaller::Repository::Microsoft::Schema // Returns a bool value indicating whether all checkpoint tables are empty. virtual bool IsEmpty(SQLite::Connection& connection) = 0; - // Returns all recorded checkpoint names. - virtual std::vector GetAvailableCheckpoints(SQLite::Connection& connection) = 0; + // Returns all checkpoint ids in descending (newest at the front) order. + virtual std::vector GetCheckpointIds(SQLite::Connection& connection) = 0; // Adds a new checkpoint to the Checkpoint table. virtual SQLite::rowid_t AddCheckpoint(SQLite::Connection& connection, std::string_view checkpointName) = 0; - // Gets the id of a checkpoint by name. - virtual std::optional GetCheckpointIdByName(SQLite::Connection& connection, std::string_view checkpointName) = 0; - // Gets the data types associated with a checkpoint id. virtual std::vector GetCheckpointDataTypes(SQLite::Connection& connection, SQLite::rowid_t checkpointId) = 0; - // Returns a bool value indicating whether a field exists for a checkpoint data type. - virtual bool HasCheckpointDataField(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int dataType, std::string_view name) = 0; - // Sets the field values for a checkpoint data type. virtual void SetCheckpointDataValues(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int dataType, std::string_view name, std::vector values) = 0; - // Gets a single value for a checkpoint data type. - virtual std::string GetCheckpointDataValue(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int dataType) = 0; - // Gets all field names for a checkpoint data type. virtual std::vector GetCheckpointDataFields(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int dataType) = 0; // Gets all field values for a checkpoint data type. - virtual std::vector GetCheckpointDataFieldValues(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int dataType, std::string_view name) = 0; + virtual std::optional> GetCheckpointDataFieldValues(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int dataType, std::string_view name) = 0; }; } \ No newline at end of file diff --git a/src/AppInstallerCLICore/Checkpoint.h b/src/AppInstallerRepositoryCore/Public/winget/Checkpoint.h similarity index 51% rename from src/AppInstallerCLICore/Checkpoint.h rename to src/AppInstallerRepositoryCore/Public/winget/Checkpoint.h index 286fe7b92a..2e26deb001 100644 --- a/src/AppInstallerCLICore/Checkpoint.h +++ b/src/AppInstallerRepositoryCore/Public/winget/Checkpoint.h @@ -1,8 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once -#include "ExecutionContextData.h" -#include "Microsoft/CheckpointRecord.h" +#include "Microsoft/CheckpointDatabase.h" #include using namespace AppInstaller::Repository::Microsoft; @@ -27,62 +26,50 @@ namespace AppInstaller::Checkpoints std::vector GetCheckpointDataTypes() { - return m_checkpointRecord->GetDataTypes(m_checkpointId); + return m_checkpointDatabase->GetDataTypes(m_checkpointId); } // Returns a boolean value indicating whether the field name exists. bool Has(T dataType, std::string fieldName) { - return m_checkpointRecord->HasDataField(m_checkpointId, dataType, fieldName); + return m_checkpointDatabase->HasDataField(m_checkpointId, dataType, fieldName); } // Gets all available field names. std::vector GetFieldNames(T dataType) { - return m_checkpointRecord->GetDataFieldNames(m_checkpointId, dataType); - } - - // Sets a single value for the a data type. - void Set(T dataType, std::string value) - { - m_checkpointRecord->SetDataValue(m_checkpointId, dataType, {}, { value }); + return m_checkpointDatabase->GetDataFieldNames(m_checkpointId, dataType); } // Sets a single field value for a data type. void Set(T dataType, std::string fieldName, std::string value) { - m_checkpointRecord->SetDataValue(m_checkpointId, dataType, fieldName, { value }); + m_checkpointDatabase->SetDataValue(m_checkpointId, dataType, fieldName, { value }); } // Sets multiple field values for a data type. void SetMany(T dataType, std::string fieldName, std::vector values) { - m_checkpointRecord->SetDataValue(m_checkpointId, dataType, fieldName, values); - } - - // Gets a single value for a data type. - std::string Get(T dataType) - { - return m_checkpointRecord->GetDataSingleValue(m_checkpointId, dataType); + m_checkpointDatabase->SetDataValue(m_checkpointId, dataType, fieldName, values); } // Gets a single field value for a data type. std::string Get(T dataType, std::string fieldName) { - return m_checkpointRecord->GetDataFieldSingleValue(m_checkpointId, dataType, fieldName); + return m_checkpointDatabase->GetDataFieldSingleValue(m_checkpointId, dataType, fieldName); } // Gets multiple field values for a data type. std::vector GetMany(T dataType, std::string fieldName) { - return m_checkpointRecord->GetDataFieldMultiValue(m_checkpointId, dataType, fieldName); + return m_checkpointDatabase->GetDataFieldMultiValue(m_checkpointId, dataType, fieldName); } private: - Checkpoint(std::shared_ptr checkpointRecord, AppInstaller::Repository::Microsoft::CheckpointRecord::IdType checkpointId) : - m_checkpointRecord(checkpointRecord), m_checkpointId(checkpointId){}; + Checkpoint(std::shared_ptr checkpointDatabase, AppInstaller::Repository::Microsoft::CheckpointDatabase::IdType checkpointId) : + m_checkpointDatabase(checkpointDatabase), m_checkpointId(checkpointId){}; - AppInstaller::Repository::Microsoft::CheckpointRecord::IdType m_checkpointId; - std::shared_ptr m_checkpointRecord; + AppInstaller::Repository::Microsoft::CheckpointDatabase::IdType m_checkpointId; + std::shared_ptr m_checkpointDatabase; }; } \ No newline at end of file From 38d3f837d75712a9bf5bdd839bfb8abc9e4ddcac Mon Sep 17 00:00:00 2001 From: Ryan Fu Date: Tue, 19 Sep 2023 18:03:01 -0700 Subject: [PATCH 37/43] save work --- src/AppInstallerCLICore/CheckpointManager.cpp | 18 ++++---- src/AppInstallerCLICore/CheckpointManager.h | 6 +-- .../Commands/ResumeCommand.cpp | 38 ++++++++-------- .../Commands/ResumeCommand.h | 1 - src/AppInstallerCLICore/ExecutionContext.cpp | 6 +-- src/AppInstallerCLICore/ExecutionContext.h | 2 +- src/AppInstallerCommonCore/Runtime.cpp | 8 ++-- .../Checkpoint_1_0/CheckpointDataTable.cpp | 43 +++++++++++++----- .../Checkpoint_1_0/CheckpointDataTable.h | 3 ++ .../Schema/Checkpoint_1_0/CheckpointTable.cpp | 2 + .../Public/winget/Checkpoint.h | 12 ++--- .../AppInstallerStrings.cpp | 44 ++----------------- .../Public/AppInstallerStrings.h | 7 +-- 13 files changed, 85 insertions(+), 105 deletions(-) diff --git a/src/AppInstallerCLICore/CheckpointManager.cpp b/src/AppInstallerCLICore/CheckpointManager.cpp index 58f226f181..c530f61def 100644 --- a/src/AppInstallerCLICore/CheckpointManager.cpp +++ b/src/AppInstallerCLICore/CheckpointManager.cpp @@ -16,12 +16,10 @@ namespace AppInstaller::Checkpoints constexpr std::string_view s_AutomaticCheckpoint = "automatic"sv; constexpr std::string_view s_CheckpointsFileName = "checkpoints.db"sv; - std::filesystem::path CheckpointManager::GetCheckpointDatabasePath(GUID guid, bool createCheckpointDirectory) + std::filesystem::path CheckpointManager::GetCheckpointDatabasePath(const std::string_view& resumeId, bool createCheckpointDirectory) { - wchar_t checkpointGuid[256]; - THROW_HR_IF(E_UNEXPECTED, !StringFromGUID2(guid, checkpointGuid, ARRAYSIZE(checkpointGuid))); - const auto checkpointsDirectory = Runtime::GetPathTo(Runtime::PathName::CheckpointsLocation) / checkpointGuid; + const auto checkpointsDirectory = Runtime::GetPathTo(Runtime::PathName::CheckpointsLocation) / resumeId; if (createCheckpointDirectory) { @@ -42,12 +40,14 @@ namespace AppInstaller::Checkpoints CheckpointManager::CheckpointManager() { - std::ignore = CoCreateGuid(&m_resumeId); - const auto& checkpointDatabasePath = GetCheckpointDatabasePath(m_resumeId); + GUID resumeId; + std::ignore = CoCreateGuid(&resumeId); + m_resumeId = Utility::ConvertGuidToString(resumeId); + const auto& checkpointDatabasePath = GetCheckpointDatabasePath(m_resumeId, true); m_checkpointDatabase = CheckpointDatabase::CreateNew(checkpointDatabasePath.u8string()); } - CheckpointManager::CheckpointManager(GUID resumeId) + CheckpointManager::CheckpointManager(const std::string& resumeId) { m_resumeId = resumeId; const auto& checkpointDatabasePath = GetCheckpointDatabasePath(m_resumeId); @@ -64,7 +64,7 @@ namespace AppInstaller::Checkpoints const auto& executingCommand = context.GetExecutingCommand(); if (executingCommand != nullptr) { - automaticCheckpoint.Set(AutomaticCheckpointData::CommandName, {}, std::string{ executingCommand->FullName() }); + automaticCheckpoint.Set(AutomaticCheckpointData::Command, {}, std::string{ executingCommand->FullName() }); } const auto& argTypes = context.Args.GetTypes(); @@ -127,7 +127,7 @@ namespace AppInstaller::Checkpoints m_checkpointDatabase.reset(); } - if (m_resumeId != GUID_NULL) + if (!m_resumeId.empty()) { const auto& checkpointDatabasePath = GetCheckpointDatabasePath(m_resumeId); diff --git a/src/AppInstallerCLICore/CheckpointManager.h b/src/AppInstallerCLICore/CheckpointManager.h index 39277d6206..33f09c08c7 100644 --- a/src/AppInstallerCLICore/CheckpointManager.h +++ b/src/AppInstallerCLICore/CheckpointManager.h @@ -20,12 +20,12 @@ namespace AppInstaller::Checkpoints CheckpointManager(); // Constructor that loads the resume id and opens an existing checkpoint database. - CheckpointManager(GUID resumeId); + CheckpointManager(const std::string& resumeId); ~CheckpointManager() = default; // Gets the file path of the checkpoint database. - static std::filesystem::path GetCheckpointDatabasePath(GUID guid, bool createCheckpointDirectory = false); + static std::filesystem::path GetCheckpointDatabasePath(const std::string_view& resumeId, bool createCheckpointDirectory = false); // Gets the automatic checkpoint. Checkpoint GetAutomaticCheckpoint(); @@ -43,7 +43,7 @@ namespace AppInstaller::Checkpoints void CleanUpDatabase(); private: - GUID m_resumeId = {}; + std::string m_resumeId; std::shared_ptr m_checkpointDatabase; }; } \ No newline at end of file diff --git a/src/AppInstallerCLICore/Commands/ResumeCommand.cpp b/src/AppInstallerCLICore/Commands/ResumeCommand.cpp index d80baa46c9..2b9478602f 100644 --- a/src/AppInstallerCLICore/Commands/ResumeCommand.cpp +++ b/src/AppInstallerCLICore/Commands/ResumeCommand.cpp @@ -39,17 +39,16 @@ namespace AppInstaller::CLI void ResumeCommand::ExecuteInternal(Execution::Context& context) const { - std::string resumeGuidString{ context.Args.GetArg(Execution::Args::Type::ResumeId) }; - GUID checkpointId = Utility::ConvertToGuid(resumeGuidString); + const auto& resumeId = context.Args.GetArg(Execution::Args::Type::ResumeId); - if (!std::filesystem::exists(Checkpoints::CheckpointManager::GetCheckpointDatabasePath(checkpointId))) + if (!std::filesystem::exists(Checkpoints::CheckpointManager::GetCheckpointDatabasePath(resumeId))) { - context.Reporter.Error() << Resource::String::ResumeIdNotFoundError(Utility::LocIndView{ resumeGuidString }) << std::endl; + context.Reporter.Error() << Resource::String::ResumeIdNotFoundError(Utility::LocIndView{ resumeId }) << std::endl; AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_RESUME_ID_NOT_FOUND); } Execution::Context resumeContext = context.CreateEmptyContext(); - std::optional> foundAutomaticCheckpoint = resumeContext.LoadCheckpoint(checkpointId); + std::optional> foundAutomaticCheckpoint = resumeContext.LoadCheckpoint(std::string{ resumeId }); if (!foundAutomaticCheckpoint.has_value()) { context.Reporter.Error() << Resource::String::ResumeStateDataNotFoundError << std::endl; @@ -65,19 +64,27 @@ namespace AppInstaller::CLI AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_CLIENT_VERSION_MISMATCH); } - const auto& checkpointCommandName = automaticCheckpoint.Get(AutomaticCheckpointData::CommandName, {}); + const auto& checkpointCommand = automaticCheckpoint.Get(AutomaticCheckpointData::Command, {}); + + AICLI_LOG(CLI, Info, << "Resuming command: " << checkpointCommand); std::unique_ptr commandToResume; + std::unique_ptr currentCommand = std::make_unique(); - AICLI_LOG(CLI, Info, << "Resuming command: " << checkpointCommandName); - for (auto& command : std::make_unique()->GetCommands()) + // TODO: Handle command parsing for multiple commands + for (const auto& checkpointCommandPart : Utility::Split(checkpointCommand, ' ')) { - if (Utility::CaseInsensitiveEquals(checkpointCommandName, command->Name())) + for (auto& command : currentCommand->GetCommands()) { - commandToResume = std::move(command); - break; + if (Utility::CaseInsensitiveEquals(checkpointCommandPart, command->FullName())) + { + currentCommand = std::move(command); + break; + } } } + commandToResume = std::move(currentCommand); + THROW_HR_IF_MSG(E_UNEXPECTED, !commandToResume, "Command to resume not found."); for (const auto& fieldName : automaticCheckpoint.GetFieldNames(AutomaticCheckpointData::Arguments)) @@ -109,13 +116,4 @@ namespace AppInstaller::CLI commandToResume->Resume(resumeContext); context.SetTerminationHR(resumeContext.GetTerminationHR()); } - - void ResumeCommand::ValidateArgumentsInternal(Execution::Args& execArgs) const - { - std::string resumeGuidString{ execArgs.GetArg(Execution::Args::Type::ResumeId) }; - if (!Utility::IsValidGuidString(resumeGuidString)) - { - throw CommandException(Resource::String::InvalidResumeIdError(Utility::LocIndView{ resumeGuidString })); - } - } } diff --git a/src/AppInstallerCLICore/Commands/ResumeCommand.h b/src/AppInstallerCLICore/Commands/ResumeCommand.h index 497458a644..79c915df6d 100644 --- a/src/AppInstallerCLICore/Commands/ResumeCommand.h +++ b/src/AppInstallerCLICore/Commands/ResumeCommand.h @@ -18,6 +18,5 @@ namespace AppInstaller::CLI protected: void ExecuteInternal(Execution::Context& context) const override; - void ValidateArgumentsInternal(Execution::Args& execArgs) const override; }; } diff --git a/src/AppInstallerCLICore/ExecutionContext.cpp b/src/AppInstallerCLICore/ExecutionContext.cpp index b9adb8fcee..3a57761af0 100644 --- a/src/AppInstallerCLICore/ExecutionContext.cpp +++ b/src/AppInstallerCLICore/ExecutionContext.cpp @@ -7,7 +7,6 @@ #include "winget/UserSettings.h" #include "AppInstallerRuntime.h" #include "Command.h" -#include "CheckpointManager.h" #include "Public/winget/Checkpoint.h" using namespace AppInstaller::Checkpoints; @@ -272,7 +271,8 @@ namespace AppInstaller::CLI::Execution Context Context::CreateEmptyContext() { - return Context(Reporter, m_threadGlobals); + AppInstaller::ThreadLocalStorage::WingetThreadGlobals threadGlobals{ m_threadGlobals, ThreadLocalStorage::WingetThreadGlobals::create_sub_thread_globals_t{} }; + return Context(Reporter, threadGlobals); } std::unique_ptr Context::CreateSubContext() @@ -427,7 +427,7 @@ namespace AppInstaller::CLI::Execution } #endif - std::optional> Context::LoadCheckpoint(GUID resumeId) + std::optional> Context::LoadCheckpoint(const std::string& resumeId) { m_checkpointManager = std::make_unique(resumeId); return m_checkpointManager->GetAutomaticCheckpoint(); diff --git a/src/AppInstallerCLICore/ExecutionContext.h b/src/AppInstallerCLICore/ExecutionContext.h index 31b32ce757..906063b7af 100644 --- a/src/AppInstallerCLICore/ExecutionContext.h +++ b/src/AppInstallerCLICore/ExecutionContext.h @@ -168,7 +168,7 @@ namespace AppInstaller::CLI::Execution #endif // Called by the resume command. Loads the checkpoint manager with the resume id and returns the automatic checkpoint. - std::optional> LoadCheckpoint(GUID resumeId); + std::optional> LoadCheckpoint(const std::string& resumeId); // Returns data checkpoints in the order of latest checkpoint to earliest. std::vector> GetCheckpoints(); diff --git a/src/AppInstallerCommonCore/Runtime.cpp b/src/AppInstallerCommonCore/Runtime.cpp index 1d575f59e7..560f6bdaaa 100644 --- a/src/AppInstallerCommonCore/Runtime.cpp +++ b/src/AppInstallerCommonCore/Runtime.cpp @@ -418,15 +418,10 @@ namespace AppInstaller::Runtime result.ACL[ACEPrincipal::System] = ACEPermissions::All; result.ACL[ACEPrincipal::Admins] = ACEPermissions::All; break; - case PathName::CheckpointsLocation: case PathName::LocalState: case PathName::UserFileSettings: result.Path.assign(appStorage.LocalFolder().Path().c_str()); mayBeInProfilePath = true; - if (path == PathName::CheckpointsLocation) - { - result.Path /= s_CheckpointsDirectory; - } break; case PathName::DefaultLogLocation: // To enable UIF collection through Feedback hub, we must put our logs here. @@ -473,6 +468,9 @@ namespace AppInstaller::Runtime result.Path = GetPackagePath(); result.Create = false; break; + case PathName::CheckpointsLocation: + result.Path = GetPathTo(PathName::LocalState) / s_CheckpointsDirectory; + break; default: THROW_HR(E_UNEXPECTED); } diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointDataTable.cpp b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointDataTable.cpp index 10bf31d5bb..aac10d1e28 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointDataTable.cpp +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointDataTable.cpp @@ -58,11 +58,18 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 StatementBuilder createTableBuilder; createTableBuilder.CreateTable(s_CheckpointDataTable_Table_Name).BeginColumns(); createTableBuilder.Column(ColumnBuilder(s_CheckpointDataTable_CheckpointId_Column, Type::Int).NotNull()); - createTableBuilder.Column(ColumnBuilder(s_CheckpointDataTable_ContextData_Column, Type::Int)); - createTableBuilder.Column(ColumnBuilder(s_CheckpointDataTable_Name_Column, Type::Text)); + createTableBuilder.Column(ColumnBuilder(s_CheckpointDataTable_ContextData_Column, Type::Int).NotNull()); + createTableBuilder.Column(ColumnBuilder(s_CheckpointDataTable_Name_Column, Type::Text).NotNull()); createTableBuilder.Column(ColumnBuilder(s_CheckpointDataTable_Value_Column, Type::Text)); - createTableBuilder.Column(ColumnBuilder(s_CheckpointDataTable_Index_Column, Type::Int)); - createTableBuilder.EndColumns(); + createTableBuilder.Column(ColumnBuilder(s_CheckpointDataTable_Index_Column, Type::Int).NotNull()); + + PrimaryKeyBuilder pkBuilder; + pkBuilder.Column(s_CheckpointDataTable_CheckpointId_Column); + pkBuilder.Column(s_CheckpointDataTable_ContextData_Column); + pkBuilder.Column(s_CheckpointDataTable_Name_Column); + pkBuilder.Column(s_CheckpointDataTable_Index_Column); + + createTableBuilder.Column(pkBuilder).EndColumns(); createTableBuilder.Execute(connection); savepoint.Commit(); } @@ -100,13 +107,26 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 SQLite::rowid_t CheckpointDataTable::AddCheckpointData(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int contextData, std::string_view name, std::string_view value, int index) { SQLite::Builder::StatementBuilder builder; - builder.InsertInto(s_CheckpointDataTable_Table_Name) - .Columns({ s_CheckpointDataTable_CheckpointId_Column, - s_CheckpointDataTable_ContextData_Column, - s_CheckpointDataTable_Name_Column, - s_CheckpointDataTable_Value_Column, - s_CheckpointDataTable_Index_Column}) - .Values(checkpointId, contextData, name, value, index); + + if (value.empty()) + { + builder.InsertInto(s_CheckpointDataTable_Table_Name) + .Columns({ s_CheckpointDataTable_CheckpointId_Column, + s_CheckpointDataTable_ContextData_Column, + s_CheckpointDataTable_Name_Column, + s_CheckpointDataTable_Index_Column }) + .Values(checkpointId, contextData, name, index); + } + else + { + builder.InsertInto(s_CheckpointDataTable_Table_Name) + .Columns({ s_CheckpointDataTable_CheckpointId_Column, + s_CheckpointDataTable_ContextData_Column, + s_CheckpointDataTable_Name_Column, + s_CheckpointDataTable_Value_Column, + s_CheckpointDataTable_Index_Column }) + .Values(checkpointId, contextData, name, value, index); + } builder.Execute(connection); return connection.GetLastInsertRowID(); @@ -161,7 +181,6 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 return values; } - std::string CheckpointDataTable::GetDataValue(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int type) { SQLite::Builder::StatementBuilder builder; diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointDataTable.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointDataTable.h index a3daf281fa..0fa70b0d8f 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointDataTable.h +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointDataTable.h @@ -24,16 +24,19 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 // Adds a context data for a checkpoint. Index is used to represent the item number if the context data has more than one value. static SQLite::rowid_t AddCheckpointData(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int contextData, std::string_view name, std::string_view value, int index = 1); + // Gets all fields for a context data. static std::vector GetDataFields(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int type); // Gets the context data values by property name from a checkpoint id. static std::vector GetDataValuesByFieldName(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int contextData, std::string_view name); + // Returns a boolean value indicating whether a field exists for a context data. static bool HasDataField(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int type, std::string_view name); // Removes the context data by checkpoint id. static void RemoveData(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int contextData); + // Gets a single data value for a context data. static std::string GetDataValue(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int type); }; } \ No newline at end of file diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointTable.cpp b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointTable.cpp index 86ccb08b27..90c8ffcc76 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointTable.cpp +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointTable.cpp @@ -8,6 +8,7 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 { using namespace std::string_view_literals; static constexpr std::string_view s_CheckpointTable_Table_Name = "Checkpoints"sv; + static constexpr std::string_view s_CheckpointTable_Index_Name = "Checkpoints_pkindex"sv; static constexpr std::string_view s_CheckpointTable_Name_Column = "Name"; static constexpr std::string_view s_CheckpointTable_CreationTime_Column = "CreationTime"; @@ -24,6 +25,7 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 StatementBuilder createTableBuilder; createTableBuilder.CreateTable(s_CheckpointTable_Table_Name).BeginColumns(); + createTableBuilder.Column(ColumnBuilder(s_CheckpointTable_Index_Name, Type::RowId).PrimaryKey()); createTableBuilder.Column(ColumnBuilder(s_CheckpointTable_Name_Column, Type::Text)); createTableBuilder.Column(ColumnBuilder(s_CheckpointTable_CreationTime_Column, Type::Int64)); createTableBuilder.EndColumns(); diff --git a/src/AppInstallerRepositoryCore/Public/winget/Checkpoint.h b/src/AppInstallerRepositoryCore/Public/winget/Checkpoint.h index 2e26deb001..924c30145e 100644 --- a/src/AppInstallerRepositoryCore/Public/winget/Checkpoint.h +++ b/src/AppInstallerRepositoryCore/Public/winget/Checkpoint.h @@ -11,7 +11,7 @@ namespace AppInstaller::Checkpoints enum AutomaticCheckpointData { ClientVersion, - CommandName, + Command, Arguments }; @@ -30,7 +30,7 @@ namespace AppInstaller::Checkpoints } // Returns a boolean value indicating whether the field name exists. - bool Has(T dataType, std::string fieldName) + bool Has(T dataType, const std::string& fieldName) { return m_checkpointDatabase->HasDataField(m_checkpointId, dataType, fieldName); } @@ -42,25 +42,25 @@ namespace AppInstaller::Checkpoints } // Sets a single field value for a data type. - void Set(T dataType, std::string fieldName, std::string value) + void Set(T dataType, const std::string& fieldName, const std::string& value) { m_checkpointDatabase->SetDataValue(m_checkpointId, dataType, fieldName, { value }); } // Sets multiple field values for a data type. - void SetMany(T dataType, std::string fieldName, std::vector values) + void SetMany(T dataType, const std::string& fieldName, const std::vector& values) { m_checkpointDatabase->SetDataValue(m_checkpointId, dataType, fieldName, values); } // Gets a single field value for a data type. - std::string Get(T dataType, std::string fieldName) + std::string Get(T dataType, const std::string& fieldName) { return m_checkpointDatabase->GetDataFieldSingleValue(m_checkpointId, dataType, fieldName); } // Gets multiple field values for a data type. - std::vector GetMany(T dataType, std::string fieldName) + std::vector GetMany(T dataType, const std::string& fieldName) { return m_checkpointDatabase->GetDataFieldMultiValue(m_checkpointId, dataType, fieldName); } diff --git a/src/AppInstallerSharedLib/AppInstallerStrings.cpp b/src/AppInstallerSharedLib/AppInstallerStrings.cpp index ad2cdcdf2c..630b019d0d 100644 --- a/src/AppInstallerSharedLib/AppInstallerStrings.cpp +++ b/src/AppInstallerSharedLib/AppInstallerStrings.cpp @@ -861,46 +861,10 @@ namespace AppInstaller::Utility return value ? "true"sv : "false"sv; } - bool IsValidGuidString(const std::string& value) + std::string ConvertGuidToString(GUID value) { - if (value.empty()) - { - return false; - } - - const std::regex guidPattern("^[{]?[0-9a-fA-F]{8}-([0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}[}]?$"); - - std::string guidString = value; - - if (value.front() != '{') - { - guidString.insert(0, 1, '{'); - } - - if (value.back() != '}') - { - guidString.push_back('}'); - } - - return regex_match(value, guidPattern); - } - - GUID ConvertToGuid(const std::string& value) - { - std::string guidString = value; - - if (value.front() != '{') - { - guidString.insert(0, 1, '{'); - } - - if (value.back() != '}') - { - guidString.push_back('}'); - } - - GUID result; - THROW_IF_FAILED_MSG(CLSIDFromString(ConvertToUTF16(guidString).c_str(), &result), "ConvertToGuid: Invalid guid string"); - return result; + wchar_t buffer[256]; + THROW_HR_IF(E_UNEXPECTED, !StringFromGUID2(value, buffer, ARRAYSIZE(buffer))); + return ConvertToUTF8(buffer); } } diff --git a/src/AppInstallerSharedLib/Public/AppInstallerStrings.h b/src/AppInstallerSharedLib/Public/AppInstallerStrings.h index 190443ec51..0cd46936d5 100644 --- a/src/AppInstallerSharedLib/Public/AppInstallerStrings.h +++ b/src/AppInstallerSharedLib/Public/AppInstallerStrings.h @@ -266,9 +266,6 @@ namespace AppInstaller::Utility // Converts the given boolean value to a string. std::string_view ConvertBoolToString(bool value); - // Returns a boolean value indicating whether the provided string is a valid guid. - bool IsValidGuidString(const std::string& value); - - // Converts the given string into a guid. - GUID ConvertToGuid(const std::string& value); + // Converts the given GUID value to a string. + std::string ConvertGuidToString(GUID value); } From 1d1250b74b46166ecea913f9896fd00a5a018175 Mon Sep 17 00:00:00 2001 From: Ryan Fu Date: Wed, 20 Sep 2023 12:06:28 -0700 Subject: [PATCH 38/43] address rest of comments --- .github/actions/spelling/allow.txt | 3 +- src/AppInstallerCLICore/CheckpointManager.cpp | 12 +- src/AppInstallerCLICore/CheckpointManager.h | 6 +- .../Commands/PinCommand.cpp | 2 + .../Commands/ResumeCommand.cpp | 56 +++++--- src/AppInstallerCLICore/ExecutionContext.cpp | 2 +- src/AppInstallerCLICore/Resources.h | 1 - src/AppInstallerCLIE2ETests/ResumeCommand.cs | 9 +- .../Shared/Strings/en-us/winget.resw | 3 - .../AppInstallerCLITests.vcxproj | 2 +- .../AppInstallerCLITests.vcxproj.filters | 2 +- .../CheckpointDatabase.cpp | 129 ++++++++++++++++++ src/AppInstallerCLITests/CheckpointRecord.cpp | 129 ------------------ src/AppInstallerCLITests/ResumeFlow.cpp | 9 +- src/AppInstallerCLITests/Strings.cpp | 19 +-- .../Microsoft/CheckpointDatabase.cpp | 10 +- .../Microsoft/CheckpointDatabase.h | 8 +- .../CheckpointDatabaseInterface.h | 2 +- .../Schema/Checkpoint_1_0/CheckpointTable.cpp | 11 +- .../SQLiteStatementBuilder.cpp | 3 + .../SQLiteStatementBuilder.h | 1 + 21 files changed, 210 insertions(+), 209 deletions(-) create mode 100644 src/AppInstallerCLITests/CheckpointDatabase.cpp delete mode 100644 src/AppInstallerCLITests/CheckpointRecord.cpp diff --git a/.github/actions/spelling/allow.txt b/.github/actions/spelling/allow.txt index 1eb064b53d..5ecdd33c7c 100644 --- a/.github/actions/spelling/allow.txt +++ b/.github/actions/spelling/allow.txt @@ -61,6 +61,7 @@ cer certutil chrono cin +cguid cla CLASSNOTAVAILABLE CLSCTX @@ -495,8 +496,6 @@ selectany SERVICEPACKMAJOR SERVICEPACKMINOR setfill -setcheckpointdata -setdatavalue setschemaversion setupexitcodes setvariable diff --git a/src/AppInstallerCLICore/CheckpointManager.cpp b/src/AppInstallerCLICore/CheckpointManager.cpp index c530f61def..c3c1c6587b 100644 --- a/src/AppInstallerCLICore/CheckpointManager.cpp +++ b/src/AppInstallerCLICore/CheckpointManager.cpp @@ -8,9 +8,9 @@ namespace AppInstaller::Checkpoints { + using namespace AppInstaller::CLI; using namespace AppInstaller::Repository::Microsoft; using namespace AppInstaller::Repository::SQLite; - using namespace AppInstaller::CLI; // This checkpoint name is reserved for the starting checkpoint which captures the automatic metadata. constexpr std::string_view s_AutomaticCheckpoint = "automatic"sv; @@ -93,14 +93,14 @@ namespace AppInstaller::Checkpoints THROW_HR(E_UNEXPECTED); } - const auto& automaticCheckpointId = checkpointIds.back(); - return Checkpoint{ std::move(m_checkpointDatabase), automaticCheckpointId }; + CheckpointDatabase::IdType automaticCheckpointId = checkpointIds.back(); + return Checkpoint{ m_checkpointDatabase, automaticCheckpointId }; } Checkpoint CheckpointManager::CreateCheckpoint(std::string_view checkpointName) { - CheckpointDatabase::IdType startCheckpointId = m_checkpointDatabase->AddCheckpoint(checkpointName); - Checkpoint checkpoint{ m_checkpointDatabase, startCheckpointId }; + CheckpointDatabase::IdType checkpointId = m_checkpointDatabase->AddCheckpoint(checkpointName); + Checkpoint checkpoint{ m_checkpointDatabase, checkpointId }; return checkpoint; } @@ -114,7 +114,7 @@ namespace AppInstaller::Checkpoints std::vector> checkpoints; for (const auto& checkpointId : checkpointIds) { - checkpoints.emplace_back(Checkpoint{ std::move(m_checkpointDatabase), checkpointId }); + checkpoints.emplace_back(Checkpoint{ m_checkpointDatabase, checkpointId }); } return checkpoints; diff --git a/src/AppInstallerCLICore/CheckpointManager.h b/src/AppInstallerCLICore/CheckpointManager.h index 33f09c08c7..dca4c934d9 100644 --- a/src/AppInstallerCLICore/CheckpointManager.h +++ b/src/AppInstallerCLICore/CheckpointManager.h @@ -30,13 +30,13 @@ namespace AppInstaller::Checkpoints // Gets the automatic checkpoint. Checkpoint GetAutomaticCheckpoint(); - // Creates a new automatic checkpoint. + // Creates an automatic checkpoint using the provided context. void CreateAutomaticCheckpoint(CLI::Execution::Context& context); - // Gets all data checkpoints. + // Gets all context data checkpoints. std::vector> GetCheckpoints(); - // Creates a new data checkpoint. + // Creates a new context data checkpoint. Checkpoint CreateCheckpoint(std::string_view checkpointName); // Cleans up the checkpoint database. diff --git a/src/AppInstallerCLICore/Commands/PinCommand.cpp b/src/AppInstallerCLICore/Commands/PinCommand.cpp index 8ebf05249c..2755846db1 100644 --- a/src/AppInstallerCLICore/Commands/PinCommand.cpp +++ b/src/AppInstallerCLICore/Commands/PinCommand.cpp @@ -5,6 +5,7 @@ #include "Workflows/CompletionFlow.h" #include "Workflows/PinFlow.h" #include "Workflows/WorkflowBase.h" +#include "Workflows/ResumeFlow.h" #include "Resources.h" namespace AppInstaller::CLI @@ -130,6 +131,7 @@ namespace AppInstaller::CLI } context << + Workflow::Checkpoint("exampleCheckpoint", {}) << // TODO: Checkpoint example Workflow::SearchSourceForSingle << Workflow::HandleSearchResultFailures << Workflow::EnsureOneMatchFromSearchResult(OperationType::Pin) << diff --git a/src/AppInstallerCLICore/Commands/ResumeCommand.cpp b/src/AppInstallerCLICore/Commands/ResumeCommand.cpp index 2b9478602f..152a175f72 100644 --- a/src/AppInstallerCLICore/Commands/ResumeCommand.cpp +++ b/src/AppInstallerCLICore/Commands/ResumeCommand.cpp @@ -12,6 +12,41 @@ using namespace AppInstaller::Checkpoints; namespace AppInstaller::CLI { + namespace + { + std::unique_ptr FindCommandToResume(const std::string& commandFullName) + { + std::unique_ptr commandToResume = std::make_unique(); + + for (const auto& commandPart : Utility::Split(commandFullName, ':')) + { + bool commandFound = false; + if (Utility::CaseInsensitiveEquals(commandPart, commandToResume->Name())) + { + // Since we always expect to start at the 'root' command, skip and check the next command part. + continue; + } + + for (auto& command : commandToResume->GetCommands()) + { + if (Utility::CaseInsensitiveEquals(commandPart, command->Name())) + { + commandFound = true; + commandToResume = std::move(command); + break; + } + } + + if (!commandFound) + { + THROW_HR_MSG(E_UNEXPECTED, "Command to resume not found."); + } + } + + return std::move(commandToResume); + } + } + using namespace std::string_view_literals; using namespace Execution; @@ -65,27 +100,8 @@ namespace AppInstaller::CLI } const auto& checkpointCommand = automaticCheckpoint.Get(AutomaticCheckpointData::Command, {}); - AICLI_LOG(CLI, Info, << "Resuming command: " << checkpointCommand); - std::unique_ptr commandToResume; - std::unique_ptr currentCommand = std::make_unique(); - - // TODO: Handle command parsing for multiple commands - for (const auto& checkpointCommandPart : Utility::Split(checkpointCommand, ' ')) - { - for (auto& command : currentCommand->GetCommands()) - { - if (Utility::CaseInsensitiveEquals(checkpointCommandPart, command->FullName())) - { - currentCommand = std::move(command); - break; - } - } - } - - commandToResume = std::move(currentCommand); - - THROW_HR_IF_MSG(E_UNEXPECTED, !commandToResume, "Command to resume not found."); + std::unique_ptr commandToResume = FindCommandToResume(checkpointCommand); for (const auto& fieldName : automaticCheckpoint.GetFieldNames(AutomaticCheckpointData::Arguments)) { diff --git a/src/AppInstallerCLICore/ExecutionContext.cpp b/src/AppInstallerCLICore/ExecutionContext.cpp index 3a57761af0..9918a77c24 100644 --- a/src/AppInstallerCLICore/ExecutionContext.cpp +++ b/src/AppInstallerCLICore/ExecutionContext.cpp @@ -271,7 +271,7 @@ namespace AppInstaller::CLI::Execution Context Context::CreateEmptyContext() { - AppInstaller::ThreadLocalStorage::WingetThreadGlobals threadGlobals{ m_threadGlobals, ThreadLocalStorage::WingetThreadGlobals::create_sub_thread_globals_t{} }; + AppInstaller::ThreadLocalStorage::WingetThreadGlobals threadGlobals; return Context(Reporter, threadGlobals); } diff --git a/src/AppInstallerCLICore/Resources.h b/src/AppInstallerCLICore/Resources.h index c3794e9e87..95fc08f72f 100644 --- a/src/AppInstallerCLICore/Resources.h +++ b/src/AppInstallerCLICore/Resources.h @@ -288,7 +288,6 @@ namespace AppInstaller::CLI::Resource WINGET_DEFINE_RESOURCE_STRINGID(InvalidJsonFile); WINGET_DEFINE_RESOURCE_STRINGID(InvalidNameError); WINGET_DEFINE_RESOURCE_STRINGID(InvalidPathToNestedInstaller); - WINGET_DEFINE_RESOURCE_STRINGID(InvalidResumeIdError); WINGET_DEFINE_RESOURCE_STRINGID(KeyDirectoriesHeader); WINGET_DEFINE_RESOURCE_STRINGID(LicenseAgreement); WINGET_DEFINE_RESOURCE_STRINGID(Links); diff --git a/src/AppInstallerCLIE2ETests/ResumeCommand.cs b/src/AppInstallerCLIE2ETests/ResumeCommand.cs index b633210ffc..c20cf8f8be 100644 --- a/src/AppInstallerCLIE2ETests/ResumeCommand.cs +++ b/src/AppInstallerCLIE2ETests/ResumeCommand.cs @@ -58,14 +58,13 @@ public void InstallExe_VerifyIndexDoesNotExist() } /// - /// Verifies that an error message is shown when an invalid resume id is provided. + /// Verifies that an error message is shown when a resume id does not exist. /// [Test] - public void InvalidResumeId() + public void ResumeIdNotFound() { - var invalidResumeId = "invalidResumeId"; - var resumeResult = TestCommon.RunAICLICommand("resume", $"-g {invalidResumeId}"); - Assert.AreEqual(Constants.ErrorCode.ERROR_INVALID_CL_ARGUMENTS, resumeResult.ExitCode); + var resumeResult = TestCommon.RunAICLICommand("resume", "-g invalidResumeId"); + Assert.AreEqual(Constants.ErrorCode.ERROR_RESUME_ID_NOT_FOUND, resumeResult.ExitCode); } /// diff --git a/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw b/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw index 72c206425a..fc4f81290c 100644 --- a/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw +++ b/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw @@ -2092,9 +2092,6 @@ Please specify one of them using the --source option to proceed. The unique identifier of the saved state to resume - - The resume id string provided is invalid: {0} - Resuming the state from a different client version is not supported: {0} {Locked= "{0}"} Message displayed to inform the user that the client version of the resume state does not match the current client version. {0} is a placeholder for the client version that created the resume state. diff --git a/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj b/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj index 587c360a91..20c0fd222e 100644 --- a/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj +++ b/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj @@ -198,7 +198,7 @@ - + diff --git a/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj.filters b/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj.filters index 3ffaebee8f..71ed2a293f 100644 --- a/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj.filters +++ b/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj.filters @@ -317,7 +317,7 @@ Source Files\CLI - + Source Files\Repository diff --git a/src/AppInstallerCLITests/CheckpointDatabase.cpp b/src/AppInstallerCLITests/CheckpointDatabase.cpp new file mode 100644 index 0000000000..fb13ae282e --- /dev/null +++ b/src/AppInstallerCLITests/CheckpointDatabase.cpp @@ -0,0 +1,129 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#include "pch.h" +#include "TestCommon.h" +#include +#include + +using namespace std::string_literals; +using namespace TestCommon; +using namespace AppInstaller::Repository::Microsoft; +using namespace AppInstaller::Repository::SQLite; +using namespace AppInstaller::Repository::Microsoft::Schema; +using namespace AppInstaller::Checkpoints; + +TEST_CASE("CheckpointDatabaseCreateLatestAndReopen", "[checkpointDatabase]") +{ + TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; + INFO("Using temporary file named: " << tempFile.GetPath()); + + Schema::Version versionCreated; + + // Create the database + { + std::shared_ptr database = CheckpointDatabase::CreateNew(tempFile, Schema::Version::Latest()); + versionCreated = database->GetVersion(); + } + + // Reopen the database + { + INFO("Trying with ReadWrite"); + std::shared_ptr database = CheckpointDatabase::Open(tempFile, SQLiteStorageBase::OpenDisposition::ReadWrite); + Schema::Version versionRead = database->GetVersion(); + REQUIRE(versionRead == versionCreated); + } +} + +TEST_CASE("CheckpointDatabase_WriteMetadata", "[checkpointDatabase]") +{ + TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; + INFO("Using temporary file named: " << tempFile.GetPath()); + + std::string_view testCheckpointName = "testCheckpoint"sv; + std::string testCommand = "install"; + std::string testClientVersion = "1.20.1234"; + + { + std::shared_ptr database = CheckpointDatabase::CreateNew(tempFile, { 1, 0 }); + CheckpointDatabase::IdType checkpointId = database->AddCheckpoint(testCheckpointName); + database->SetDataValue(checkpointId, AutomaticCheckpointData::Command, {}, { testCommand }); + database->SetDataValue(checkpointId, AutomaticCheckpointData::ClientVersion, {}, { testClientVersion }); + } + + { + std::shared_ptr database = CheckpointDatabase::Open(tempFile); + const auto& checkpointIds = database->GetCheckpointIds(); + REQUIRE_FALSE(checkpointIds.empty()); + + auto checkpointId = checkpointIds[0]; + REQUIRE(testCommand == database->GetDataFieldSingleValue(checkpointId, AutomaticCheckpointData::Command, {})); + REQUIRE(testClientVersion == database->GetDataFieldSingleValue(checkpointId, AutomaticCheckpointData::ClientVersion, {})); + } +} + +TEST_CASE("CheckpointDatabase_WriteContextData", "[checkpointDatabase]") +{ + TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; + INFO("Using temporary file named: " << tempFile.GetPath()); + + std::string_view testCheckpoint = "testCheckpoint"sv; + + std::string fieldName1 = "field1"; + std::string fieldName2 = "field2"; + + std::string testValue1 = "value1"; + std::string testValue2 = "value2"; + std::string testValue3 = "value3"; + + { + std::shared_ptr database = CheckpointDatabase::CreateNew(tempFile, { 1, 0 }); + CheckpointDatabase::IdType checkpointId = database->AddCheckpoint(testCheckpoint); + + // Add multiple fields. + database->SetDataValue(checkpointId, AutomaticCheckpointData::Arguments, fieldName1, { testValue1 }); + database->SetDataValue(checkpointId, AutomaticCheckpointData::Arguments, fieldName2, { testValue2, testValue3 }); + } + + { + std::shared_ptr database = CheckpointDatabase::Open(tempFile); + const auto& checkpointIds = database->GetCheckpointIds(); + REQUIRE_FALSE(checkpointIds.empty()); + + auto automaticCheckpointId = checkpointIds.back(); + + const auto& fieldNames = database->GetDataFieldNames(automaticCheckpointId, AutomaticCheckpointData::Arguments); + + REQUIRE(fieldNames[0] == fieldName1); + REQUIRE(fieldNames[1] == fieldName2); + + REQUIRE(testValue1 == database->GetDataFieldSingleValue(automaticCheckpointId, AutomaticCheckpointData::Arguments, fieldName1)); + + const auto& multiValues = database->GetDataFieldMultiValue(automaticCheckpointId, AutomaticCheckpointData::Arguments, fieldName2); + + REQUIRE(testValue2 == multiValues[0]); + REQUIRE(testValue3 == multiValues[1]); + } +} + +TEST_CASE("CheckpointDatabase_CheckpointOrder", "[checkpointDatabase]") +{ + // Verifies that the checkpoints are shown in reverse order (latest first). + TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; + INFO("Using temporary file named: " << tempFile.GetPath()); + + { + std::shared_ptr database = CheckpointDatabase::CreateNew(tempFile, { 1, 0 }); + database->AddCheckpoint("firstCheckpoint"sv); + database->AddCheckpoint("secondCheckpoint"sv); + database->AddCheckpoint("thirdCheckpoint"sv); + } + + { + std::shared_ptr database = CheckpointDatabase::Open(tempFile); + const auto& checkpointIds = database->GetCheckpointIds(); + REQUIRE(checkpointIds.size() == 3); + REQUIRE(checkpointIds[0] == 3); + REQUIRE(checkpointIds[1] == 2); + REQUIRE(checkpointIds[2] == 1); + } +} diff --git a/src/AppInstallerCLITests/CheckpointRecord.cpp b/src/AppInstallerCLITests/CheckpointRecord.cpp deleted file mode 100644 index 024058d699..0000000000 --- a/src/AppInstallerCLITests/CheckpointRecord.cpp +++ /dev/null @@ -1,129 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. -#include "pch.h" -#include "TestCommon.h" -#include -#include - -using namespace std::string_literals; -using namespace TestCommon; -using namespace AppInstaller::Repository::Microsoft; -using namespace AppInstaller::Repository::SQLite; -using namespace AppInstaller::Repository::Microsoft::Schema; -using namespace AppInstaller::Checkpoints; - -TEST_CASE("CheckpointRecordCreateLatestAndReopen", "[checkpointRecord]") -{ - TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; - INFO("Using temporary file named: " << tempFile.GetPath()); - - Schema::Version versionCreated; - - // Create the record - { - CheckpointRecord record = CheckpointRecord::CreateNew(tempFile, Schema::Version::Latest()); - versionCreated = record.GetVersion(); - } - - // Reopen the record - { - INFO("Trying with ReadWrite"); - CheckpointRecord record = CheckpointRecord::Open(tempFile, SQLiteStorageBase::OpenDisposition::ReadWrite); - Schema::Version versionRead = record.GetVersion(); - REQUIRE(versionRead == versionCreated); - } -} - -TEST_CASE("CheckpointRecord_WriteMetadata", "[checkpointRecord]") -{ - TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; - INFO("Using temporary file named: " << tempFile.GetPath()); - - std::string_view testCheckpointName = "testCheckpoint"sv; - std::string testCommand = "install"; - std::string testClientVersion = "1.20.1234"; - - { - CheckpointRecord record = CheckpointRecord::CreateNew(tempFile, { 1, 0 }); - CheckpointRecord::IdType checkpointId = record.AddCheckpoint(testCheckpointName); - record.SetDataValue(checkpointId, AutomaticCheckpointData::CommandName, {}, { testCommand }); - record.SetDataValue(checkpointId, AutomaticCheckpointData::ClientVersion, {}, { testClientVersion }); - } - - { - CheckpointRecord record = CheckpointRecord::Open(tempFile); - std::optional checkpointId = record.GetCheckpointIdByName(testCheckpointName); - REQUIRE(checkpointId.has_value()); - REQUIRE(testCommand == record.GetDataSingleValue(checkpointId.value(), AutomaticCheckpointData::CommandName)); - REQUIRE(testClientVersion == record.GetDataSingleValue(checkpointId.value(), AutomaticCheckpointData::ClientVersion)); - } -} - -TEST_CASE("CheckpointRecord_WriteContextData", "[checkpointRecord]") -{ - TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; - INFO("Using temporary file named: " << tempFile.GetPath()); - - std::string_view testCheckpoint = "testCheckpoint"sv; - - std::string fieldName1 = "field1"; - std::string fieldName2 = "field2"; - - std::string testValue1 = "value1"; - std::string testValue2 = "value2"; - std::string testValue3 = "value3"; - - { - CheckpointRecord record = CheckpointRecord::CreateNew(tempFile, { 1, 0 }); - CheckpointRecord::IdType checkpointId = record.AddCheckpoint(testCheckpoint); - - // Add multiple fields. - record.SetDataValue(checkpointId, AutomaticCheckpointData::Arguments, fieldName1, { testValue1 }); - record.SetDataValue(checkpointId, AutomaticCheckpointData::Arguments, fieldName2, { testValue2, testValue3 }); - } - - { - CheckpointRecord record = CheckpointRecord::Open(tempFile); - std::optional checkpointId = record.GetCheckpointIdByName(testCheckpoint); - REQUIRE(checkpointId.has_value()); - - const auto& fieldNames = record.GetDataFieldNames(checkpointId.value(), AutomaticCheckpointData::Arguments); - - REQUIRE(fieldNames[0] == fieldName1); - REQUIRE(fieldNames[1] == fieldName2); - - REQUIRE(testValue1 == record.GetDataFieldSingleValue(checkpointId.value(), AutomaticCheckpointData::Arguments, fieldName1)); - - const auto& multiValues = record.GetDataFieldMultiValue(checkpointId.value(), AutomaticCheckpointData::Arguments, fieldName2); - - REQUIRE(testValue2 == multiValues[0]); - REQUIRE(testValue3 == multiValues[1]); - } -} - -TEST_CASE("CheckpointRecord_CheckpointOrder", "[checkpointRecord]") -{ - // Verifies that the checkpoints are shown in reverse order (latest first). - TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; - INFO("Using temporary file named: " << tempFile.GetPath()); - - std::string_view firstCheckpoint = "firstCheckpoint"sv; - std::string_view secondCheckpoint = "secondCheckpoint"sv; - std::string_view thirdCheckpoint = "thirdCheckpoint"sv; - - { - CheckpointRecord record = CheckpointRecord::CreateNew(tempFile, { 1, 0 }); - record.AddCheckpoint(firstCheckpoint); - record.AddCheckpoint(secondCheckpoint); - record.AddCheckpoint(thirdCheckpoint); - } - - { - CheckpointRecord record = CheckpointRecord::Open(tempFile); - const auto& checkpoints = record.GetCheckpoints(); - - REQUIRE(checkpoints[0] == thirdCheckpoint); - REQUIRE(checkpoints[1] == secondCheckpoint); - REQUIRE(checkpoints[2] == firstCheckpoint); - } -} diff --git a/src/AppInstallerCLITests/ResumeFlow.cpp b/src/AppInstallerCLITests/ResumeFlow.cpp index 2ed647396d..a9cf8f314f 100644 --- a/src/AppInstallerCLITests/ResumeFlow.cpp +++ b/src/AppInstallerCLITests/ResumeFlow.cpp @@ -5,7 +5,6 @@ #include "TestHooks.h" #include #include -#include #include #include #include @@ -58,9 +57,9 @@ TEST_CASE("ResumeFlow_InvalidClientVersion", "[Resume]") { // Manually set invalid client version std::filesystem::create_directories(tempRecordPath.parent_path()); - CheckpointDatabase checkpointRecord = CheckpointDatabase::CreateNew(tempRecordPath.u8string()); - CheckpointDatabase::IdType checkpointId = checkpointRecord.AddCheckpoint(s_AutomaticCheckpoint); - checkpointRecord.SetDataValue(checkpointId, AutomaticCheckpointData::ClientVersion, {}, { "1.2.3.4" }); + std::shared_ptr checkpointRecord = CheckpointDatabase::CreateNew(tempRecordPath.u8string()); + CheckpointDatabase::IdType checkpointId = checkpointRecord->AddCheckpoint(s_AutomaticCheckpoint); + checkpointRecord->SetDataValue(checkpointId, AutomaticCheckpointData::ClientVersion, {}, { "1.2.3.4" }); } std::ostringstream resumeOutput; @@ -92,7 +91,7 @@ TEST_CASE("ResumeFlow_EmptyIndex", "[Resume]") { std::filesystem::create_directories(tempRecordPath.parent_path()); - CheckpointDatabase checkpointRecord = CheckpointDatabase::CreateNew(tempRecordPath.u8string()); + CheckpointDatabase::CreateNew(tempRecordPath.u8string()); } std::ostringstream resumeOutput; diff --git a/src/AppInstallerCLITests/Strings.cpp b/src/AppInstallerCLITests/Strings.cpp index 74cb68a7c4..345fb9a499 100644 --- a/src/AppInstallerCLITests/Strings.cpp +++ b/src/AppInstallerCLITests/Strings.cpp @@ -282,23 +282,8 @@ TEST_CASE("SplitWithSeparator", "[string]") TEST_CASE("ConvertGuid", "[string]") { - std::string validGuidString1 = "{4d1e55b2-f16f-11cf-88cb-001111000030}"; - std::string validGuidString2 = "4d1e55b2-f16f-11cf-88cb-001111000030"; - std::string invalidGuidString1 = "4d1e55b2-f16f-11cf-001111000030"; // Not the correct length. - std::string invalidGuidString2 = "4d1e55b2f16f11cf88cb001111000030"; // Missing hyphens. - std::string invalidGuidString3 = "fakeGuid"; - + std::string validGuidString = "{4d1e55b2-f16f-11cf-88cb-001111000030}"; GUID guid = { 0x4d1e55b2, 0xf16f, 0x11cf, 0x88, 0xcb, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30 }; - REQUIRE(IsValidGuidString(validGuidString1)); - REQUIRE(IsValidGuidString(validGuidString2)); - REQUIRE_FALSE(IsValidGuidString(invalidGuidString1)); - REQUIRE_FALSE(IsValidGuidString(invalidGuidString2)); - REQUIRE_FALSE(IsValidGuidString(invalidGuidString3)); - - REQUIRE(ConvertToGuid(validGuidString1) == guid); - REQUIRE(ConvertToGuid(validGuidString2) == guid); - REQUIRE_THROWS(ConvertToGuid(invalidGuidString1)); - REQUIRE_THROWS(ConvertToGuid(invalidGuidString2)); - REQUIRE_THROWS(ConvertToGuid(invalidGuidString3)); + REQUIRE(CaseInsensitiveEquals(ConvertGuidToString(guid), validGuidString)); } diff --git a/src/AppInstallerRepositoryCore/Microsoft/CheckpointDatabase.cpp b/src/AppInstallerRepositoryCore/Microsoft/CheckpointDatabase.cpp index 93073366d9..832af6bd45 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/CheckpointDatabase.cpp +++ b/src/AppInstallerRepositoryCore/Microsoft/CheckpointDatabase.cpp @@ -35,7 +35,7 @@ namespace AppInstaller::Repository::Microsoft std::lock_guard lockInterface{ *m_interfaceLock }; AICLI_LOG(Repo, Verbose, << "Adding checkpoint [" << checkpointName << "]"); - SQLite::Savepoint savepoint = SQLite::Savepoint::Create(m_dbconn, "CheckpointDatabase_addcheckpoint"); + SQLite::Savepoint savepoint = SQLite::Savepoint::Create(m_dbconn, "CheckpointDatabase_addCheckpoint"); IdType result = m_interface->AddCheckpoint(m_dbconn, checkpointName); @@ -49,7 +49,7 @@ namespace AppInstaller::Repository::Microsoft return m_interface->GetCheckpointIds(m_dbconn); } - bool CheckpointDatabase::HasDataField(IdType checkpointId, int type, std::string name) + bool CheckpointDatabase::HasDataField(IdType checkpointId, int type, const std::string& name) { return m_interface->GetCheckpointDataFieldValues(m_dbconn, checkpointId, type, name).has_value(); } @@ -64,7 +64,7 @@ namespace AppInstaller::Repository::Microsoft return m_interface->GetCheckpointDataFields(m_dbconn, checkpointId, dataType); } - void CheckpointDatabase::SetDataValue(IdType checkpointId, int dataType, std::string field, std::vector values) + void CheckpointDatabase::SetDataValue(IdType checkpointId, int dataType, const std::string& field, const std::vector& values) { std::lock_guard lockInterface{ *m_interfaceLock }; AICLI_LOG(Repo, Verbose, << "Setting checkpoint data [" << dataType << "]"); @@ -77,7 +77,7 @@ namespace AppInstaller::Repository::Microsoft savepoint.Commit(); } - std::string CheckpointDatabase::GetDataFieldSingleValue(IdType checkpointId, int dataType, std::string_view field) + std::string CheckpointDatabase::GetDataFieldSingleValue(IdType checkpointId, int dataType, const std::string& field) { const auto& values = m_interface->GetCheckpointDataFieldValues(m_dbconn, checkpointId, dataType, field); @@ -89,7 +89,7 @@ namespace AppInstaller::Repository::Microsoft return values.value()[0]; } - std::vector CheckpointDatabase::GetDataFieldMultiValue(IdType checkpointId, int dataType, std::string field) + std::vector CheckpointDatabase::GetDataFieldMultiValue(IdType checkpointId, int dataType, const std::string& field) { const auto& values = m_interface->GetCheckpointDataFieldValues(m_dbconn, checkpointId, dataType, field); diff --git a/src/AppInstallerRepositoryCore/Microsoft/CheckpointDatabase.h b/src/AppInstallerRepositoryCore/Microsoft/CheckpointDatabase.h index d5f0b4698a..262a479f64 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/CheckpointDatabase.h +++ b/src/AppInstallerRepositoryCore/Microsoft/CheckpointDatabase.h @@ -39,7 +39,7 @@ namespace AppInstaller::Repository::Microsoft std::vector GetCheckpointIds(); // Returns a boolean value indicating a field exists for a checkpoint data type. - bool HasDataField(IdType checkpointId, int type, std::string name); + bool HasDataField(IdType checkpointId, int type, const std::string& name); // Returns the available data types for a checkpoint id. std::vector GetDataTypes(IdType checkpointId); @@ -48,13 +48,13 @@ namespace AppInstaller::Repository::Microsoft std::vector GetDataFieldNames(IdType checkpointId, int dataType); // Sets the value(s) for a data type and field. - void SetDataValue(IdType checkpointId, int dataType, std::string field, std::vector values); + void SetDataValue(IdType checkpointId, int dataType, const std::string& field, const std::vector& values); // Gets a single value for a data type field. - std::string GetDataFieldSingleValue(IdType checkpointId, int dataType, std::string_view field); + std::string GetDataFieldSingleValue(IdType checkpointId, int dataType, const std::string& field); // Gets multiple values for a data type field. - std::vector GetDataFieldMultiValue(IdType checkpointId, int dataType, std::string field); + std::vector GetDataFieldMultiValue(IdType checkpointId, int dataType, const std::string& field); private: // Constructor used to open an existing index. diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointDatabaseInterface.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointDatabaseInterface.h index c05a217ec3..61ac3d768e 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointDatabaseInterface.h +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointDatabaseInterface.h @@ -16,8 +16,8 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 SQLite::rowid_t AddCheckpoint(SQLite::Connection& connection, std::string_view checkpointName) override; std::vector GetCheckpointIds(SQLite::Connection& connection) override; std::vector GetCheckpointDataTypes(SQLite::Connection& connection, SQLite::rowid_t checkpointId) override; - void SetCheckpointDataValues(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int dataType, std::string_view name, std::vector values) override; std::vector GetCheckpointDataFields(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int dataType) override; + void SetCheckpointDataValues(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int dataType, std::string_view name, std::vector values) override; std::optional> GetCheckpointDataFieldValues(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int dataType, std::string_view name) override; }; } \ No newline at end of file diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointTable.cpp b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointTable.cpp index 90c8ffcc76..6fdb60dd75 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointTable.cpp +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointTable.cpp @@ -24,11 +24,12 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "createCheckpointTable_v1_0"); StatementBuilder createTableBuilder; - createTableBuilder.CreateTable(s_CheckpointTable_Table_Name).BeginColumns(); - createTableBuilder.Column(ColumnBuilder(s_CheckpointTable_Index_Name, Type::RowId).PrimaryKey()); - createTableBuilder.Column(ColumnBuilder(s_CheckpointTable_Name_Column, Type::Text)); - createTableBuilder.Column(ColumnBuilder(s_CheckpointTable_CreationTime_Column, Type::Int64)); - createTableBuilder.EndColumns(); + createTableBuilder.CreateTable(s_CheckpointTable_Table_Name).Columns({ + ColumnBuilder(s_CheckpointTable_Index_Name, Type::Integer).PrimaryKey(), + ColumnBuilder(s_CheckpointTable_Name_Column, Type::Text).NotNull(), + ColumnBuilder(s_CheckpointTable_CreationTime_Column, Type::Int64).NotNull() + }); + createTableBuilder.Execute(connection); savepoint.Commit(); diff --git a/src/AppInstallerRepositoryCore/SQLiteStatementBuilder.cpp b/src/AppInstallerRepositoryCore/SQLiteStatementBuilder.cpp index ee3fd2b4bc..f24c51ebcd 100644 --- a/src/AppInstallerRepositoryCore/SQLiteStatementBuilder.cpp +++ b/src/AppInstallerRepositoryCore/SQLiteStatementBuilder.cpp @@ -139,6 +139,9 @@ namespace AppInstaller::Repository::SQLite::Builder case Type::Blob: out << "BLOB"; break; + case Type::Integer: + out << "INTEGER"; + break; default: THROW_HR(E_UNEXPECTED); } diff --git a/src/AppInstallerRepositoryCore/SQLiteStatementBuilder.h b/src/AppInstallerRepositoryCore/SQLiteStatementBuilder.h index a108301cce..89b0f821a7 100644 --- a/src/AppInstallerRepositoryCore/SQLiteStatementBuilder.h +++ b/src/AppInstallerRepositoryCore/SQLiteStatementBuilder.h @@ -111,6 +111,7 @@ namespace AppInstaller::Repository::SQLite::Builder RowId = Int64, Text, Blob, + Integer, // Type for specifying a primary key column as a row id alias. }; // Aggregate functions. From d73ef7d373432453487f089b9d1bbecb5acfb9dd Mon Sep 17 00:00:00 2001 From: Ryan Fu Date: Wed, 20 Sep 2023 15:32:56 -0700 Subject: [PATCH 39/43] fix path --- src/AppInstallerCLICore/CheckpointManager.cpp | 4 ++-- src/AppInstallerCLICore/CheckpointManager.h | 2 +- src/AppInstallerCommonCore/Runtime.cpp | 10 ++++------ 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/AppInstallerCLICore/CheckpointManager.cpp b/src/AppInstallerCLICore/CheckpointManager.cpp index c3c1c6587b..311d78df63 100644 --- a/src/AppInstallerCLICore/CheckpointManager.cpp +++ b/src/AppInstallerCLICore/CheckpointManager.cpp @@ -85,12 +85,12 @@ namespace AppInstaller::Checkpoints } } - Checkpoint CheckpointManager::GetAutomaticCheckpoint() + std::optional> CheckpointManager::GetAutomaticCheckpoint() { const auto& checkpointIds = m_checkpointDatabase->GetCheckpointIds(); if (checkpointIds.empty()) { - THROW_HR(E_UNEXPECTED); + return {}; } CheckpointDatabase::IdType automaticCheckpointId = checkpointIds.back(); diff --git a/src/AppInstallerCLICore/CheckpointManager.h b/src/AppInstallerCLICore/CheckpointManager.h index dca4c934d9..03029862c5 100644 --- a/src/AppInstallerCLICore/CheckpointManager.h +++ b/src/AppInstallerCLICore/CheckpointManager.h @@ -28,7 +28,7 @@ namespace AppInstaller::Checkpoints static std::filesystem::path GetCheckpointDatabasePath(const std::string_view& resumeId, bool createCheckpointDirectory = false); // Gets the automatic checkpoint. - Checkpoint GetAutomaticCheckpoint(); + std::optional> GetAutomaticCheckpoint(); // Creates an automatic checkpoint using the provided context. void CreateAutomaticCheckpoint(CLI::Execution::Context& context); diff --git a/src/AppInstallerCommonCore/Runtime.cpp b/src/AppInstallerCommonCore/Runtime.cpp index 560f6bdaaa..d25220994e 100644 --- a/src/AppInstallerCommonCore/Runtime.cpp +++ b/src/AppInstallerCommonCore/Runtime.cpp @@ -469,7 +469,7 @@ namespace AppInstaller::Runtime result.Create = false; break; case PathName::CheckpointsLocation: - result.Path = GetPathTo(PathName::LocalState) / s_CheckpointsDirectory; + result.Path = GetPathTo(PathName::LocalState, forDisplay) / s_CheckpointsDirectory; break; default: THROW_HR(E_UNEXPECTED); @@ -506,17 +506,12 @@ namespace AppInstaller::Runtime } } break; - case PathName::CheckpointsLocation: case PathName::LocalState: result.Path = GetPathToAppDataDir(s_AppDataDir_State, forDisplay); result.Path /= GetRuntimePathStateName(); result.SetOwner(ACEPrincipal::CurrentUser); result.ACL[ACEPrincipal::System] = ACEPermissions::All; result.ACL[ACEPrincipal::Admins] = ACEPermissions::All; - if (path == PathName::CheckpointsLocation) - { - result.Path /= s_CheckpointsDirectory; - } break; case PathName::StandardSettings: case PathName::UserFileSettings: @@ -562,6 +557,9 @@ namespace AppInstaller::Runtime result.Path = GetBinaryDirectoryPath(); result.Create = false; break; + case PathName::CheckpointsLocation: + result.Path = GetPathTo(PathName::LocalState, forDisplay) / s_CheckpointsDirectory; + break; default: THROW_HR(E_UNEXPECTED); } From ae2dda09b2847bcd73e4071f8e4c9cabde6dfac9 Mon Sep 17 00:00:00 2001 From: Ryan Fu Date: Thu, 21 Sep 2023 11:49:44 -0700 Subject: [PATCH 40/43] fix path recursive call --- src/AppInstallerCommonCore/Runtime.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/AppInstallerCommonCore/Runtime.cpp b/src/AppInstallerCommonCore/Runtime.cpp index d25220994e..9af6f62d3f 100644 --- a/src/AppInstallerCommonCore/Runtime.cpp +++ b/src/AppInstallerCommonCore/Runtime.cpp @@ -469,7 +469,8 @@ namespace AppInstaller::Runtime result.Create = false; break; case PathName::CheckpointsLocation: - result.Path = GetPathTo(PathName::LocalState, forDisplay) / s_CheckpointsDirectory; + result = GetPathDetailsForPackagedContext(PathName::LocalState, forDisplay); + result.Path /= s_CheckpointsDirectory; break; default: THROW_HR(E_UNEXPECTED); @@ -558,7 +559,8 @@ namespace AppInstaller::Runtime result.Create = false; break; case PathName::CheckpointsLocation: - result.Path = GetPathTo(PathName::LocalState, forDisplay) / s_CheckpointsDirectory; + result = GetPathDetailsForUnpackagedContext(PathName::LocalState, forDisplay); + result.Path /= s_CheckpointsDirectory; break; default: THROW_HR(E_UNEXPECTED); From a3e72c73d0d61b0fef42f5e5a5870c83886b181b Mon Sep 17 00:00:00 2001 From: Ryan Fu Date: Thu, 21 Sep 2023 12:59:11 -0700 Subject: [PATCH 41/43] minor fix --- src/AppInstallerCLICore/Commands/PinCommand.cpp | 2 -- src/AppInstallerCLIE2ETests/FeaturesCommand.cs | 1 + src/AppInstallerCLIE2ETests/Helpers/WinGetSettingsHelper.cs | 5 ++++- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/AppInstallerCLICore/Commands/PinCommand.cpp b/src/AppInstallerCLICore/Commands/PinCommand.cpp index 2755846db1..8ebf05249c 100644 --- a/src/AppInstallerCLICore/Commands/PinCommand.cpp +++ b/src/AppInstallerCLICore/Commands/PinCommand.cpp @@ -5,7 +5,6 @@ #include "Workflows/CompletionFlow.h" #include "Workflows/PinFlow.h" #include "Workflows/WorkflowBase.h" -#include "Workflows/ResumeFlow.h" #include "Resources.h" namespace AppInstaller::CLI @@ -131,7 +130,6 @@ namespace AppInstaller::CLI } context << - Workflow::Checkpoint("exampleCheckpoint", {}) << // TODO: Checkpoint example Workflow::SearchSourceForSingle << Workflow::HandleSearchResultFailures << Workflow::EnsureOneMatchFromSearchResult(OperationType::Pin) << diff --git a/src/AppInstallerCLIE2ETests/FeaturesCommand.cs b/src/AppInstallerCLIE2ETests/FeaturesCommand.cs index 04770c4690..5c0d47bd2d 100644 --- a/src/AppInstallerCLIE2ETests/FeaturesCommand.cs +++ b/src/AppInstallerCLIE2ETests/FeaturesCommand.cs @@ -54,6 +54,7 @@ public void EnableExperimentalFeatures() WinGetSettingsHelper.ConfigureFeature("experimentalCmd", true); WinGetSettingsHelper.ConfigureFeature("directMSI", true); WinGetSettingsHelper.ConfigureFeature("windowsFeature", true); + WinGetSettingsHelper.ConfigureFeature("resume", true); var result = TestCommon.RunAICLICommand("features", string.Empty); Assert.True(result.StdOut.Contains("Enabled")); } diff --git a/src/AppInstallerCLIE2ETests/Helpers/WinGetSettingsHelper.cs b/src/AppInstallerCLIE2ETests/Helpers/WinGetSettingsHelper.cs index 8207b09941..b3f4be3f63 100644 --- a/src/AppInstallerCLIE2ETests/Helpers/WinGetSettingsHelper.cs +++ b/src/AppInstallerCLIE2ETests/Helpers/WinGetSettingsHelper.cs @@ -42,6 +42,8 @@ public static void InitializeWingetSettings() { "experimentalArg", false }, { "experimentalCmd", false }, { "directMSI", false }, + { "windowsFeature", false }, + { "resume", false }, } }, { @@ -206,7 +208,8 @@ public static void InitializeAllFeatures(bool status) ConfigureFeature("experimentalArg", status); ConfigureFeature("experimentalCmd", status); ConfigureFeature("directMSI", status); - ConfigureFeature("pinning", status); + ConfigureFeature("windowsFeature", status); + ConfigureFeature("resume", status); } private static JObject GetJsonSettingsObject(string objectName) From c133d520668862fe6bbe5204984aa8fdecbf0fca Mon Sep 17 00:00:00 2001 From: Ryan Fu Date: Mon, 25 Sep 2023 10:06:05 -0700 Subject: [PATCH 42/43] fix resume e2e test --- src/AppInstallerCLIE2ETests/ResumeCommand.cs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/AppInstallerCLIE2ETests/ResumeCommand.cs b/src/AppInstallerCLIE2ETests/ResumeCommand.cs index c20cf8f8be..ab6b84303f 100644 --- a/src/AppInstallerCLIE2ETests/ResumeCommand.cs +++ b/src/AppInstallerCLIE2ETests/ResumeCommand.cs @@ -77,9 +77,10 @@ public void ResumeRecordPreserved() int initialCheckpointsCount = Directory.Exists(checkpointsDir) ? Directory.GetDirectories(checkpointsDir).Length : 0; - var installResult = TestCommon.RunAICLICommand("install", "--id AppInstallerTest.WindowsFeature"); - Assert.AreEqual(Constants.ErrorCode.ERROR_INSTALL_MISSING_DEPENDENCY, installResult.ExitCode); - Assert.True(installResult.StdOut.Contains("The feature [invalidFeature] was not found.")); + // TODO: Refine test case to more accurately reflect usage once resume is fully implemented. + var result = TestCommon.RunAICLICommand("install", $"AppInstallerTest.TestZipInvalidRelativePath"); + Assert.AreNotEqual(Constants.ErrorCode.S_OK, result.ExitCode); + Assert.True(result.StdOut.Contains("Invalid relative file path to the nested installer; path points to a location outside of the install directory")); int actualCheckpointsCount = Directory.GetDirectories(checkpointsDir).Length; @@ -94,8 +95,8 @@ public void ResumeRecordPreserved() // Resume output should be the same as the install result. var resumeResult = TestCommon.RunAICLICommand("resume", $"-g {checkpoint.Name}"); - Assert.AreEqual(Constants.ErrorCode.ERROR_INSTALL_MISSING_DEPENDENCY, resumeResult.ExitCode); - Assert.True(resumeResult.StdOut.Contains("The feature [invalidFeature] was not found.")); + Assert.AreNotEqual(Constants.ErrorCode.S_OK, resumeResult.ExitCode); + Assert.True(resumeResult.StdOut.Contains("Invalid relative file path to the nested installer; path points to a location outside of the install directory")); } } } \ No newline at end of file From 792c465968e85a04ec7e55fb9bd37770a4d6e66f Mon Sep 17 00:00:00 2001 From: Ryan Fu Date: Tue, 10 Oct 2023 13:47:10 -0700 Subject: [PATCH 43/43] fix error code --- src/AppInstallerCLIE2ETests/Constants.cs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/AppInstallerCLIE2ETests/Constants.cs b/src/AppInstallerCLIE2ETests/Constants.cs index 199867c828..5c5e793278 100644 --- a/src/AppInstallerCLIE2ETests/Constants.cs +++ b/src/AppInstallerCLIE2ETests/Constants.cs @@ -255,10 +255,11 @@ public class ErrorCode public const int ERROR_APPTERMINATION_RECEIVED = unchecked((int)0x8A15006A); public const int ERROR_DOWNLOAD_DEPENDENCIES = unchecked((int)0x8A15006B); public const int ERROR_DOWNLOAD_COMMAND_PROHIBITED = unchecked((int)0x8A15006C); - public const int ERROR_RESUME_ID_NOT_FOUND = unchecked((int)0x8A15006D); - public const int ERROR_CLIENT_VERSION_MISMATCH = unchecked((int)0x8A15006E); - public const int ERROR_INVALID_RESUME_STATE = unchecked((int)0x8A15006F); - public const int ERROR_CANNOT_OPEN_CHECKPOINT_INDEX = unchecked((int)0x8A150070); + public const int ERROR_SERVICE_UNAVAILABLE = unchecked((int)0x8A15006D); + public const int ERROR_RESUME_ID_NOT_FOUND = unchecked((int)0x8A15006E); + public const int ERROR_CLIENT_VERSION_MISMATCH = unchecked((int)0x8A15006F); + public const int ERROR_INVALID_RESUME_STATE = unchecked((int)0x8A150070); + public const int ERROR_CANNOT_OPEN_CHECKPOINT_INDEX = unchecked((int)0x8A150071); public const int ERROR_INSTALL_PACKAGE_IN_USE = unchecked((int)0x8A150101); public const int ERROR_INSTALL_INSTALL_IN_PROGRESS = unchecked((int)0x8A150102);