Skip to content

Commit 62ede9b

Browse files
authored
feat: add export to json file (#105)
1 parent a59356b commit 62ede9b

File tree

4 files changed

+149
-4
lines changed

4 files changed

+149
-4
lines changed

src/AzureAppConfigurationEmulator/Components/ImportExportOperationInputRadioGroup.razor

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
</AzureInputRadioLabel>
88

99
<AzureInputRadioLabel>
10-
<AzureInputRadio checked="@(Value is Operation.Export)" disabled Value="@Operation.Export"/>
10+
<AzureInputRadio checked="@(Value is Operation.Export)" Value="@Operation.Export"/>
1111
<div>Export</div>
1212
</AzureInputRadioLabel>
1313
</AzureInputRadioGroup>
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
@using System.Linq.Expressions
2+
3+
<AzureInputSelect AdditionalAttributes="@AdditionalAttributes" TValue="@string" Value="@Value" ValueChanged="@HandleValueChanged" ValueExpression="@ValueExpression">
4+
<option checked="@(Value is null)" hidden value="">Please select a target service</option>
5+
<option checked="@(Value is TargetType.AzureAppConfiguration)" disabled value="@TargetType.AzureAppConfiguration">App Configuration</option>
6+
<option checked="@(Value is TargetType.AzureAppService)" disabled value="@TargetType.AzureAppService">App Service</option>
7+
<option checked="@(Value is TargetType.ConfigurationFile)" value="@TargetType.ConfigurationFile">Configuration file</option>
8+
</AzureInputSelect>
9+
10+
@code {
11+
[Parameter(CaptureUnmatchedValues = true)] public IDictionary<string, object>? AdditionalAttributes { get; set; }
12+
13+
[Parameter] public string? Value { get; set; }
14+
15+
[Parameter] public EventCallback<string?> ValueChanged { get; set; }
16+
17+
[Parameter] public Expression<Func<string?>>? ValueExpression { get; set; }
18+
19+
private async Task HandleValueChanged(string? value)
20+
{
21+
await ValueChanged.InvokeAsync(!string.IsNullOrEmpty(value) ? value : null);
22+
}
23+
24+
public static class TargetType
25+
{
26+
public const string AzureAppConfiguration = nameof(AzureAppConfiguration);
27+
28+
public const string AzureAppService = nameof(AzureAppService);
29+
30+
public const string ConfigurationFile = nameof(ConfigurationFile);
31+
}
32+
33+
}

src/AzureAppConfigurationEmulator/Components/Pages/ImportExport.razor

Lines changed: 103 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
1+
@implements IAsyncDisposable
12
@inject IConfigurationSettingFactory ConfigurationSettingFactory
23
@inject IConfigurationSettingRepository ConfigurationSettingRepository
4+
@inject IJSRuntime JS
35
@inject IKeyValuePairJsonDecoder KeyValuePairJsonDecoder
6+
@inject IKeyValuePairJsonEncoder KeyValuePairJsonEncoder
47
@page "/kvdata"
8+
@using System.Net
59
@using System.Security.Cryptography
610
@using System.Text
711
@using System.Text.Json
@@ -21,6 +25,61 @@
2125
<EditForm class="flex flex-col gap-10" Model="@Model" OnSubmit="@HandleSubmit">
2226
@switch (Model?.Operation)
2327
{
28+
case ImportExportOperationInputRadioGroup.Operation.Export:
29+
<div class="flex flex-col gap-3">
30+
<ImportExportOperationInputRadioGroup @bind-Value="@Model.Operation" name="@nameof(Model.Operation)"/>
31+
32+
<label class="flex flex-row items-center">
33+
<div class="w-[200px]">Target type</div>
34+
<div class="flex-1 max-w-[600px]">
35+
<ImportExportTargetTypeInputSelect @bind-Value="@Model.TargetType" name="@nameof(Model.TargetType)"/>
36+
</div>
37+
</label>
38+
</div>
39+
40+
switch (Model.TargetType)
41+
{
42+
case ImportExportTargetTypeInputSelect.TargetType.ConfigurationFile:
43+
<div class="flex flex-col gap-3">
44+
<div class="font-bold text-lg">Export options</div>
45+
46+
<label class="flex flex-row items-center">
47+
<div class="w-[200px]">File format</div>
48+
<div class="flex-1 max-w-[600px]">
49+
<ImportExportFileFormatInputSelect @bind-Value="@Model.FileFormat" name="@nameof(Model.FileFormat)"/>
50+
</div>
51+
</label>
52+
</div>
53+
54+
if (Model.FileFormat is not null)
55+
{
56+
<div class="flex flex-col gap-3">
57+
<div class="font-bold text-lg">Apply changes to key-values</div>
58+
59+
<label class="flex flex-row items-center">
60+
<div class="w-[200px]">Separator</div>
61+
<div class="flex-1 max-w-[600px]">
62+
<ImportExportSeparatorInputSelect @bind-Value="@Model.Separator" name="@nameof(Model.Separator)"/>
63+
</div>
64+
</label>
65+
66+
<label class="flex flex-row items-center">
67+
<div class="w-[200px]">Remove prefix</div>
68+
<div class="flex-1 max-w-[600px]">
69+
<AzureInputText @bind-Value="@Model.Prefix" name="@nameof(Model.Prefix)"/>
70+
</div>
71+
</label>
72+
</div>
73+
74+
<div>
75+
<AzureButton Appearance="AzureButton.AzureAppearance.Primary" type="submit">Export</AzureButton>
76+
</div>
77+
}
78+
79+
break;
80+
}
81+
82+
break;
2483
case ImportExportOperationInputRadioGroup.Operation.Import:
2584
<div class="flex flex-col gap-3">
2685
<ImportExportOperationInputRadioGroup @bind-Value="@Model.Operation" name="@nameof(Model.Operation)"/>
@@ -86,7 +145,7 @@
86145
</div>
87146

88147
<div>
89-
<AzureButton Appearance="AzureButton.AzureAppearance.Primary" type="submit">@(Model.Operation switch { ImportExportOperationInputRadioGroup.Operation.Export => "Export", ImportExportOperationInputRadioGroup.Operation.Import => "Import", _ => throw new ArgumentOutOfRangeException() })</AzureButton>
148+
<AzureButton Appearance="AzureButton.AzureAppearance.Primary" type="submit">Import</AzureButton>
90149
</div>
91150
}
92151

@@ -148,7 +207,7 @@
148207
</div>
149208

150209
<div>
151-
<AzureButton Appearance="AzureButton.AzureAppearance.Primary" type="submit">@(Model.Operation switch { ImportExportOperationInputRadioGroup.Operation.Export => "Export", ImportExportOperationInputRadioGroup.Operation.Import => "Import", _ => throw new ArgumentOutOfRangeException() })</AzureButton>
210+
<AzureButton Appearance="AzureButton.AzureAppearance.Primary" type="submit">Import</AzureButton>
152211
</div>
153212
}
154213

@@ -166,6 +225,24 @@
166225

167226
private ICollection<string?> Labels { get; } = [];
168227

228+
private IJSObjectReference? Module { get; set; }
229+
230+
public async ValueTask DisposeAsync()
231+
{
232+
if (Module is not null)
233+
{
234+
await Module.DisposeAsync();
235+
}
236+
}
237+
238+
protected override async Task OnAfterRenderAsync(bool firstRender)
239+
{
240+
if (firstRender)
241+
{
242+
Module = await JS.InvokeAsync<IJSObjectReference>("import", "./Components/Pages/ImportExport.razor.js");
243+
}
244+
}
245+
169246
protected override void OnInitialized()
170247
{
171248
Model ??= new InputModel();
@@ -218,6 +295,27 @@
218295

219296
switch (Model?.Operation)
220297
{
298+
case ImportExportOperationInputRadioGroup.Operation.Export:
299+
{
300+
switch (Model?.TargetType)
301+
{
302+
case ImportExportTargetTypeInputSelect.TargetType.ConfigurationFile:
303+
{
304+
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);
305+
306+
if (Module is not null)
307+
{
308+
await Module.InvokeVoidAsync("download", $"{Dns.GetHostName()}-{DateTimeOffset.UtcNow:yyyy-MM-dd}.json", Convert.ToBase64String(JsonSerializer.SerializeToUtf8Bytes(document)));
309+
}
310+
311+
Model = new InputModel();
312+
313+
break;
314+
}
315+
}
316+
317+
break;
318+
}
221319
case ImportExportOperationInputRadioGroup.Operation.Import:
222320
{
223321
switch (Model?.SourceType)
@@ -284,7 +382,7 @@
284382
destinationSetting.Etag = Convert.ToBase64String(SHA256.HashData(Encoding.UTF8.GetBytes(date.UtcDateTime.ToString("yyyy-MM-dd HH:mm:ss"))));
285383
destinationSetting.LastModified = date;
286384
destinationSetting.ContentType = Model.ContentType;
287-
destinationSetting.Value = sourceValue?.ToString();
385+
destinationSetting.Value = sourceValue;
288386

289387
await ConfigurationSettingRepository.Update(destinationSetting);
290388
}
@@ -334,6 +432,8 @@
334432
public IBrowserFile? SourceFile { get; set; }
335433

336434
public string? SourceType { get; set; }
435+
436+
public string? TargetType { get; set; }
337437
}
338438

339439
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
export function download(name, bytes) {
2+
const element = document.createElement("a");
3+
4+
element.download = name;
5+
element.href = "data:application/octet-stream;base64," + bytes;
6+
7+
window.document.body.appendChild(element);
8+
9+
element.click();
10+
11+
window.document.body.removeChild(element);
12+
}

0 commit comments

Comments
 (0)