 are being blocked by third-party party apps. Remove them manually and continue
TempNotEmpty                           = The "{0}" folder is not empty. Clear it manually
WindowsFeaturesTitle                   = Windows features
OptionalFeaturesTitle                  = Optional features
EnableHardwareVT                       = Enable Virtualization in UEFI
UserShellFolderNotEmpty                = Some files left in the "{0}" folder. Move them manually to a new location
RetrievingDrivesList                   = Retrieving drives list...
NewUserShellFolderLocation             = New location: "{0}"
DesktopDriveSelect                     = Select the drive within the root of which the Desktop folder will be created
DocumentsDriveSelect                   = Select the drive within the root of which the Documents folder will be created
DownloadsDriveSelect                   = Select the drive within the root of which the Downloads folder will be created
MusicDriveSelect                       = Select the drive within the root of which the Music folder will be created
PicturesDriveSelect                    = Select the drive within the root of which the Pictures folder will be created
VideosDriveSelect                      = Select the drive within the root of which the Videos folder will be created
DesktopRequest                         = Would you like to change the location of the Desktop folder?
DocumentsRequest                       = Would you like to change the location of the Documents folder?
DownloadsRequest                       = Would you like to change the location of the Downloads folder?
MusicRequest                           = Would you like to change the location of the Music folder?
PicturesRequest                        = Would you like to change the location of the Pictures folder?
VideosRequest                          = Would you like to change the location of the Videos folder?
DesktopFolderSelect                    = Select a folder for the Desktop folder
DocumentsFolderSelect                  = Select a folder for the Documents folder
DownloadsFolderSelect                  = Select a folder for the Downloads folder
MusicFolderSelect                      = Select a folder for the Music folder
PicturesFolderSelect                   = Select a folder for the Pictures folder
VideosFolderSelect                     = Select a folder for the Videos folder
DesktopDefaultFolder                   = Would you like to change the location of the Desktop folder to the default value?
DocumentsDefaultFolder                 = Would you like to change the location of the Documents folder to the default value?
DownloadsDefaultFolder                 = Would you like to change the location of the Downloads folder to the default value?
MusicDefaultFolder                     = Would you like to change the location of the Music folder to the default value?
PicturesDefaultFolder                  = Would you like to change the location of the Pictures folder to the default value?
VideosDefaultFolder                    = Would you like to change the location of the Videos folder to the default value?
ReservedStorageIsInUse                 = This operation is not supported when reserved storage is in use\nPlease wait for any servicing operations to complete and then try again later
ShortcutPinning                        = The "{0}" shortcut is being pinned to Start
UninstallUWPForAll                     = For All Users
UninstallUWPTitle                      = UWP Packages to Uninstall
WSLUpdateDownloading                   = Downloading the Linux kernel update package... ~14 MB
WSLUpdateInstalling                    = Installing the Linux kernel update package...
GraphicsPerformanceTitle               = Graphics performance preference
GraphicsPerformanceRequest             = Would you like to set the graphics performance setting of an app of your choice to "High performance"?
CleanUpTaskToast                       = Cleaning up unused Windows files and updates starts in a minute
CleanUpTaskDescription                 = Cleaning up unused Windows files and updates using built-in Disk cleanup app
FolderTaskDescription                  = The "{0}" folder cleaning
ControlledFolderAccess                 = Controlled folder access
ProtectedFoldersRequest                = Would you like to enable Controlled folder access and specify the folder that Microsoft Defender will protect from malicious apps and threats?
ProtectedFoldersListRemoved            = Removed folders
AppControlledFolderRequest             = Would you like to specify an app that is allowed through Controlled Folder access?
AllowedControlledFolderAppsRemoved     = Removed allowed apps
DefenderTitle                          = Microsoft Defender
DefenderExclusionFolderRequest         = Would you like to specify a folder to be excluded from Microsoft Defender malware scans?
DefenderExclusionFoldersListRemoved    = Excluded folders removed
AddDefenderExclusionFileRequest        = Would you like to specify a file to be excluded from Microsoft Defender malware scans?
DefenderExclusionFilesRemoved          = Excluded files removed
EventViewerCustomViewName              = Process Creation
EventViewerCustomViewDescription       = Process creation and command-line auditing events
RestartWarning                         = Make sure to restart your PC
ErrorsLine                             = Line
ErrorsFile                             = File
ErrorsMessage                          = Errors/Warnings
Add                                    = Add
AllFilesFilter                         = All Files (*.*)|*.*
Change                                 = Change
Continue                               = Continue
DialogBoxOpening                       = Displaying the dialog box...
Disable                                = Disable
Enable                                 = Enable
EXEFilesFilter                         = *.exe|*.exe|All Files (*.*)|*.*
FolderSelect                           = Select a folder
FilesWontBeMoved                       = Files will not be moved
NoData                                 = Nothing to display
NoInternetConnection                   = No Internet connection
Patient                                = Please wait...
Select                                 = Select
Skip                                   = Skip
Skipped                                = Skipped
SymbolicSkipped                        = Skipped creating a symbolic link
Uninstall                              = Uninstall
Install                                = Install
'@
<reponame>sjlbos/asherah
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using Amazon.DynamoDBv2;
using Amazon.DynamoDBv2.DocumentModel;
using Amazon.DynamoDBv2.Model;
using App.Metrics.Timer;
using GoDaddy.Asherah.AppEncryption.Util;
using GoDaddy.Asherah.Crypto.Exceptions;
using GoDaddy.Asherah.Logging;
using LanguageExt;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json.Linq;

[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")]
[assembly: InternalsVisibleTo("AppEncryption.Tests")]

namespace GoDaddy.Asherah.AppEncryption.Persistence
{
    public class DynamoDbMetastoreImpl : IMetastore<JObject>
    {
        internal const string TableName = "EncryptionKey";
        internal const string PartitionKey = "Id";
        internal const string SortKey = "Created";
        internal const string AttributeKeyRecord = "KeyRecord";

        private static readonly TimerOptions LoadTimerOptions = new TimerOptions { Name = MetricsUtil.AelMetricsPrefix + ".metastore.dynamodb.load" };
        private static readonly TimerOptions LoadLatestTimerOptions = new TimerOptions { Name = MetricsUtil.AelMetricsPrefix + ".metastore.dynamodb.loadlatest" };
        private static readonly TimerOptions StoreTimerOptions = new TimerOptions { Name = MetricsUtil.AelMetricsPrefix + ".metastore.dynamodb.store" };

        private static readonly ILogger Logger = LogManager.CreateLogger<DynamoDbMetastoreImpl>();

        // Note this instance is thread-safe
        private readonly Table table;

        internal DynamoDbMetastoreImpl(IAmazonDynamoDB dbClient)
        {
            // Note this results in a network call. For now, cleaner than refactoring w/ thread-safe lazy loading
            table = Table.LoadTable(dbClient, TableName);
        }

        public static Builder NewBuilder()
        {
            return new Builder();
        }

        public Option<JObject> Load(string keyId, DateTimeOffset created)
        {
            using (MetricsUtil.MetricsInstance.Measure.Timer.Time(LoadTimerOptions))
            {
                try
                {
                    GetItemOperationConfig config = new GetItemOperationConfig
                    {
                        AttributesToGet = new List<string> { AttributeKeyRecord },
                        ConsistentRead = true, // Always use strong consistency
                    };
                    Document result = table.GetItemAsync(keyId, created.ToUnixTimeSeconds(), config).Result;
                    if (result!= null)
                    {
                        // TODO Optimize Document to JObject conversion. Helper method could enumerate over Document KeyPairs
                        // and convert DynamoDBEntry values based on type inspection
                        return Option<JObject>.Some(JObject.Parse(result[AttributeKeyRecord].AsDocument().ToJson()));
                    }
                }
                catch (AggregateException ae)
                {
                    Logger.LogError(ae, "Metastore error");
                }

                return Option<JObject>.None;
            }
        }

        public Option<JObject> LoadLatest(string keyId)
        {
            using (MetricsUtil.MetricsInstance.Measure.Timer.Time(LoadLatestTimerOptions))
            {
                // Have to use query api to use limit and reverse sort order
                try
                