diff --git a/src/AzureAppConfigurationEmulator/Common/IKeyValuePairJsonEncoder.cs b/src/AzureAppConfigurationEmulator/Common/IKeyValuePairJsonEncoder.cs new file mode 100644 index 0000000..5667629 --- /dev/null +++ b/src/AzureAppConfigurationEmulator/Common/IKeyValuePairJsonEncoder.cs @@ -0,0 +1,11 @@ +using System.Text.Json; + +namespace AzureAppConfigurationEmulator.Common; + +public interface IKeyValuePairJsonEncoder +{ + JsonDocument Encode( + IEnumerable> pairs, + string? prefix = null, + string? separator = null); +} diff --git a/src/AzureAppConfigurationEmulator/Common/KeyValuePairJsonEncoder.cs b/src/AzureAppConfigurationEmulator/Common/KeyValuePairJsonEncoder.cs new file mode 100644 index 0000000..1991a62 --- /dev/null +++ b/src/AzureAppConfigurationEmulator/Common/KeyValuePairJsonEncoder.cs @@ -0,0 +1,90 @@ +using System.Text.Json; +using System.Text.Json.Nodes; + +namespace AzureAppConfigurationEmulator.Common; + +public class KeyValuePairJsonEncoder : IKeyValuePairJsonEncoder +{ + public JsonDocument Encode( + IEnumerable> pairs, + string? prefix = null, + string? separator = null) + { + JsonNode root = new JsonObject(); + + foreach (var (key, value) in pairs) + { + var keys = key.Split(separator).ToList(); + + if (!string.IsNullOrEmpty(prefix)) + { + if (keys[0] == prefix) + { + keys.RemoveAt(0); + } + else if (keys[0].StartsWith(prefix)) + { + keys[0] = keys[0][prefix.Length..]; + } + } + + var current = root; + + for (var i = 0; i < keys.Count; i++) + { + if (int.TryParse(keys[i], out var index)) + { + if (i == keys.Count - 1) + { + current.AsArray().Insert(index, value); + + break; + } + + if (current.AsArray().ElementAtOrDefault(index) is not { } next) + { + if (int.TryParse(keys[i + 1], out _)) + { + next = new JsonArray(); + } + else + { + next = new JsonObject(); + } + + current.AsArray().Insert(index, next); + } + + current = next; + } + else + { + if (i == keys.Count - 1) + { + current[keys[i]] = value; + + break; + } + + if (current[keys[i]] is not { } next) + { + if (int.TryParse(keys[i + 1], out _)) + { + next = new JsonArray(); + } + else + { + next = new JsonObject(); + } + + current[keys[i]] = next; + } + + current = next; + } + } + } + + return root.Deserialize()!; + } +} diff --git a/src/AzureAppConfigurationEmulator/Program.cs b/src/AzureAppConfigurationEmulator/Program.cs index 69af054..5936087 100644 --- a/src/AzureAppConfigurationEmulator/Program.cs +++ b/src/AzureAppConfigurationEmulator/Program.cs @@ -87,6 +87,7 @@ builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddSingleton(); +builder.Services.AddSingleton(); var app = builder.Build(); diff --git a/tests/AzureAppConfigurationEmulator.Tests/Common/KeyValuePairJsonDecoderTests.cs b/tests/AzureAppConfigurationEmulator.Tests/Common/KeyValuePairJsonDecoderTests.cs index 6995111..95232ae 100644 --- a/tests/AzureAppConfigurationEmulator.Tests/Common/KeyValuePairJsonDecoderTests.cs +++ b/tests/AzureAppConfigurationEmulator.Tests/Common/KeyValuePairJsonDecoderTests.cs @@ -15,7 +15,7 @@ public void SetUp() } [TestCaseSource(nameof(Decode_KeyValuePairs_DocumentAndPrefixAndSeparator_TestCases))] - public void Decode_KeyValuePairs_DocumentAndPrefixAndSeparator(string json, string? prefix, string? separator, IDictionary expected) + public void Decode_KeyValuePairs_DocumentAndPrefixAndSeparator(string json, string? prefix, string? separator, IEnumerable> expected) { // Arrange using var document = JsonDocument.Parse(json); @@ -24,7 +24,7 @@ public void Decode_KeyValuePairs_DocumentAndPrefixAndSeparator(string json, stri var settings = Decoder.Decode(document, prefix, separator); // Assert - Assert.That(settings.ToDictionary(), Is.EqualTo(expected)); + Assert.That(settings, Is.EqualTo(expected)); } // ReSharper disable once InconsistentNaming diff --git a/tests/AzureAppConfigurationEmulator.Tests/Common/KeyValuePairJsonEncoderTests.cs b/tests/AzureAppConfigurationEmulator.Tests/Common/KeyValuePairJsonEncoderTests.cs new file mode 100644 index 0000000..0e047fe --- /dev/null +++ b/tests/AzureAppConfigurationEmulator.Tests/Common/KeyValuePairJsonEncoderTests.cs @@ -0,0 +1,101 @@ +using System.Text.Json; +using AzureAppConfigurationEmulator.Common; +using NUnit.Framework; + +namespace AzureAppConfigurationEmulator.Tests.Common; + +public class KeyValuePairJsonEncoderTests +{ + private KeyValuePairJsonEncoder Encoder { get; set; } + + [SetUp] + public void SetUp() + { + Encoder = new KeyValuePairJsonEncoder(); + } + + [TestCaseSource(nameof(Encode_Document_KeyValuePairsAndPrefixAndSeparator_TestCases))] + public void Encode_Document_KeyValuePairsAndPrefixAndSeparator(IEnumerable> pairs, string? prefix, string? separator, string expected) + { + // Act + using var document = Encoder.Encode(pairs, prefix, separator); + + // Assert + Assert.That(JsonSerializer.Serialize(document), Is.EqualTo(expected)); + } + + // ReSharper disable once InconsistentNaming + private static object[] Encode_Document_KeyValuePairsAndPrefixAndSeparator_TestCases = + [ + new object?[] + { + new Dictionary { { "TestKey", "TestValue" } }, + null, + null, + "{\"TestKey\":\"TestValue\"}" + }, + new object?[] + { + new Dictionary { { "TestPrefixTestKey", "TestValue" } }, + "TestPrefix", + null, + "{\"TestKey\":\"TestValue\"}" + }, + new object?[] + { + new Dictionary { { "TestKey", "TestValue" } }, + null, + ".", + "{\"TestKey\":\"TestValue\"}" + }, + new object?[] + { + new Dictionary { { "TestPrefix.TestKey", "TestValue" } }, + "TestPrefix", + ".", + "{\"TestKey\":\"TestValue\"}" + }, + new object?[] + { + new Dictionary { { "TestOuterKey.TestInnerKey", "TestValue" } }, + null, + ".", + "{\"TestOuterKey\":{\"TestInnerKey\":\"TestValue\"}}" + }, + new object?[] + { + new Dictionary { { "TestPrefix.TestOuterKey.TestInnerKey", "TestValue" } }, + "TestPrefix", + ".", + "{\"TestOuterKey\":{\"TestInnerKey\":\"TestValue\"}}" + }, + new object?[] + { + new Dictionary { { "TestKey.0", "TestValue" } }, + null, + ".", + "{\"TestKey\":[\"TestValue\"]}" + }, + new object?[] + { + new Dictionary { { "TestPrefix.TestKey.0", "TestValue" } }, + "TestPrefix", + ".", + "{\"TestKey\":[\"TestValue\"]}" + }, + new object?[] + { + new Dictionary { { "TestOuterKey.0.TestInnerKey", "TestValue" } }, + null, + ".", + "{\"TestOuterKey\":[{\"TestInnerKey\":\"TestValue\"}]}" + }, + new object?[] + { + new Dictionary { { "TestPrefix.TestOuterKey.0.TestInnerKey", "TestValue" } }, + "TestPrefix", + ".", + "{\"TestOuterKey\":[{\"TestInnerKey\":\"TestValue\"}]}" + } + ]; +}