From f505f9443c0467d48f69175209d153de5a40a2ff Mon Sep 17 00:00:00 2001 From: Thomas Clark Date: Sun, 1 Dec 2024 08:51:30 +0000 Subject: [PATCH] feat: add export to json file --- ...ImportExportOperationInputRadioGroup.razor | 2 +- .../ImportExportTargetTypeInputSelect.razor | 33 ++++++ .../Components/Pages/ImportExport.razor | 106 +++++++++++++++++- .../Components/Pages/ImportExport.razor.js | 12 ++ 4 files changed, 149 insertions(+), 4 deletions(-) create mode 100644 src/AzureAppConfigurationEmulator/Components/ImportExportTargetTypeInputSelect.razor create mode 100644 src/AzureAppConfigurationEmulator/Components/Pages/ImportExport.razor.js diff --git a/src/AzureAppConfigurationEmulator/Components/ImportExportOperationInputRadioGroup.razor b/src/AzureAppConfigurationEmulator/Components/ImportExportOperationInputRadioGroup.razor index 9e755b2..45fd759 100644 --- a/src/AzureAppConfigurationEmulator/Components/ImportExportOperationInputRadioGroup.razor +++ b/src/AzureAppConfigurationEmulator/Components/ImportExportOperationInputRadioGroup.razor @@ -7,7 +7,7 @@ - +
Export
diff --git a/src/AzureAppConfigurationEmulator/Components/ImportExportTargetTypeInputSelect.razor b/src/AzureAppConfigurationEmulator/Components/ImportExportTargetTypeInputSelect.razor new file mode 100644 index 0000000..d9c536f --- /dev/null +++ b/src/AzureAppConfigurationEmulator/Components/ImportExportTargetTypeInputSelect.razor @@ -0,0 +1,33 @@ +@using System.Linq.Expressions + + + + + + + + +@code { + [Parameter(CaptureUnmatchedValues = true)] public IDictionary? AdditionalAttributes { get; set; } + + [Parameter] public string? Value { get; set; } + + [Parameter] public EventCallback ValueChanged { get; set; } + + [Parameter] public Expression>? ValueExpression { get; set; } + + private async Task HandleValueChanged(string? value) + { + await ValueChanged.InvokeAsync(!string.IsNullOrEmpty(value) ? value : null); + } + + public static class TargetType + { + public const string AzureAppConfiguration = nameof(AzureAppConfiguration); + + public const string AzureAppService = nameof(AzureAppService); + + public const string ConfigurationFile = nameof(ConfigurationFile); + } + +} \ No newline at end of file diff --git a/src/AzureAppConfigurationEmulator/Components/Pages/ImportExport.razor b/src/AzureAppConfigurationEmulator/Components/Pages/ImportExport.razor index 6e55e47..5d4c900 100644 --- a/src/AzureAppConfigurationEmulator/Components/Pages/ImportExport.razor +++ b/src/AzureAppConfigurationEmulator/Components/Pages/ImportExport.razor @@ -1,7 +1,11 @@ +@implements IAsyncDisposable @inject IConfigurationSettingFactory ConfigurationSettingFactory @inject IConfigurationSettingRepository ConfigurationSettingRepository +@inject IJSRuntime JS @inject IKeyValuePairJsonDecoder KeyValuePairJsonDecoder +@inject IKeyValuePairJsonEncoder KeyValuePairJsonEncoder @page "/kvdata" +@using System.Net @using System.Security.Cryptography @using System.Text @using System.Text.Json @@ -21,6 +25,61 @@ @switch (Model?.Operation) { + case ImportExportOperationInputRadioGroup.Operation.Export: +
+ + + +
+ + switch (Model.TargetType) + { + case ImportExportTargetTypeInputSelect.TargetType.ConfigurationFile: +
+
Export options
+ + +
+ + if (Model.FileFormat is not null) + { +
+
Apply changes to key-values
+ + + + +
+ +
+ Export +
+ } + + break; + } + + break; case ImportExportOperationInputRadioGroup.Operation.Import:
@@ -86,7 +145,7 @@
- @(Model.Operation switch { ImportExportOperationInputRadioGroup.Operation.Export => "Export", ImportExportOperationInputRadioGroup.Operation.Import => "Import", _ => throw new ArgumentOutOfRangeException() }) + Import
} @@ -148,7 +207,7 @@
- @(Model.Operation switch { ImportExportOperationInputRadioGroup.Operation.Export => "Export", ImportExportOperationInputRadioGroup.Operation.Import => "Import", _ => throw new ArgumentOutOfRangeException() }) + Import
} @@ -166,6 +225,24 @@ private ICollection Labels { get; } = []; + private IJSObjectReference? Module { get; set; } + + public async ValueTask DisposeAsync() + { + if (Module is not null) + { + await Module.DisposeAsync(); + } + } + + protected override async Task OnAfterRenderAsync(bool firstRender) + { + if (firstRender) + { + Module = await JS.InvokeAsync("import", "./Components/Pages/ImportExport.razor.js"); + } + } + protected override void OnInitialized() { Model ??= new InputModel(); @@ -218,6 +295,27 @@ switch (Model?.Operation) { + case ImportExportOperationInputRadioGroup.Operation.Export: + { + switch (Model?.TargetType) + { + case ImportExportTargetTypeInputSelect.TargetType.ConfigurationFile: + { + using var document = KeyValuePairJsonEncoder.Encode(await ConfigurationSettingRepository.Get().Where(setting => setting is not FeatureFlagConfigurationSetting).ToDictionaryAsync(setting => setting.Key, setting => setting.Value), Model.Prefix, Model.Separator); + + if (Module is not null) + { + await Module.InvokeVoidAsync("download", $"{Dns.GetHostName()}-{DateTimeOffset.UtcNow:yyyy-MM-dd}.json", Convert.ToBase64String(JsonSerializer.SerializeToUtf8Bytes(document))); + } + + Model = new InputModel(); + + break; + } + } + + break; + } case ImportExportOperationInputRadioGroup.Operation.Import: { switch (Model?.SourceType) @@ -284,7 +382,7 @@ destinationSetting.Etag = Convert.ToBase64String(SHA256.HashData(Encoding.UTF8.GetBytes(date.UtcDateTime.ToString("yyyy-MM-dd HH:mm:ss")))); destinationSetting.LastModified = date; destinationSetting.ContentType = Model.ContentType; - destinationSetting.Value = sourceValue?.ToString(); + destinationSetting.Value = sourceValue; await ConfigurationSettingRepository.Update(destinationSetting); } @@ -334,6 +432,8 @@ public IBrowserFile? SourceFile { get; set; } public string? SourceType { get; set; } + + public string? TargetType { get; set; } } } \ No newline at end of file diff --git a/src/AzureAppConfigurationEmulator/Components/Pages/ImportExport.razor.js b/src/AzureAppConfigurationEmulator/Components/Pages/ImportExport.razor.js new file mode 100644 index 0000000..4640997 --- /dev/null +++ b/src/AzureAppConfigurationEmulator/Components/Pages/ImportExport.razor.js @@ -0,0 +1,12 @@ +export function download(name, bytes) { + const element = document.createElement("a"); + + element.download = name; + element.href = "data:application/octet-stream;base64," + bytes; + + window.document.body.appendChild(element); + + element.click(); + + window.document.body.removeChild(element); +}