From 49ad577fbd5cc1bdf6855b0a1ad260bec2b89ca6 Mon Sep 17 00:00:00 2001 From: Snigdhajyoti Ghosh Date: Wed, 7 Aug 2024 22:15:59 +0530 Subject: [PATCH 1/8] Parse Json param with fallback to default way of parsing i.e. string or stringList --- .../JsonOrDefaultParameterProcessor.cs | 92 +++++++++++++ .../JsonOrDefaultParameterProcessorTest.cs | 123 ++++++++++++++++++ 2 files changed, 215 insertions(+) create mode 100644 src/Amazon.Extensions.Configuration.SystemsManager/JsonOrDefaultParameterProcessor.cs create mode 100644 test/Amazon.Extensions.Configuration.SystemsManager.Tests/JsonOrDefaultParameterProcessorTest.cs diff --git a/src/Amazon.Extensions.Configuration.SystemsManager/JsonOrDefaultParameterProcessor.cs b/src/Amazon.Extensions.Configuration.SystemsManager/JsonOrDefaultParameterProcessor.cs new file mode 100644 index 0000000..2be7471 --- /dev/null +++ b/src/Amazon.Extensions.Configuration.SystemsManager/JsonOrDefaultParameterProcessor.cs @@ -0,0 +1,92 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text.Json; +using Amazon.Extensions.Configuration.SystemsManager.Internal; +using Amazon.SimpleSystemsManagement; +using Amazon.SimpleSystemsManagement.Model; +using Microsoft.Extensions.Configuration; + +namespace Amazon.Extensions.Configuration.SystemsManager +{ + public class JsonOrDefaultParameterProcessor : DefaultParameterProcessor + { + /// + /// + /// Converts AWS SSM Parameters into a dictionary, handling JSON, strings, and StringLists. + /// Ensures case-insensitive unique keys. + /// + public override IDictionary ProcessParameters(IEnumerable parameters, string path) + { + var result = new Dictionary(StringComparer.OrdinalIgnoreCase); + + foreach (var parameter in parameters.Where(parameter => IncludeParameter(parameter, path))) + { + var prefix = GetKey(parameter, path); + + if (parameter.Type == ParameterType.StringList) + { + ParseStringListParameter(parameter, prefix, result); + continue; + } + + if (!TryParseJsonParameter(parameter, prefix, result)) + { + ParseStringParameter(parameter, prefix, result); + } + } + + return result; + } + + protected static void ParseStringListParameter(Parameter parameter, string keyPrefix, Dictionary result) + { + var configKeyValuePairs = parameter.Value + .Split(',') + .Select((value, idx) => new KeyValuePair($"{keyPrefix}{ConfigurationPath.KeyDelimiter}{idx}", value)); + + foreach (var kv in configKeyValuePairs) + { + if (result.ContainsKey(kv.Key)) + { + throw new DuplicateParameterException($"Duplicate parameter '{kv.Key}' found. Parameter keys are case-insensitive."); + } + + result.Add(kv.Key, kv.Value); + } + } + + protected static void ParseStringParameter(Parameter parameter, string key, Dictionary result) + { + if (result.ContainsKey(key)) + { + throw new DuplicateParameterException($"Duplicate parameter '{key}' found. Parameter keys are case-insensitive."); + } + + result.Add(key, parameter.Value); + } + + protected static bool TryParseJsonParameter(Parameter parameter, string keyPrefix, Dictionary result) + { + try + { + foreach (var kv in JsonConfigurationParser.Parse(parameter.Value)) + { + var key = !string.IsNullOrEmpty(keyPrefix) ? ConfigurationPath.Combine(keyPrefix, kv.Key) : kv.Key; + if (result.ContainsKey(key)) + { + throw new DuplicateParameterException($"Duplicate parameter '{key}' found. Parameter keys are case-insensitive."); + } + + result.Add(key, kv.Value); + + } + return true; + } + catch (JsonException) + { + return false; + } + } + } +} \ No newline at end of file diff --git a/test/Amazon.Extensions.Configuration.SystemsManager.Tests/JsonOrDefaultParameterProcessorTest.cs b/test/Amazon.Extensions.Configuration.SystemsManager.Tests/JsonOrDefaultParameterProcessorTest.cs new file mode 100644 index 0000000..0f1f003 --- /dev/null +++ b/test/Amazon.Extensions.Configuration.SystemsManager.Tests/JsonOrDefaultParameterProcessorTest.cs @@ -0,0 +1,123 @@ +using System.Collections.Generic; +using Amazon.SimpleSystemsManagement; +using Amazon.SimpleSystemsManagement.Model; +using Xunit; + +namespace Amazon.Extensions.Configuration.SystemsManager.Tests +{ + public class JsonOrDefaultParameterProcessorTest + { + private readonly JsonOrDefaultParameterProcessor _processor = new JsonOrDefaultParameterProcessor(); + + [Fact] + public void ParsesJsonParametersSuccessfully() + { + var parameters = new List + { + new Parameter() { Name = "/test/level1/level2", Type = ParameterType.String, Value = "{\"level1Key\":\"level1value\"}" }, + new Parameter() { Name = "/test/level1", Type = ParameterType.String, Value = "{\"level1Key\" : {\"level2key\" : \"level2value\"}}" } + }; + var result = _processor.ProcessParameters(parameters, "/test"); + + Assert.Equal(2, result.Count); + Assert.True(result.ContainsKey("level1:level2:level1Key")); + Assert.True(result.ContainsKey("level1:level1Key:level2key")); + Assert.Equal("level1value", result["level1:level2:level1Key"]); + Assert.Equal("level2value", result["level1:level1Key:level2key"]); + } + + [Fact] + public void ProcessParameters_ParsesJsonParametersWithoutPrefixSuccessfully() + { + var parameters = new List + { + new Parameter() + { + Name = "/test/level1/level2", Type = ParameterType.String, Value = "{\"level1Key\":\"level1value\"}" + }, + new Parameter() + { + Name = "/test/level1", Type = ParameterType.String, + Value = "{\"level1Key\" : {\"level2key\" : \"level2value\"}}" + } + }; + var result = _processor.ProcessParameters(parameters, ""); + + Assert.Equal(2, result.Count); + Assert.True(result.ContainsKey("test:level1:level2:level1Key")); + Assert.True(result.ContainsKey("test:level1:level1Key:level2key")); + Assert.Equal("level1value", result["test:level1:level2:level1Key"]); + Assert.Equal("level2value", result["test:level1:level1Key:level2key"]); + } + + + [Fact] + public void ProcessParameters_FallsBackOnString() + { + var parameters = new List + { + new Parameter() { Name = "/test/stringParam", Type = ParameterType.String, Value = "some string" } + }; + var result = _processor.ProcessParameters(parameters, "/test"); + + Assert.Equal(1, result.Count); + Assert.True(result.ContainsKey("stringParam")); + Assert.Equal("some string", result["stringParam"]); + } + + [Fact] + public void ProcessParameters_ThrowsOnDuplicateParameter() + { + var parameters = new List + { + new Parameter() { Name = "/test/Duplicate", Type = ParameterType.String, Value = "value1" }, + new Parameter() { Name = "/test/duplicate", Type = ParameterType.String, Value = "value2" } + }; + var duplicateParameterException = Assert.Throws(() => _processor.ProcessParameters(parameters, "/test")); + Assert.Equal("Duplicate parameter 'duplicate' found. Parameter keys are case-insensitive.", duplicateParameterException.Message); + } + + [Fact] + public void ProcessParameters_ThrowsOnDuplicateParameterAtMultiLevel() + { + var parameters = new List + { + new Parameter() { Name = "/test/level1", Type = ParameterType.String, Value = "{\"level1Key\":\"level1value\"}" }, + new Parameter() { Name = "/test/level1/level1key", Type = ParameterType.String, Value = "level1valueOverriden" }, + }; + + var duplicateParameterException = Assert.Throws(() => _processor.ProcessParameters(parameters, "/test")); + Assert.Equal("Duplicate parameter 'level1:level1key' found. Parameter keys are case-insensitive.", duplicateParameterException.Message); + } + + [Fact] + public void ProcessParameters_ThrowsOnDuplicateParameterAtMultilevelForJsonArray() + { + var parameters = new List + { + new Parameter() { Name = "/test/level1", Type = ParameterType.String, Value = "{\"level1Key\" : [\"level1value1\", \"level1value2\"] }" }, + new Parameter() { Name = "/test/level1/Level1key", Type = ParameterType.StringList, Value = "level1valueOverriden" }, + }; + + var duplicateParameterException = Assert.Throws(() => _processor.ProcessParameters(parameters, "/test")); + Assert.Equal("Duplicate parameter 'level1:Level1key:0' found. Parameter keys are case-insensitive.", duplicateParameterException.Message); + } + + + [Fact] + public void ProcessParameters_ProcessesStringListParameters() + { + var parameters = new List + { + new Parameter() { Name = "/test/stringList", Type = ParameterType.StringList, Value = "value1,value2" } + }; + var result = _processor.ProcessParameters(parameters, "/test"); + + Assert.Equal(2, result.Count); + Assert.True(result.ContainsKey("stringList:0")); + Assert.Equal("value1", result["stringList:0"]); + Assert.True(result.ContainsKey("stringList:1")); + Assert.Equal("value2", result["stringList:1"]); + } + } +} \ No newline at end of file From d8049f2870033b495cb9f1470358e413efb76a9e Mon Sep 17 00:00:00 2001 From: Snigdhajyoti Ghosh Date: Wed, 7 Aug 2024 23:45:08 +0530 Subject: [PATCH 2/8] refact: extract common method into utility class --- .../DefaultParameterProcessor.cs | 55 ++++---------- .../JsonOrStringParameterProcessor.cs | 40 +++++++++++ .../JsonParameterProcessor.cs | 36 ++++------ .../ParameterProcessorUtil.cs} | 72 ++++++------------- ... => JsonOrStringParameterProcessorTest.cs} | 4 +- 5 files changed, 92 insertions(+), 115 deletions(-) create mode 100644 src/Amazon.Extensions.Configuration.SystemsManager/JsonOrStringParameterProcessor.cs rename src/Amazon.Extensions.Configuration.SystemsManager/{JsonOrDefaultParameterProcessor.cs => Utils/ParameterProcessorUtil.cs} (52%) rename test/Amazon.Extensions.Configuration.SystemsManager.Tests/{JsonOrDefaultParameterProcessorTest.cs => JsonOrStringParameterProcessorTest.cs} (97%) diff --git a/src/Amazon.Extensions.Configuration.SystemsManager/DefaultParameterProcessor.cs b/src/Amazon.Extensions.Configuration.SystemsManager/DefaultParameterProcessor.cs index a5d637b..5e51616 100644 --- a/src/Amazon.Extensions.Configuration.SystemsManager/DefaultParameterProcessor.cs +++ b/src/Amazon.Extensions.Configuration.SystemsManager/DefaultParameterProcessor.cs @@ -1,12 +1,12 @@ /* * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * + * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. * A copy of the License is located at - * + * * http://aws.amazon.com/apache2.0 - * + * * or in the "license" file accompanying this file. This file is distributed * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either * express or implied. See the License for the specific language governing @@ -19,6 +19,7 @@ using Amazon.SimpleSystemsManagement; using Amazon.SimpleSystemsManagement.Model; using Microsoft.Extensions.Configuration; +using static Amazon.Extensions.Configuration.SystemsManager.Utils.ParameterProcessorUtil; namespace Amazon.Extensions.Configuration.SystemsManager { @@ -33,10 +34,11 @@ public class DefaultParameterProcessor : IParameterProcessor public virtual bool IncludeParameter(Parameter parameter, string path) => true; + /// Get the extra prefix if the path is subset of parameter name. public virtual string GetKey(Parameter parameter, string path) { - var name = parameter.Name.StartsWith(path, StringComparison.OrdinalIgnoreCase) - ? parameter.Name.Substring(path.Length) + var name = parameter.Name.StartsWith(path, StringComparison.OrdinalIgnoreCase) + ? parameter.Name.Substring(path.Length) : parameter.Name; #if NETCOREAPP3_1_OR_GREATER return name.TrimStart('/').Replace("/", KeyDelimiter, StringComparison.InvariantCulture); @@ -47,51 +49,24 @@ public virtual string GetKey(Parameter parameter, string path) public virtual string GetValue(Parameter parameter, string path) => parameter.Value; - private IEnumerable> ParseStringList(Parameter parameter, string path) - { - // Items in a StringList must be separated by a comma (,). - // You can't use other punctuation or special characters to escape items in the list. - // If you have a parameter value that requires a comma, then use the String type. - // https://docs.aws.amazon.com/systems-manager/latest/userguide/param-create-cli.html#param-create-cli-stringlist - return parameter.Value.Split(',').Select((value, idx) => - new KeyValuePair($"{GetKey(parameter, path)}:{idx}", value)); - } - public virtual IDictionary ProcessParameters(IEnumerable parameters, string path) { - var result = new List>(); + var result = new Dictionary(StringComparer.OrdinalIgnoreCase); + foreach (var parameter in parameters.Where(parameter => IncludeParameter(parameter, path))) { + var prefix = GetKey(parameter, path); + if (parameter.Type == ParameterType.StringList) { - var parameterList = ParseStringList(parameter, path); - - // Check for duplicate parameter keys. - var stringListKeys = parameterList.Select(p => p.Key); - var duplicateKeys = result.Where(r => stringListKeys.Contains(r.Key, StringComparer.OrdinalIgnoreCase)).Select(r => r.Key); - if (duplicateKeys.Count() > 0) - { - throw new DuplicateParameterException($"Duplicate parameters '{string.Join(";", duplicateKeys)}' found. Parameter keys are case-insensitive."); - } - - result.AddRange(parameterList); + ParseStringListParameter(parameter, prefix, result); + continue; } - else - { - string parameterKey = GetKey(parameter, path); - // Check for duplicate parameter key. - if (result.Any(r => string.Equals(r.Key, parameterKey, StringComparison.OrdinalIgnoreCase))) - { - throw new DuplicateParameterException($"Duplicate parameter '{parameterKey}' found. Parameter keys are case-insensitive."); - } - - result.Add(new KeyValuePair(parameterKey, GetValue(parameter, path))); - } + ParseStringParameter(parameter, prefix, result); } - return result.ToDictionary(parameter => parameter.Key, parameter => parameter.Value, - StringComparer.OrdinalIgnoreCase); + return result; } } } \ No newline at end of file diff --git a/src/Amazon.Extensions.Configuration.SystemsManager/JsonOrStringParameterProcessor.cs b/src/Amazon.Extensions.Configuration.SystemsManager/JsonOrStringParameterProcessor.cs new file mode 100644 index 0000000..e48cbf5 --- /dev/null +++ b/src/Amazon.Extensions.Configuration.SystemsManager/JsonOrStringParameterProcessor.cs @@ -0,0 +1,40 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Amazon.SimpleSystemsManagement; +using Amazon.SimpleSystemsManagement.Model; +using static Amazon.Extensions.Configuration.SystemsManager.Utils.ParameterProcessorUtil; + +namespace Amazon.Extensions.Configuration.SystemsManager +{ + /// + /// + /// A processor that prioritizes JSON parameters but falls back to string parameters, + /// in accordance with Systems Manager's suggested naming conventions + /// + public class JsonOrStringParameterProcessor : DefaultParameterProcessor + { + public override IDictionary ProcessParameters(IEnumerable parameters, string path) + { + var result = new Dictionary(StringComparer.OrdinalIgnoreCase); + + foreach (var parameter in parameters.Where(parameter => IncludeParameter(parameter, path))) + { + var prefix = GetKey(parameter, path); + + if (parameter.Type == ParameterType.StringList) + { + ParseStringListParameter(parameter, prefix, result); + continue; + } + + if (!TryParseJsonParameter(parameter, prefix, result)) + { + ParseStringParameter(parameter, prefix, result); + } + } + + return result; + } + } +} \ No newline at end of file diff --git a/src/Amazon.Extensions.Configuration.SystemsManager/JsonParameterProcessor.cs b/src/Amazon.Extensions.Configuration.SystemsManager/JsonParameterProcessor.cs index 498053f..646bd11 100644 --- a/src/Amazon.Extensions.Configuration.SystemsManager/JsonParameterProcessor.cs +++ b/src/Amazon.Extensions.Configuration.SystemsManager/JsonParameterProcessor.cs @@ -1,12 +1,12 @@ /* * Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * + * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. * A copy of the License is located at - * + * * http://aws.amazon.com/apache2.0 - * + * * or in the "license" file accompanying this file. This file is distributed * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either * express or implied. See the License for the specific language governing @@ -16,42 +16,30 @@ using System; using System.Collections.Generic; using System.Linq; -using Amazon.Extensions.Configuration.SystemsManager.Internal; using Amazon.SimpleSystemsManagement.Model; -using Microsoft.Extensions.Configuration; +using static Amazon.Extensions.Configuration.SystemsManager.Utils.ParameterProcessorUtil; namespace Amazon.Extensions.Configuration.SystemsManager { /// /// - /// Default parameter processor based on Systems Manager's suggested naming convention + /// A processor specifically designed for handling JSON parameters, + /// following Systems Manager's recommended naming conventions /// public class JsonParameterProcessor : DefaultParameterProcessor { public override IDictionary ProcessParameters(IEnumerable parameters, string path) { - var outputDictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); - foreach (Parameter parameter in parameters.Where(parameter => IncludeParameter(parameter, path))) - { - // Get the extra prefix if the path is subset of paramater name. - string prefix = GetKey(parameter, path); - - var parameterDictionary = JsonConfigurationParser.Parse(parameter.Value); - foreach (var keyValue in parameterDictionary) - { - string key = (!string.IsNullOrEmpty(prefix) ? ConfigurationPath.Combine(prefix, keyValue.Key) : keyValue.Key); + var result = new Dictionary(StringComparer.OrdinalIgnoreCase); - // Check for duplicate parameter key. - if (outputDictionary.ContainsKey(key)) - { - throw new DuplicateParameterException($"Duplicate parameter '{key}' found. Parameter keys are case-insensitive."); - } + foreach (var parameter in parameters.Where(parameter => IncludeParameter(parameter, path))) + { + var prefix = GetKey(parameter, path); - outputDictionary.Add(key, keyValue.Value); - } + TryParseJsonParameter(parameter, prefix, result); } - return outputDictionary; + return result; } } } \ No newline at end of file diff --git a/src/Amazon.Extensions.Configuration.SystemsManager/JsonOrDefaultParameterProcessor.cs b/src/Amazon.Extensions.Configuration.SystemsManager/Utils/ParameterProcessorUtil.cs similarity index 52% rename from src/Amazon.Extensions.Configuration.SystemsManager/JsonOrDefaultParameterProcessor.cs rename to src/Amazon.Extensions.Configuration.SystemsManager/Utils/ParameterProcessorUtil.cs index 2be7471..ecd3dc5 100644 --- a/src/Amazon.Extensions.Configuration.SystemsManager/JsonOrDefaultParameterProcessor.cs +++ b/src/Amazon.Extensions.Configuration.SystemsManager/Utils/ParameterProcessorUtil.cs @@ -1,46 +1,43 @@ -using System; using System.Collections.Generic; using System.Linq; using System.Text.Json; using Amazon.Extensions.Configuration.SystemsManager.Internal; -using Amazon.SimpleSystemsManagement; using Amazon.SimpleSystemsManagement.Model; using Microsoft.Extensions.Configuration; -namespace Amazon.Extensions.Configuration.SystemsManager +namespace Amazon.Extensions.Configuration.SystemsManager.Utils { - public class JsonOrDefaultParameterProcessor : DefaultParameterProcessor + public static class ParameterProcessorUtil { - /// - /// - /// Converts AWS SSM Parameters into a dictionary, handling JSON, strings, and StringLists. - /// Ensures case-insensitive unique keys. - /// - public override IDictionary ProcessParameters(IEnumerable parameters, string path) + public static bool TryParseJsonParameter(Parameter parameter, string keyPrefix, IDictionary result) { - var result = new Dictionary(StringComparer.OrdinalIgnoreCase); - - foreach (var parameter in parameters.Where(parameter => IncludeParameter(parameter, path))) + try { - var prefix = GetKey(parameter, path); - - if (parameter.Type == ParameterType.StringList) + foreach (var kv in JsonConfigurationParser.Parse(parameter.Value)) { - ParseStringListParameter(parameter, prefix, result); - continue; - } + var key = !string.IsNullOrEmpty(keyPrefix) ? ConfigurationPath.Combine(keyPrefix, kv.Key) : kv.Key; + if (result.ContainsKey(key)) + { + throw new DuplicateParameterException($"Duplicate parameter '{key}' found. Parameter keys are case-insensitive."); + } - if (!TryParseJsonParameter(parameter, prefix, result)) - { - ParseStringParameter(parameter, prefix, result); + result.Add(key, kv.Value); } - } - return result; + return true; + } + catch (JsonException) + { + return false; + } } - protected static void ParseStringListParameter(Parameter parameter, string keyPrefix, Dictionary result) + public static void ParseStringListParameter(Parameter parameter, string keyPrefix, IDictionary result) { + // Items in a StringList must be separated by a comma (,). + // You can't use other punctuation or special characters to escape items in the list. + // If you have a parameter value that requires a comma, then use the String type. + // https://docs.aws.amazon.com/systems-manager/latest/userguide/param-create-cli.html#param-create-cli-stringlist var configKeyValuePairs = parameter.Value .Split(',') .Select((value, idx) => new KeyValuePair($"{keyPrefix}{ConfigurationPath.KeyDelimiter}{idx}", value)); @@ -56,7 +53,7 @@ protected static void ParseStringListParameter(Parameter parameter, string keyPr } } - protected static void ParseStringParameter(Parameter parameter, string key, Dictionary result) + public static void ParseStringParameter(Parameter parameter, string key, IDictionary result) { if (result.ContainsKey(key)) { @@ -65,28 +62,5 @@ protected static void ParseStringParameter(Parameter parameter, string key, Dict result.Add(key, parameter.Value); } - - protected static bool TryParseJsonParameter(Parameter parameter, string keyPrefix, Dictionary result) - { - try - { - foreach (var kv in JsonConfigurationParser.Parse(parameter.Value)) - { - var key = !string.IsNullOrEmpty(keyPrefix) ? ConfigurationPath.Combine(keyPrefix, kv.Key) : kv.Key; - if (result.ContainsKey(key)) - { - throw new DuplicateParameterException($"Duplicate parameter '{key}' found. Parameter keys are case-insensitive."); - } - - result.Add(key, kv.Value); - - } - return true; - } - catch (JsonException) - { - return false; - } - } } } \ No newline at end of file diff --git a/test/Amazon.Extensions.Configuration.SystemsManager.Tests/JsonOrDefaultParameterProcessorTest.cs b/test/Amazon.Extensions.Configuration.SystemsManager.Tests/JsonOrStringParameterProcessorTest.cs similarity index 97% rename from test/Amazon.Extensions.Configuration.SystemsManager.Tests/JsonOrDefaultParameterProcessorTest.cs rename to test/Amazon.Extensions.Configuration.SystemsManager.Tests/JsonOrStringParameterProcessorTest.cs index 0f1f003..bd157f0 100644 --- a/test/Amazon.Extensions.Configuration.SystemsManager.Tests/JsonOrDefaultParameterProcessorTest.cs +++ b/test/Amazon.Extensions.Configuration.SystemsManager.Tests/JsonOrStringParameterProcessorTest.cs @@ -5,9 +5,9 @@ namespace Amazon.Extensions.Configuration.SystemsManager.Tests { - public class JsonOrDefaultParameterProcessorTest + public class JsonOrStringParameterProcessorTest { - private readonly JsonOrDefaultParameterProcessor _processor = new JsonOrDefaultParameterProcessor(); + private readonly JsonOrStringParameterProcessor _processor = new JsonOrStringParameterProcessor(); [Fact] public void ParsesJsonParametersSuccessfully() From 276340ae80bd9ec033fecf534ec12da352abb0c3 Mon Sep 17 00:00:00 2001 From: Snigdhajyoti Ghosh Date: Thu, 8 Aug 2024 00:04:18 +0530 Subject: [PATCH 3/8] fix: json processing should throw JsonException if not json value --- .../JsonOrStringParameterProcessor.cs | 7 +++++- .../JsonParameterProcessor.cs | 2 +- .../Utils/ParameterProcessorUtil.cs | 24 ++++++------------- .../JsonParameterProcessorTests.cs | 13 ++++++++++ 4 files changed, 27 insertions(+), 19 deletions(-) diff --git a/src/Amazon.Extensions.Configuration.SystemsManager/JsonOrStringParameterProcessor.cs b/src/Amazon.Extensions.Configuration.SystemsManager/JsonOrStringParameterProcessor.cs index e48cbf5..bc97ace 100644 --- a/src/Amazon.Extensions.Configuration.SystemsManager/JsonOrStringParameterProcessor.cs +++ b/src/Amazon.Extensions.Configuration.SystemsManager/JsonOrStringParameterProcessor.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Text.Json; using Amazon.SimpleSystemsManagement; using Amazon.SimpleSystemsManagement.Model; using static Amazon.Extensions.Configuration.SystemsManager.Utils.ParameterProcessorUtil; @@ -28,7 +29,11 @@ public override IDictionary ProcessParameters(IEnumerable ProcessParameters(IEnumerable result) + public static void ParseJsonParameter(Parameter parameter, string keyPrefix, IDictionary result) { - try + foreach (var kv in JsonConfigurationParser.Parse(parameter.Value)) { - foreach (var kv in JsonConfigurationParser.Parse(parameter.Value)) + var key = !string.IsNullOrEmpty(keyPrefix) ? ConfigurationPath.Combine(keyPrefix, kv.Key) : kv.Key; + if (result.ContainsKey(key)) { - var key = !string.IsNullOrEmpty(keyPrefix) ? ConfigurationPath.Combine(keyPrefix, kv.Key) : kv.Key; - if (result.ContainsKey(key)) - { - throw new DuplicateParameterException($"Duplicate parameter '{key}' found. Parameter keys are case-insensitive."); - } - - result.Add(key, kv.Value); + throw new DuplicateParameterException($"Duplicate parameter '{key}' found. Parameter keys are case-insensitive."); } - return true; - } - catch (JsonException) - { - return false; + result.Add(key, kv.Value); } } - + public static void ParseStringListParameter(Parameter parameter, string keyPrefix, IDictionary result) { // Items in a StringList must be separated by a comma (,). diff --git a/test/Amazon.Extensions.Configuration.SystemsManager.Tests/JsonParameterProcessorTests.cs b/test/Amazon.Extensions.Configuration.SystemsManager.Tests/JsonParameterProcessorTests.cs index a4acec1..16c5d2f 100644 --- a/test/Amazon.Extensions.Configuration.SystemsManager.Tests/JsonParameterProcessorTests.cs +++ b/test/Amazon.Extensions.Configuration.SystemsManager.Tests/JsonParameterProcessorTests.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Text.Json; using Amazon.SimpleSystemsManagement.Model; using Xunit; @@ -58,5 +59,17 @@ public void DuplicateParametersTest() const string path = "/"; Assert.Throws(() => _parameterProcessor.ProcessParameters(parameters, path)); } + + [Fact] + public void InvalidJsonTest() + { + var parameters = new List + { + new Parameter {Name = "/p1", Value = "String value"}, + }; + + const string path = "/"; + Assert.ThrowsAny(() => _parameterProcessor.ProcessParameters(parameters, path)); + } } } \ No newline at end of file From e8b82d9fc34df422dbbc75f76d0a5e2d31d7d4c4 Mon Sep 17 00:00:00 2001 From: Snigdhajyoti Ghosh Date: Thu, 8 Aug 2024 00:24:09 +0530 Subject: [PATCH 4/8] refactor tests and add more tests to cover muitlple scenarios --- .../DefaultParameterProcessorTests.cs | 51 ++++++++++++------- .../JsonOrStringParameterProcessorTest.cs | 16 +++--- .../JsonParameterProcessorTests.cs | 7 +-- 3 files changed, 42 insertions(+), 32 deletions(-) diff --git a/test/Amazon.Extensions.Configuration.SystemsManager.Tests/DefaultParameterProcessorTests.cs b/test/Amazon.Extensions.Configuration.SystemsManager.Tests/DefaultParameterProcessorTests.cs index ef44742..70f3149 100644 --- a/test/Amazon.Extensions.Configuration.SystemsManager.Tests/DefaultParameterProcessorTests.cs +++ b/test/Amazon.Extensions.Configuration.SystemsManager.Tests/DefaultParameterProcessorTests.cs @@ -1,18 +1,33 @@ using System.Collections.Generic; using Amazon.SimpleSystemsManagement; using Amazon.SimpleSystemsManagement.Model; -using Moq; using Xunit; namespace Amazon.Extensions.Configuration.SystemsManager.Tests { public class DefaultParameterProcessorTests { - private readonly IParameterProcessor _parameterProcessor; + private readonly IParameterProcessor _parameterProcessor = new DefaultParameterProcessor(); - public DefaultParameterProcessorTests() + [Fact] + public void ExtractConfigurationKeyFromParameter() { - _parameterProcessor = new DefaultParameterProcessor(); + var processor = new DefaultParameterProcessor(); + + Assert.Equal("level1:level2", processor.GetKey(new Parameter() { Name = "/level1/level2" }, "")); + Assert.Equal("level1:level2", processor.GetKey(new Parameter() { Name = "/level1/level2" }, "/")); + Assert.Equal("level1:level2", processor.GetKey(new Parameter() { Name = "/level1/level2" }, "/someotherlevel")); + + Assert.Equal("level2:level3", processor.GetKey(new Parameter() { Name = "/level1/level2/level3" }, "/level1")); + Assert.Equal("level2", processor.GetKey(new Parameter() { Name = "/level1/level2" }, "/LEVEL1")); + } + + [Fact] + public void ExtractConfigurationValueFromParameter() + { + var processor = new DefaultParameterProcessor(); + + Assert.Equal("Some value", processor.GetValue(new Parameter() { Value = "Some value" }, null)); } [Fact] @@ -20,10 +35,10 @@ public void ProcessParametersTest() { var parameters = new List { - new Parameter {Name = "/start/path/p1/p2-1", Value = "p1:p2-1"}, - new Parameter {Name = "/start/path/p1/p2-2", Value = "p1:p2-2"}, - new Parameter {Name = "/start/path/p1/p2/p3-1", Value = "p1:p2:p3-1"}, - new Parameter {Name = "/start/path/p1/p2/p3-2", Value = "p1:p2:p3-2"}, + new Parameter { Name = "/start/path/p1/p2-1", Value = "p1:p2-1" }, + new Parameter { Name = "/start/path/p1/p2-2", Value = "p1:p2-2" }, + new Parameter { Name = "/start/path/p1/p2/p3-1", Value = "p1:p2:p3-1" }, + new Parameter { Name = "/start/path/p1/p2/p3-2", Value = "p1:p2:p3-2" }, }; const string path = "/start/path"; @@ -38,9 +53,9 @@ public void ProcessParametersStringListTest() { var parameters = new List { - new Parameter {Name = "/string-list/single", Value = "p1", Type = ParameterType.StringList}, - new Parameter {Name = "/string-list/multiple", Value = "p1,p2,p3", Type = ParameterType.StringList}, - new Parameter {Name = "/string-list/empty", Value = "", Type = ParameterType.StringList}, + new Parameter { Name = "/string-list/single", Value = "p1", Type = ParameterType.StringList }, + new Parameter { Name = "/string-list/multiple", Value = "p1,p2,p3", Type = ParameterType.StringList }, + new Parameter { Name = "/string-list/empty", Value = "", Type = ParameterType.StringList }, }; const string path = "/string-list"; @@ -55,14 +70,14 @@ public void ProcessParametersStringListTest() Assert.Equal("", data["empty:0"]); } - + [Fact] public void ProcessParametersRootTest() { var parameters = new List { - new Parameter {Name = "/p1", Value = "p1"}, - new Parameter {Name = "p2", Value = "p2"}, + new Parameter { Name = "/p1", Value = "p1" }, + new Parameter { Name = "p2", Value = "p2" }, }; const string path = "/"; @@ -77,8 +92,8 @@ public void DuplicateSimpleParametersTest() { var parameters = new List { - new Parameter {Name = "/start/path/p1", Value = "p1:1"}, - new Parameter {Name = "/start/path/P1", Value = "p1:2"} + new Parameter { Name = "/start/path/p1", Value = "p1:1" }, + new Parameter { Name = "/start/path/P1", Value = "p1:2" } }; const string path = "/start/path"; @@ -90,8 +105,8 @@ public void DuplicateStringListParametersTest() { var parameters = new List { - new Parameter {Name = "/string-list/multiple", Value = "p1,p2,p3", Type = ParameterType.StringList}, - new Parameter {Name = "/string-list/MULTIPLE", Value = "p3,p5,p6", Type = ParameterType.StringList} + new Parameter { Name = "/string-list/multiple", Value = "p1,p2,p3", Type = ParameterType.StringList }, + new Parameter { Name = "/string-list/MULTIPLE", Value = "p3,p5,p6", Type = ParameterType.StringList } }; const string path = "/string-list"; diff --git a/test/Amazon.Extensions.Configuration.SystemsManager.Tests/JsonOrStringParameterProcessorTest.cs b/test/Amazon.Extensions.Configuration.SystemsManager.Tests/JsonOrStringParameterProcessorTest.cs index bd157f0..fba2a14 100644 --- a/test/Amazon.Extensions.Configuration.SystemsManager.Tests/JsonOrStringParameterProcessorTest.cs +++ b/test/Amazon.Extensions.Configuration.SystemsManager.Tests/JsonOrStringParameterProcessorTest.cs @@ -7,7 +7,7 @@ namespace Amazon.Extensions.Configuration.SystemsManager.Tests { public class JsonOrStringParameterProcessorTest { - private readonly JsonOrStringParameterProcessor _processor = new JsonOrStringParameterProcessor(); + private readonly IParameterProcessor _parameterProcessor = new JsonOrStringParameterProcessor(); [Fact] public void ParsesJsonParametersSuccessfully() @@ -17,7 +17,7 @@ public void ParsesJsonParametersSuccessfully() new Parameter() { Name = "/test/level1/level2", Type = ParameterType.String, Value = "{\"level1Key\":\"level1value\"}" }, new Parameter() { Name = "/test/level1", Type = ParameterType.String, Value = "{\"level1Key\" : {\"level2key\" : \"level2value\"}}" } }; - var result = _processor.ProcessParameters(parameters, "/test"); + var result = _parameterProcessor.ProcessParameters(parameters, "/test"); Assert.Equal(2, result.Count); Assert.True(result.ContainsKey("level1:level2:level1Key")); @@ -41,7 +41,7 @@ public void ProcessParameters_ParsesJsonParametersWithoutPrefixSuccessfully() Value = "{\"level1Key\" : {\"level2key\" : \"level2value\"}}" } }; - var result = _processor.ProcessParameters(parameters, ""); + var result = _parameterProcessor.ProcessParameters(parameters, ""); Assert.Equal(2, result.Count); Assert.True(result.ContainsKey("test:level1:level2:level1Key")); @@ -58,7 +58,7 @@ public void ProcessParameters_FallsBackOnString() { new Parameter() { Name = "/test/stringParam", Type = ParameterType.String, Value = "some string" } }; - var result = _processor.ProcessParameters(parameters, "/test"); + var result = _parameterProcessor.ProcessParameters(parameters, "/test"); Assert.Equal(1, result.Count); Assert.True(result.ContainsKey("stringParam")); @@ -73,7 +73,7 @@ public void ProcessParameters_ThrowsOnDuplicateParameter() new Parameter() { Name = "/test/Duplicate", Type = ParameterType.String, Value = "value1" }, new Parameter() { Name = "/test/duplicate", Type = ParameterType.String, Value = "value2" } }; - var duplicateParameterException = Assert.Throws(() => _processor.ProcessParameters(parameters, "/test")); + var duplicateParameterException = Assert.Throws(() => _parameterProcessor.ProcessParameters(parameters, "/test")); Assert.Equal("Duplicate parameter 'duplicate' found. Parameter keys are case-insensitive.", duplicateParameterException.Message); } @@ -86,7 +86,7 @@ public void ProcessParameters_ThrowsOnDuplicateParameterAtMultiLevel() new Parameter() { Name = "/test/level1/level1key", Type = ParameterType.String, Value = "level1valueOverriden" }, }; - var duplicateParameterException = Assert.Throws(() => _processor.ProcessParameters(parameters, "/test")); + var duplicateParameterException = Assert.Throws(() => _parameterProcessor.ProcessParameters(parameters, "/test")); Assert.Equal("Duplicate parameter 'level1:level1key' found. Parameter keys are case-insensitive.", duplicateParameterException.Message); } @@ -99,7 +99,7 @@ public void ProcessParameters_ThrowsOnDuplicateParameterAtMultilevelForJsonArray new Parameter() { Name = "/test/level1/Level1key", Type = ParameterType.StringList, Value = "level1valueOverriden" }, }; - var duplicateParameterException = Assert.Throws(() => _processor.ProcessParameters(parameters, "/test")); + var duplicateParameterException = Assert.Throws(() => _parameterProcessor.ProcessParameters(parameters, "/test")); Assert.Equal("Duplicate parameter 'level1:Level1key:0' found. Parameter keys are case-insensitive.", duplicateParameterException.Message); } @@ -111,7 +111,7 @@ public void ProcessParameters_ProcessesStringListParameters() { new Parameter() { Name = "/test/stringList", Type = ParameterType.StringList, Value = "value1,value2" } }; - var result = _processor.ProcessParameters(parameters, "/test"); + var result = _parameterProcessor.ProcessParameters(parameters, "/test"); Assert.Equal(2, result.Count); Assert.True(result.ContainsKey("stringList:0")); diff --git a/test/Amazon.Extensions.Configuration.SystemsManager.Tests/JsonParameterProcessorTests.cs b/test/Amazon.Extensions.Configuration.SystemsManager.Tests/JsonParameterProcessorTests.cs index 16c5d2f..ee64555 100644 --- a/test/Amazon.Extensions.Configuration.SystemsManager.Tests/JsonParameterProcessorTests.cs +++ b/test/Amazon.Extensions.Configuration.SystemsManager.Tests/JsonParameterProcessorTests.cs @@ -7,12 +7,7 @@ namespace Amazon.Extensions.Configuration.SystemsManager.Tests { public class JsonParameterProcessorTests { - private readonly IParameterProcessor _parameterProcessor; - - public JsonParameterProcessorTests() - { - _parameterProcessor = new JsonParameterProcessor(); - } + private readonly IParameterProcessor _parameterProcessor = new JsonParameterProcessor(); [Fact] public void ProcessParametersTest() From 47070ac325c9c79e740976f09d43ad9ecebd8b1a Mon Sep 17 00:00:00 2001 From: Snigdhajyoti Ghosh Date: Thu, 8 Aug 2024 00:46:59 +0530 Subject: [PATCH 5/8] add documentation and test for utility methods --- .../Utils/ParameterProcessorUtil.cs | 32 +++++- ...=> JsonOrStringParameterProcessorTests.cs} | 2 +- .../Utils/ParameterProcessorUtilTests.cs | 98 +++++++++++++++++++ 3 files changed, 127 insertions(+), 5 deletions(-) rename test/Amazon.Extensions.Configuration.SystemsManager.Tests/{JsonOrStringParameterProcessorTest.cs => JsonOrStringParameterProcessorTests.cs} (99%) create mode 100644 test/Amazon.Extensions.Configuration.SystemsManager.Tests/Utils/ParameterProcessorUtilTests.cs diff --git a/src/Amazon.Extensions.Configuration.SystemsManager/Utils/ParameterProcessorUtil.cs b/src/Amazon.Extensions.Configuration.SystemsManager/Utils/ParameterProcessorUtil.cs index 84ff4ba..8ecde10 100644 --- a/src/Amazon.Extensions.Configuration.SystemsManager/Utils/ParameterProcessorUtil.cs +++ b/src/Amazon.Extensions.Configuration.SystemsManager/Utils/ParameterProcessorUtil.cs @@ -3,11 +3,20 @@ using Amazon.Extensions.Configuration.SystemsManager.Internal; using Amazon.SimpleSystemsManagement.Model; using Microsoft.Extensions.Configuration; +using System.Text.Json; namespace Amazon.Extensions.Configuration.SystemsManager.Utils { public static class ParameterProcessorUtil { + /// + /// Parses the SSM parameter as JSON + /// + /// SSM parameter + /// prefix to add in configution key + /// append the parsed JSON value into + /// SSM parameter key is already present in + /// value does not represent a valid single JSON value. public static void ParseJsonParameter(Parameter parameter, string keyPrefix, IDictionary result) { foreach (var kv in JsonConfigurationParser.Parse(parameter.Value)) @@ -22,12 +31,20 @@ public static void ParseJsonParameter(Parameter parameter, string keyPrefix, IDi } } + /// + /// Parses the StringList SSM parameter as List of String + ///

+ /// Items in a StringList must be separated by a comma (,). + /// You can't use other punctuation or special characters to escape items in the list. + /// If you have a parameter value that requires a comma, then use the String type. + /// https://docs.aws.amazon.com/systems-manager/latest/userguide/param-create-cli.html#param-create-cli-stringlist + ///
+ /// SSM parameter + /// prefix to add in configution key + /// append the parsed string list value into + /// SSM parameter key is already present in public static void ParseStringListParameter(Parameter parameter, string keyPrefix, IDictionary result) { - // Items in a StringList must be separated by a comma (,). - // You can't use other punctuation or special characters to escape items in the list. - // If you have a parameter value that requires a comma, then use the String type. - // https://docs.aws.amazon.com/systems-manager/latest/userguide/param-create-cli.html#param-create-cli-stringlist var configKeyValuePairs = parameter.Value .Split(',') .Select((value, idx) => new KeyValuePair($"{keyPrefix}{ConfigurationPath.KeyDelimiter}{idx}", value)); @@ -43,6 +60,13 @@ public static void ParseStringListParameter(Parameter parameter, string keyPrefi } } + /// + /// Parses the SSM parameter as String + /// + /// SSM parameter + /// key to be used for configution key + /// append the parsed string value into + /// SSM parameter key is already present in public static void ParseStringParameter(Parameter parameter, string key, IDictionary result) { if (result.ContainsKey(key)) diff --git a/test/Amazon.Extensions.Configuration.SystemsManager.Tests/JsonOrStringParameterProcessorTest.cs b/test/Amazon.Extensions.Configuration.SystemsManager.Tests/JsonOrStringParameterProcessorTests.cs similarity index 99% rename from test/Amazon.Extensions.Configuration.SystemsManager.Tests/JsonOrStringParameterProcessorTest.cs rename to test/Amazon.Extensions.Configuration.SystemsManager.Tests/JsonOrStringParameterProcessorTests.cs index fba2a14..700ddc2 100644 --- a/test/Amazon.Extensions.Configuration.SystemsManager.Tests/JsonOrStringParameterProcessorTest.cs +++ b/test/Amazon.Extensions.Configuration.SystemsManager.Tests/JsonOrStringParameterProcessorTests.cs @@ -5,7 +5,7 @@ namespace Amazon.Extensions.Configuration.SystemsManager.Tests { - public class JsonOrStringParameterProcessorTest + public class JsonOrStringParameterProcessorTests { private readonly IParameterProcessor _parameterProcessor = new JsonOrStringParameterProcessor(); diff --git a/test/Amazon.Extensions.Configuration.SystemsManager.Tests/Utils/ParameterProcessorUtilTests.cs b/test/Amazon.Extensions.Configuration.SystemsManager.Tests/Utils/ParameterProcessorUtilTests.cs new file mode 100644 index 0000000..fe66036 --- /dev/null +++ b/test/Amazon.Extensions.Configuration.SystemsManager.Tests/Utils/ParameterProcessorUtilTests.cs @@ -0,0 +1,98 @@ +using System.Collections.Generic; +using System.Text.Json; +using Amazon.Extensions.Configuration.SystemsManager.Utils; +using Amazon.SimpleSystemsManagement.Model; +using Xunit; + +namespace Amazon.Extensions.Configuration.SystemsManager.Tests.Utils +{ + public class ParameterProcessorUtilTests + { + [Fact] + public void ParseJsonParameterSuccessfully() + { + var result = new Dictionary(); + var parameter = new Parameter { Value = "{\"key\": \"value\"}" }; + var keyPrefix = "prefix"; + + ParameterProcessorUtil.ParseJsonParameter(parameter, keyPrefix, result); + + Assert.Single(result); + Assert.Contains("prefix:key", result.Keys); + Assert.Equal("value", result["prefix:key"]); + } + + [Fact] + public void ParseJsonParameterWithDuplicateKeyThrowsException() + { + var result = new Dictionary { { "prefix:key", "value" } }; + var parameter = new Parameter { Value = "{\"key\": \"newvalue\"}" }; + var keyPrefix = "prefix"; + + Assert.Throws(() => ParameterProcessorUtil.ParseJsonParameter(parameter, keyPrefix, result)); + } + + [Fact] + public void ParseJsonParameterForInvalidJsonThrowsException() + { + var result = new Dictionary(); + var parameter = new Parameter { Value = "invalid json" }; + var keyPrefix = ""; + + Assert.ThrowsAny(() => ParameterProcessorUtil.ParseJsonParameter(parameter, keyPrefix, result)); + } + + [Fact] + public void ParseStringListParameterSuccessfully() + { + var result = new Dictionary(); + var parameter = new Parameter { Value = "value1,value2,value3" }; + var keyPrefix = "prefix"; + + ParameterProcessorUtil.ParseStringListParameter(parameter, keyPrefix, result); + + Assert.Equal(3, result.Count); + Assert.Contains("prefix:0", result.Keys); + Assert.Contains("prefix:1", result.Keys); + Assert.Contains("prefix:2", result.Keys); + Assert.Equal("value1", result["prefix:0"]); + Assert.Equal("value2", result["prefix:1"]); + Assert.Equal("value3", result["prefix:2"]); + } + + [Fact] + public void ParseStringListParameterWithDuplicateKeyThrowsException() + { + var result = new Dictionary { { "prefix:0", "value" } }; + var parameter = new Parameter { Value = "value1,value2,value3" }; + var keyPrefix = "prefix"; + + Assert.Throws(() => ParameterProcessorUtil.ParseStringListParameter(parameter, keyPrefix, result)); + } + + [Fact] + public void ParseStringParameterSuccessfully() + { + var result = new Dictionary(); + var parameter = new Parameter { Value = "stringValue" }; + var key = "myKey"; + + ParameterProcessorUtil.ParseStringParameter(parameter, key, result); + + Assert.Single(result); + Assert.Contains("myKey", result.Keys); + Assert.Equal("stringValue", result["myKey"]); + } + + [Fact] + public void ParseStringParameterWithDuplicateKeyThrowsException() + { + var result = new Dictionary { { "myKey", "existingValue" } }; + var parameter = new Parameter { Value = "newValue" }; + var key = "myKey"; + + Assert.Throws(() => ParameterProcessorUtil.ParseStringParameter(parameter, key, result)); + } + + } +} \ No newline at end of file From e69d16f70b2c189d9da9aabb884ea7f6ea5600d0 Mon Sep 17 00:00:00 2001 From: Snigdhajyoti Ghosh Date: Thu, 8 Aug 2024 01:03:22 +0530 Subject: [PATCH 6/8] use GetKey and GetValue to be able to override how parameter key, value is being fetched --- .../DefaultParameterProcessor.cs | 7 ++-- .../JsonOrStringParameterProcessor.cs | 9 +++--- .../JsonParameterProcessor.cs | 5 +-- .../Utils/ParameterProcessorUtil.cs | 23 +++++++------ .../Utils/ParameterProcessorUtilTests.cs | 32 +++++++++---------- 5 files changed, 38 insertions(+), 38 deletions(-) diff --git a/src/Amazon.Extensions.Configuration.SystemsManager/DefaultParameterProcessor.cs b/src/Amazon.Extensions.Configuration.SystemsManager/DefaultParameterProcessor.cs index 5e51616..0072973 100644 --- a/src/Amazon.Extensions.Configuration.SystemsManager/DefaultParameterProcessor.cs +++ b/src/Amazon.Extensions.Configuration.SystemsManager/DefaultParameterProcessor.cs @@ -55,15 +55,16 @@ public virtual IDictionary ProcessParameters(IEnumerable IncludeParameter(parameter, path))) { - var prefix = GetKey(parameter, path); + var keyPrefix = GetKey(parameter, path); + var value = GetValue(parameter, path); if (parameter.Type == ParameterType.StringList) { - ParseStringListParameter(parameter, prefix, result); + ParseStringListParameter(keyPrefix, value, result); continue; } - ParseStringParameter(parameter, prefix, result); + ParseStringParameter(keyPrefix, value, result); } return result; diff --git a/src/Amazon.Extensions.Configuration.SystemsManager/JsonOrStringParameterProcessor.cs b/src/Amazon.Extensions.Configuration.SystemsManager/JsonOrStringParameterProcessor.cs index bc97ace..f77dcb2 100644 --- a/src/Amazon.Extensions.Configuration.SystemsManager/JsonOrStringParameterProcessor.cs +++ b/src/Amazon.Extensions.Configuration.SystemsManager/JsonOrStringParameterProcessor.cs @@ -21,21 +21,22 @@ public override IDictionary ProcessParameters(IEnumerable IncludeParameter(parameter, path))) { - var prefix = GetKey(parameter, path); + var keyPrefix = GetKey(parameter, path); + var value = GetValue(parameter, path); if (parameter.Type == ParameterType.StringList) { - ParseStringListParameter(parameter, prefix, result); + ParseStringListParameter(keyPrefix, value, result); continue; } try { - ParseJsonParameter(parameter, prefix, result); + ParseJsonParameter(keyPrefix, value, result); } catch (JsonException) { - ParseStringParameter(parameter, prefix, result); + ParseStringParameter(keyPrefix, value, result); } } diff --git a/src/Amazon.Extensions.Configuration.SystemsManager/JsonParameterProcessor.cs b/src/Amazon.Extensions.Configuration.SystemsManager/JsonParameterProcessor.cs index aa8c68a..a85f2fb 100644 --- a/src/Amazon.Extensions.Configuration.SystemsManager/JsonParameterProcessor.cs +++ b/src/Amazon.Extensions.Configuration.SystemsManager/JsonParameterProcessor.cs @@ -34,9 +34,10 @@ public override IDictionary ProcessParameters(IEnumerable IncludeParameter(parameter, path))) { - var prefix = GetKey(parameter, path); + var keyPrefix = GetKey(parameter, path); + var value = GetValue(parameter, path); - ParseJsonParameter(parameter, prefix, result); + ParseJsonParameter(keyPrefix, value, result); } return result; diff --git a/src/Amazon.Extensions.Configuration.SystemsManager/Utils/ParameterProcessorUtil.cs b/src/Amazon.Extensions.Configuration.SystemsManager/Utils/ParameterProcessorUtil.cs index 8ecde10..7428078 100644 --- a/src/Amazon.Extensions.Configuration.SystemsManager/Utils/ParameterProcessorUtil.cs +++ b/src/Amazon.Extensions.Configuration.SystemsManager/Utils/ParameterProcessorUtil.cs @@ -1,7 +1,6 @@ using System.Collections.Generic; using System.Linq; using Amazon.Extensions.Configuration.SystemsManager.Internal; -using Amazon.SimpleSystemsManagement.Model; using Microsoft.Extensions.Configuration; using System.Text.Json; @@ -12,14 +11,14 @@ public static class ParameterProcessorUtil /// /// Parses the SSM parameter as JSON /// - /// SSM parameter /// prefix to add in configution key + /// SSM parameter value /// append the parsed JSON value into /// SSM parameter key is already present in - /// value does not represent a valid single JSON value. - public static void ParseJsonParameter(Parameter parameter, string keyPrefix, IDictionary result) + /// does not represent a valid single JSON value. + public static void ParseJsonParameter(string keyPrefix, string value, IDictionary result) { - foreach (var kv in JsonConfigurationParser.Parse(parameter.Value)) + foreach (var kv in JsonConfigurationParser.Parse(value)) { var key = !string.IsNullOrEmpty(keyPrefix) ? ConfigurationPath.Combine(keyPrefix, kv.Key) : kv.Key; if (result.ContainsKey(key)) @@ -39,15 +38,15 @@ public static void ParseJsonParameter(Parameter parameter, string keyPrefix, IDi /// If you have a parameter value that requires a comma, then use the String type. /// https://docs.aws.amazon.com/systems-manager/latest/userguide/param-create-cli.html#param-create-cli-stringlist /// - /// SSM parameter /// prefix to add in configution key + /// SSM parameter /// append the parsed string list value into /// SSM parameter key is already present in - public static void ParseStringListParameter(Parameter parameter, string keyPrefix, IDictionary result) + public static void ParseStringListParameter(string keyPrefix, string value, IDictionary result) { - var configKeyValuePairs = parameter.Value + var configKeyValuePairs = value .Split(',') - .Select((value, idx) => new KeyValuePair($"{keyPrefix}{ConfigurationPath.KeyDelimiter}{idx}", value)); + .Select((eachValue, idx) => new KeyValuePair($"{keyPrefix}{ConfigurationPath.KeyDelimiter}{idx}", eachValue)); foreach (var kv in configKeyValuePairs) { @@ -63,18 +62,18 @@ public static void ParseStringListParameter(Parameter parameter, string keyPrefi /// /// Parses the SSM parameter as String /// - /// SSM parameter /// key to be used for configution key + /// SSM parameter /// append the parsed string value into /// SSM parameter key is already present in - public static void ParseStringParameter(Parameter parameter, string key, IDictionary result) + public static void ParseStringParameter(string key, string value, IDictionary result) { if (result.ContainsKey(key)) { throw new DuplicateParameterException($"Duplicate parameter '{key}' found. Parameter keys are case-insensitive."); } - result.Add(key, parameter.Value); + result.Add(key, value); } } } \ No newline at end of file diff --git a/test/Amazon.Extensions.Configuration.SystemsManager.Tests/Utils/ParameterProcessorUtilTests.cs b/test/Amazon.Extensions.Configuration.SystemsManager.Tests/Utils/ParameterProcessorUtilTests.cs index fe66036..3b4e5cd 100644 --- a/test/Amazon.Extensions.Configuration.SystemsManager.Tests/Utils/ParameterProcessorUtilTests.cs +++ b/test/Amazon.Extensions.Configuration.SystemsManager.Tests/Utils/ParameterProcessorUtilTests.cs @@ -1,7 +1,6 @@ using System.Collections.Generic; using System.Text.Json; using Amazon.Extensions.Configuration.SystemsManager.Utils; -using Amazon.SimpleSystemsManagement.Model; using Xunit; namespace Amazon.Extensions.Configuration.SystemsManager.Tests.Utils @@ -12,10 +11,10 @@ public class ParameterProcessorUtilTests public void ParseJsonParameterSuccessfully() { var result = new Dictionary(); - var parameter = new Parameter { Value = "{\"key\": \"value\"}" }; + var value = "{\"key\": \"value\"}"; var keyPrefix = "prefix"; - ParameterProcessorUtil.ParseJsonParameter(parameter, keyPrefix, result); + ParameterProcessorUtil.ParseJsonParameter(keyPrefix, value, result); Assert.Single(result); Assert.Contains("prefix:key", result.Keys); @@ -26,30 +25,30 @@ public void ParseJsonParameterSuccessfully() public void ParseJsonParameterWithDuplicateKeyThrowsException() { var result = new Dictionary { { "prefix:key", "value" } }; - var parameter = new Parameter { Value = "{\"key\": \"newvalue\"}" }; + var value = "{\"key\": \"newvalue\"}"; var keyPrefix = "prefix"; - Assert.Throws(() => ParameterProcessorUtil.ParseJsonParameter(parameter, keyPrefix, result)); + Assert.Throws(() => ParameterProcessorUtil.ParseJsonParameter(keyPrefix, value, result)); } [Fact] public void ParseJsonParameterForInvalidJsonThrowsException() { var result = new Dictionary(); - var parameter = new Parameter { Value = "invalid json" }; + var value = "invalid json"; var keyPrefix = ""; - Assert.ThrowsAny(() => ParameterProcessorUtil.ParseJsonParameter(parameter, keyPrefix, result)); + Assert.ThrowsAny(() => ParameterProcessorUtil.ParseJsonParameter(keyPrefix, value, result)); } [Fact] public void ParseStringListParameterSuccessfully() { var result = new Dictionary(); - var parameter = new Parameter { Value = "value1,value2,value3" }; + var value = "value1,value2,value3"; var keyPrefix = "prefix"; - ParameterProcessorUtil.ParseStringListParameter(parameter, keyPrefix, result); + ParameterProcessorUtil.ParseStringListParameter(keyPrefix, value, result); Assert.Equal(3, result.Count); Assert.Contains("prefix:0", result.Keys); @@ -64,20 +63,20 @@ public void ParseStringListParameterSuccessfully() public void ParseStringListParameterWithDuplicateKeyThrowsException() { var result = new Dictionary { { "prefix:0", "value" } }; - var parameter = new Parameter { Value = "value1,value2,value3" }; + var value = "value1,value2,value3"; var keyPrefix = "prefix"; - Assert.Throws(() => ParameterProcessorUtil.ParseStringListParameter(parameter, keyPrefix, result)); + Assert.Throws(() => ParameterProcessorUtil.ParseStringListParameter(keyPrefix, value, result)); } - + [Fact] public void ParseStringParameterSuccessfully() { var result = new Dictionary(); - var parameter = new Parameter { Value = "stringValue" }; + var value = "stringValue"; var key = "myKey"; - ParameterProcessorUtil.ParseStringParameter(parameter, key, result); + ParameterProcessorUtil.ParseStringParameter(key, value, result); Assert.Single(result); Assert.Contains("myKey", result.Keys); @@ -88,11 +87,10 @@ public void ParseStringParameterSuccessfully() public void ParseStringParameterWithDuplicateKeyThrowsException() { var result = new Dictionary { { "myKey", "existingValue" } }; - var parameter = new Parameter { Value = "newValue" }; + var value = "newValue"; var key = "myKey"; - Assert.Throws(() => ParameterProcessorUtil.ParseStringParameter(parameter, key, result)); + Assert.Throws(() => ParameterProcessorUtil.ParseStringParameter(key, value, result)); } - } } \ No newline at end of file From 86f90278ce2bffbd70f2bd82a950d52f525103b0 Mon Sep 17 00:00:00 2001 From: Snigdhajyoti Ghosh Date: Thu, 8 Aug 2024 01:54:58 +0530 Subject: [PATCH 7/8] fix test naming to adhere to existing naming strategy --- .../JsonOrStringParameterProcessorTests.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/test/Amazon.Extensions.Configuration.SystemsManager.Tests/JsonOrStringParameterProcessorTests.cs b/test/Amazon.Extensions.Configuration.SystemsManager.Tests/JsonOrStringParameterProcessorTests.cs index 700ddc2..52323c5 100644 --- a/test/Amazon.Extensions.Configuration.SystemsManager.Tests/JsonOrStringParameterProcessorTests.cs +++ b/test/Amazon.Extensions.Configuration.SystemsManager.Tests/JsonOrStringParameterProcessorTests.cs @@ -27,7 +27,7 @@ public void ParsesJsonParametersSuccessfully() } [Fact] - public void ProcessParameters_ParsesJsonParametersWithoutPrefixSuccessfully() + public void ProcessParametersParsesJsonParametersWithoutPrefixSuccessfully() { var parameters = new List { @@ -52,7 +52,7 @@ public void ProcessParameters_ParsesJsonParametersWithoutPrefixSuccessfully() [Fact] - public void ProcessParameters_FallsBackOnString() + public void ProcessParametersFallBackOnString() { var parameters = new List { @@ -66,7 +66,7 @@ public void ProcessParameters_FallsBackOnString() } [Fact] - public void ProcessParameters_ThrowsOnDuplicateParameter() + public void ProcessParametersThrowsOnDuplicateParameter() { var parameters = new List { @@ -78,7 +78,7 @@ public void ProcessParameters_ThrowsOnDuplicateParameter() } [Fact] - public void ProcessParameters_ThrowsOnDuplicateParameterAtMultiLevel() + public void ProcessParametersThrowsOnDuplicateParameterAtMultiLevel() { var parameters = new List { @@ -91,7 +91,7 @@ public void ProcessParameters_ThrowsOnDuplicateParameterAtMultiLevel() } [Fact] - public void ProcessParameters_ThrowsOnDuplicateParameterAtMultilevelForJsonArray() + public void ProcessParametersThrowsOnDuplicateParameterAtMultilevelForJsonArray() { var parameters = new List { @@ -105,7 +105,7 @@ public void ProcessParameters_ThrowsOnDuplicateParameterAtMultilevelForJsonArray [Fact] - public void ProcessParameters_ProcessesStringListParameters() + public void ProcessParametersProcessesStringListParameters() { var parameters = new List { From 10aa9678ada4b3a1fcafbecf532e72c715ce6917 Mon Sep 17 00:00:00 2001 From: Snigdhajyoti Ghosh Date: Tue, 18 Feb 2025 01:45:34 +0530 Subject: [PATCH 8/8] add autover file --- .../changes/44bf0928-edf9-4888-aebb-196b6364ffa8.json | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 .autover/changes/44bf0928-edf9-4888-aebb-196b6364ffa8.json diff --git a/.autover/changes/44bf0928-edf9-4888-aebb-196b6364ffa8.json b/.autover/changes/44bf0928-edf9-4888-aebb-196b6364ffa8.json new file mode 100644 index 0000000..66b3fab --- /dev/null +++ b/.autover/changes/44bf0928-edf9-4888-aebb-196b6364ffa8.json @@ -0,0 +1,11 @@ +{ + "Projects": [ + { + "Name": "Amazon.Extensions.Configuration.SystemsManager", + "Type": "Minor", + "ChangelogMessages": [ + "Add opt-in JsonOrStringParameterProcessor processor supporting parameters being either strings or JSON" + ] + } + ] +} \ No newline at end of file