Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Adding OpenFeature provider for AWS AppConfig #310

Open
wants to merge 44 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
ba0a4e4
chore(main): release OpenFeature.Contrib.Providers.Flipt 0.0.5 (#302)
github-actions[bot] Oct 18, 2024
7ef76a4
Initial project and test project for aws appconfig provider
wani-guanxi Jan 4, 2025
49e6e86
added project refrence to test project
wani-guanxi Jan 4, 2025
bd9fc74
Create AwsAppCofnig Provider
wani-guanxi Jan 4, 2025
2d9cadf
adding Resolve Function for other types
wani-guanxi Jan 6, 2025
5b11cdf
created separate static class for Aws app config parsing
wani-guanxi Jan 6, 2025
1c1001a
removed unused usings
wani-guanxi Jan 6, 2025
eb96056
added constructor to AppConfig provider
wani-guanxi Jan 6, 2025
ae84a9f
cleaned and reused code, and also now accepting key in flagKey:attrib…
wani-guanxi Jan 7, 2025
ca4479a
put some more checkts in ResolveFeatureFlagValue function
wani-guanxi Jan 9, 2025
81716b8
Adding AppConfigRetriebalAPI class with memory caching for app config…
wani-guanxi Jan 9, 2025
dd40afa
updating constructor and how configuration profile id is passed along
wani-guanxi Jan 9, 2025
1ffe667
Adding extension function for adding OpenFeature AppConfig provider
wani-guanxi Jan 10, 2025
668ccf8
Added comments for OpenFeature extension class
wani-guanxi Jan 10, 2025
7c58dcc
adding ReadMe.md file with details about usable of the nuget package
wani-guanxi Jan 15, 2025
8fbea1a
mermaid in Readme
wani-guanxi Jan 16, 2025
c4951b5
Update README.md
wani-guanxi Jan 16, 2025
96b5ee4
Update README.md
wani-guanxi Jan 16, 2025
0be3b5a
Update README.md
wani-guanxi Jan 16, 2025
d0c38fa
Update README.md
wani-guanxi Jan 16, 2025
648668c
Update README.md
wani-guanxi Jan 16, 2025
1b4d148
Update README.md
wani-guanxi Jan 16, 2025
ce982c9
Updating Readme
wani-guanxi Jan 16, 2025
cf24964
Update README.md
wani-guanxi Jan 16, 2025
2c71695
Update README.md
wani-guanxi Jan 16, 2025
c7af807
Update README.md
wani-guanxi Jan 16, 2025
44e4597
Update README.md
wani-guanxi Jan 16, 2025
3f626f7
Update README.md
wani-guanxi Jan 16, 2025
93a141e
adding tests
wani-guanxi Jan 16, 2025
5aa4875
added more tests
wani-guanxi Jan 16, 2025
ac3cfd6
added more tests
wani-guanxi Jan 16, 2025
178a15c
Adding FeatureFlagParser class's unit tests
wani-guanxi Jan 17, 2025
e9186a9
tests for AppConfigProvider
wani-guanxi Jan 17, 2025
4057e5c
fixed test cases for appconfig provider
wani-guanxi Jan 17, 2025
3791029
fixed tests for RetrievalAPi
wani-guanxi Jan 18, 2025
ca58683
Updating for allowing RequiredMinimumPollIntervalInSeconds property t…
wani-guanxi Jan 18, 2025
81e53f5
added important information section
wani-guanxi Jan 18, 2025
288a1e2
update readme
wani-guanxi Jan 18, 2025
c5f048c
Update README.md for Multi-variant flag information.
wani-guanxi Jan 18, 2025
f2a9907
fix: AppConfig through exception if calling too frequently, use whats…
wani-guanxi Jan 21, 2025
79e4f0b
Update FeatureFlagProfile.cs - updated comments
wani-guanxi Jan 18, 2025
a14bc85
fixing broken test after exception catching when calling AWS
wani-guanxi Jan 27, 2025
c31db95
modifying comment to signoff
wani-guanxi Jan 27, 2025
7455bb7
Merge branch 'main' into feature/appconfig-provider
wani-guanxi Jan 27, 2025
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
14 changes: 14 additions & 0 deletions DotnetSdkContrib.sln
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenFeature.Contrib.Provide
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenFeature.Contrib.Providers.Flipt.Test", "test\OpenFeature.Contrib.Providers.Flipt.Test\OpenFeature.Contrib.Providers.Flipt.Test.csproj", "{B446D481-B5A3-4509-8933-C4CF6DA9B147}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenFeature.Contrib.Providers.AwsAppConfig", "src\OpenFeature.Contrib.Providers.AwsAppConfig\OpenFeature.Contrib.Providers.AwsAppConfig.csproj", "{B83B3CA7-7CFD-4915-A5D9-7A88372EA331}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenFeature.Contrib.Providers.AwsAppConfig.Test", "test\OpenFeature.Contrib.Providers.AwsAppConfig.Test\OpenFeature.Contrib.Providers.AwsAppConfig.Test.csproj", "{222FAE13-8472-4B7A-B6D3-BF07953B953A}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -127,6 +131,14 @@ Global
{B446D481-B5A3-4509-8933-C4CF6DA9B147}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B446D481-B5A3-4509-8933-C4CF6DA9B147}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B446D481-B5A3-4509-8933-C4CF6DA9B147}.Release|Any CPU.Build.0 = Release|Any CPU
{B83B3CA7-7CFD-4915-A5D9-7A88372EA331}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B83B3CA7-7CFD-4915-A5D9-7A88372EA331}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B83B3CA7-7CFD-4915-A5D9-7A88372EA331}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B83B3CA7-7CFD-4915-A5D9-7A88372EA331}.Release|Any CPU.Build.0 = Release|Any CPU
{222FAE13-8472-4B7A-B6D3-BF07953B953A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{222FAE13-8472-4B7A-B6D3-BF07953B953A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{222FAE13-8472-4B7A-B6D3-BF07953B953A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{222FAE13-8472-4B7A-B6D3-BF07953B953A}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -151,5 +163,7 @@ Global
{F3080350-B0AB-4D59-B416-50CC38C99087} = {B6D3230B-5E4D-4FF1-868E-2F4E325C84FE}
{5ECF7DBF-FE64-40A2-BF39-239DE173DA4B} = {0E563821-BD08-4B7F-BF9D-395CAD80F026}
{B446D481-B5A3-4509-8933-C4CF6DA9B147} = {B6D3230B-5E4D-4FF1-868E-2F4E325C84FE}
{B83B3CA7-7CFD-4915-A5D9-7A88372EA331} = {0E563821-BD08-4B7F-BF9D-395CAD80F026}
{222FAE13-8472-4B7A-B6D3-BF07953B953A} = {B6D3230B-5E4D-4FF1-868E-2F4E325C84FE}
EndGlobalSection
EndGlobal
146 changes: 146 additions & 0 deletions src/OpenFeature.Contrib.Providers.AwsAppConfig/AppConfigKey.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
using System;
using System.Text.RegularExpressions;

namespace OpenFeature.Contrib.Providers.AwsAppConfig
{
/// <summary>
/// Represents a key structure for AWS AppConfig feature flags with optional attributes.
/// Keys can be in the format "flagKey" or "flagKey:attributeKey"
/// </summary>
public class AppConfigKey
{
/// <summary>
/// The separator used to split the flag key from its attribute key
/// </summary>
private const string Separator = ":";

/// <summary>
/// Gets the App config's Configuration Profile ID
/// </summary>
public string ConfigurationProfileId {get; }

/// <summary>
/// Gets the main feature flag key
/// </summary>
public string FlagKey { get; }

/// <summary>
/// Gets the optional attribute key associated with the feature flag
/// </summary>
public string AttributeKey { get; }

/// <summary>
/// Gets whether this key has an attribute component
/// </summary>
public bool HasAttribute => !string.IsNullOrWhiteSpace(AttributeKey);

/// <summary>
/// Initializes a new instance of the <see cref="AppConfigKey"/> class that represents a structured key
/// for AWS AppConfig feature flags.
/// </summary>
/// <param name="key">
/// The composite key string that must be in the format "configurationProfileId:flagKey[:attributeKey]" where:
/// <list type="bullet">
/// <item><description>configurationProfileId - The AWS AppConfig configuration profile identifier</description></item>
/// <item><description>flagKey - The feature flag key name</description></item>
/// <item><description>attributeKey - (Optional) The specific attribute key to access within the feature flag</description></item>
/// </list>
/// </param>
/// <exception cref="ArgumentException">
/// Thrown when:
/// <list type="bullet">
/// <item><description>The key parameter is null, empty, or consists only of whitespace</description></item>
/// <item><description>The key format is invalid (missing required parts)</description></item>
/// <item><description>The key doesn't contain at least configurationProfileId and flagKey parts</description></item>
/// </list>
/// </exception>
/// <remarks>
/// The constructor parses the provided key string and populates the corresponding properties:
/// <list type="bullet">
/// <item><description><see cref="ConfigurationProfileId"/> - First part of the key</description></item>
/// <item><description><see cref="FlagKey"/> - Second part of the key</description></item>
/// <item><description><see cref="AttributeKey"/> - Third part of the key (if provided)</description></item>
/// </list>
/// </remarks>
/// <example>
/// Valid key formats:
/// <code>
/// // Basic usage with configuration profile and flag key
/// var key1 = new AppConfigKey("myProfile:myFlag");
///
/// // Usage with an attribute key
/// var key2 = new AppConfigKey("myProfile:myFlag:myAttribute");
/// </code>
/// </example>
public AppConfigKey(string key)
{
if(string.IsNullOrWhiteSpace(key))
{
throw new ArgumentException("Key cannot be null or empty");
}

// Regular expression for validating the format with last part as 0 or 1 or empty
string pattern = @"^[^:]+:[^:]+(:[^:]+)?$";
var match = Regex.IsMatch(key, pattern);

if(!match) throw new ArgumentException("Invalid key format. Flag key is expected in configurationProfileId:flagKey[:attributeKey] format");


var parts = key.Split(Separator, StringSplitOptions.RemoveEmptyEntries);

if(parts.Length < 2 )
{
throw new ArgumentException("Invalid key format. Flag key is expected in configurationProfileId:flagKey[:attributeKey] format");
}

ConfigurationProfileId = parts[0];
FlagKey = parts[1];
// At this point, AWS AppConfig allows only value types for attributes.
// Hence ignoring anything afterwords.
if (parts.Length > 2)
{
AttributeKey = parts[2];
}
}

/// <summary>
/// Constructs an AppConfigKey using individual components.
/// </summary>
/// <param name="configurationProfileId">The AWS AppConfig configuration profile identifier</param>
/// <param name="flagKey">The feature flag key name</param>
/// <param name="attributeKey">Optional. The specific attribute key to access</param>
/// <exception cref="ArgumentException">
/// Thrown when:
/// - configurationProfileId is null, empty, or whitespace
/// - flagKey is null, empty, or whitespace
/// </exception>
public AppConfigKey(string configurationProfileId, string flagKey, string attributeKey = null)
{
if (string.IsNullOrWhiteSpace(configurationProfileId))
{
throw new ArgumentNullException("Configuration Profile ID cannot be null or empty");
}

if (string.IsNullOrWhiteSpace(flagKey))
{
throw new ArgumentNullException("Flag key cannot be null or empty");
}

ConfigurationProfileId = configurationProfileId;
FlagKey = flagKey;
AttributeKey = attributeKey;
}

/// <summary>
/// Converts the AppConfigKey object back to its string representation.
/// </summary>
/// <returns>
/// A string in the format "configurationProfileId:flagKey[:attributeKey]".
/// The attributeKey part is only included if it exists.
/// </returns>
public override string ToString()
{
return $"{ConfigurationProfileId}{Separator}{FlagKey}{(HasAttribute ? Separator + AttributeKey : "")}";
}
}
}
Loading
Loading