Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions .github/workflows/change-file-in-pr.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
name: Change File Included in PR

on:
pull_request:
types: [opened, synchronize, reopened, labeled]

jobs:
check-files-in-directory:
if: ${{ !contains(github.event.pull_request.labels.*.name, 'Release Not Needed') && !contains(github.event.pull_request.labels.*.name, 'Release PR') }}
name: Change File Included in PR
runs-on: ubuntu-latest

steps:
- name: Checkout PR code
uses: actions/checkout@v3

- name: Get List of Changed Files
id: changed-files
uses: tj-actions/changed-files@4edd678ac3f81e2dc578756871e4d00c19191daf #v45

- name: Check for Change File(s) in .autover/changes/
run: |
DIRECTORY=".autover/changes/"
if echo "${{ steps.changed-files.outputs.all_changed_files }}" | grep -q "$DIRECTORY"; then
echo "✅ One or more change files in '$DIRECTORY' are included in this PR."
else
echo "❌ No change files in '$DIRECTORY' are included in this PR."
echo "Refer to the 'Adding a change file to your contribution branch' section of https://github.com/aws/aws-dotnet-extensions-configuration/blob/master/CONTRIBUTING.md"
exit 1
fi
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
## Release 2025-02-20

### Amazon.Extensions.Configuration.SystemsManager (6.3.0)
* Add opt-in JsonOrStringParameterProcessor processor supporting parameters being either strings or JSON

## Release 2024-10-17

### Amazon.Extensions.Configuration.SystemsManager (7.0.0-preview.1)
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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
{
Expand All @@ -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);
Expand All @@ -47,51 +49,25 @@ public virtual string GetKey(Parameter parameter, string path)

public virtual string GetValue(Parameter parameter, string path) => parameter.Value;

private IEnumerable<KeyValuePair<string, string>> 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<string, string>($"{GetKey(parameter, path)}:{idx}", value));
}

public virtual IDictionary<string, string> ProcessParameters(IEnumerable<Parameter> parameters, string path)
{
var result = new List<KeyValuePair<string, string>>();
var result = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);

foreach (var parameter in parameters.Where(parameter => IncludeParameter(parameter, path)))
{
var keyPrefix = GetKey(parameter, path);
var value = GetValue(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(keyPrefix, value, 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<string, string>(parameterKey, GetValue(parameter, path)));
}
ParseStringParameter(keyPrefix, value, result);
}

return result.ToDictionary(parameter => parameter.Key, parameter => parameter.Value,
StringComparer.OrdinalIgnoreCase);
return result;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
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;

namespace Amazon.Extensions.Configuration.SystemsManager
{
/// <inheritdoc />
/// <summary>
/// A processor that prioritizes JSON parameters but falls back to string parameters,
/// in accordance with Systems Manager's suggested naming conventions
/// </summary>
public class JsonOrStringParameterProcessor : DefaultParameterProcessor
{
public override IDictionary<string, string> ProcessParameters(IEnumerable<Parameter> parameters, string path)
{
var result = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);

foreach (var parameter in parameters.Where(parameter => IncludeParameter(parameter, path)))
{
var keyPrefix = GetKey(parameter, path);
var value = GetValue(parameter, path);

if (parameter.Type == ParameterType.StringList)
{
ParseStringListParameter(keyPrefix, value, result);
continue;
}

try
{
ParseJsonParameter(keyPrefix, value, result);
}
catch (JsonException)
{
ParseStringParameter(keyPrefix, value, result);
}
}

return result;
}
}
}
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -16,42 +16,31 @@
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
{
/// <inheritdoc />
/// <summary>
/// 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
/// </summary>
public class JsonParameterProcessor : DefaultParameterProcessor
{
public override IDictionary<string, string> ProcessParameters(IEnumerable<Parameter> parameters, string path)
{
var outputDictionary = new Dictionary<string, string>(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<string, string>(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 keyPrefix = GetKey(parameter, path);
var value = GetValue(parameter, path);

outputDictionary.Add(key, keyValue.Value);
}
ParseJsonParameter(keyPrefix, value, result);
}

return outputDictionary;
return result;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
using System.Collections.Generic;
using System.Linq;
using Amazon.Extensions.Configuration.SystemsManager.Internal;
using Microsoft.Extensions.Configuration;
using System.Text.Json;

namespace Amazon.Extensions.Configuration.SystemsManager.Utils
{
public static class ParameterProcessorUtil
{
/// <summary>
/// Parses the SSM parameter as JSON
/// </summary>
/// <param name="keyPrefix">prefix to add in configution key</param>
/// <param name="value">SSM parameter value</param>
/// <param name="result">append the parsed JSON value into</param>
/// <exception cref="DuplicateParameterException">SSM parameter key is already present in <paramref name="result"/></exception>
/// <exception cref="JsonException"><paramref name="value" /> does not represent a valid single JSON value.</exception>
public static void ParseJsonParameter(string keyPrefix, string value, IDictionary<string, string> result)
{
foreach (var kv in JsonConfigurationParser.Parse(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);
}
}

/// <summary>
/// Parses the StringList SSM parameter as List of String
/// <br/><br/>
/// 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
/// </summary>
/// <param name="keyPrefix">prefix to add in configution key</param>
/// <param name="value">SSM parameter</param>
/// <param name="result">append the parsed string list value into</param>
/// <exception cref="DuplicateParameterException">SSM parameter key is already present in <paramref name="result"/></exception>
public static void ParseStringListParameter(string keyPrefix, string value, IDictionary<string, string> result)
{
var configKeyValuePairs = value
.Split(',')
.Select((eachValue, idx) => new KeyValuePair<string, string>($"{keyPrefix}{ConfigurationPath.KeyDelimiter}{idx}", eachValue));

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);
}
}

/// <summary>
/// Parses the SSM parameter as String
/// </summary>
/// <param name="key">key to be used for configution key</param>
/// <param name="value">SSM parameter</param>
/// <param name="result">append the parsed string value into</param>
/// <exception cref="DuplicateParameterException">SSM parameter key is already present in <paramref name="result"/></exception>
public static void ParseStringParameter(string key, string value, IDictionary<string, string> result)
{
if (result.ContainsKey(key))
{
throw new DuplicateParameterException($"Duplicate parameter '{key}' found. Parameter keys are case-insensitive.");
}

result.Add(key, value);
}
}
}
Loading
Loading