Skip to content

Commit 4593f42

Browse files
committed
add feature flags helper methods
1 parent b538e1e commit 4593f42

File tree

4 files changed

+216
-21
lines changed

4 files changed

+216
-21
lines changed

libraries/src/AWS.Lambda.Powertools.Parameters/AppConfig/AppConfigProvider.cs

Lines changed: 75 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
*/
1515

1616
using System.Collections.Concurrent;
17+
using System.Text.Json.Nodes;
1718
using Amazon;
1819
using Amazon.AppConfigData;
1920
using Amazon.AppConfigData.Model;
@@ -35,32 +36,32 @@ public class AppConfigProvider : ParameterProvider<AppConfigProviderConfiguratio
3536
/// The default application Id.
3637
/// </summary>
3738
private string _defaultApplicationId = string.Empty;
38-
39+
3940
/// <summary>
4041
/// The default environment Id.
4142
/// </summary>
4243
private string _defaultEnvironmentId = string.Empty;
43-
44+
4445
/// <summary>
4546
/// The default configuration profile Id.
4647
/// </summary>
4748
private string _defaultConfigProfileId = string.Empty;
48-
49+
4950
/// <summary>
5051
/// Instance of datetime wrapper.
5152
/// </summary>
5253
private readonly IDateTimeWrapper _dateTimeWrapper;
53-
54+
5455
/// <summary>
5556
/// Thread safe dictionary to store results.
5657
/// </summary>
5758
private readonly ConcurrentDictionary<string, AppConfigResult> _results = new(StringComparer.OrdinalIgnoreCase);
58-
59+
5960
/// <summary>
6061
/// The client instance.
6162
/// </summary>
6263
private IAmazonAppConfigData? _client;
63-
64+
6465
/// <summary>
6566
/// Gets the client instance.
6667
/// </summary>
@@ -88,7 +89,7 @@ internal AppConfigProvider(
8889
}
8990

9091
#region IParameterProviderConfigurableClient implementation
91-
92+
9293
/// <summary>
9394
/// Use a custom client
9495
/// </summary>
@@ -238,7 +239,7 @@ public IAppConfigProvider ConfigureClient(string awsAccessKeyId, string awsSecre
238239
_client = new AmazonAppConfigDataClient(awsAccessKeyId, awsSecretAccessKey, awsSessionToken, config);
239240
return this;
240241
}
241-
242+
242243
#endregion
243244

244245
/// <summary>
@@ -253,7 +254,7 @@ public IAppConfigProvider DefaultApplication(string applicationId)
253254
_defaultApplicationId = applicationId;
254255
return this;
255256
}
256-
257+
257258
/// <summary>
258259
/// Sets the default environment ID or name.
259260
/// </summary>
@@ -266,7 +267,7 @@ public IAppConfigProvider DefaultEnvironment(string environmentId)
266267
_defaultEnvironmentId = environmentId;
267268
return this;
268269
}
269-
270+
270271
/// <summary>
271272
/// Sets the default configuration profile ID or name.
272273
/// </summary>
@@ -318,7 +319,7 @@ protected override AppConfigProviderConfigurationBuilder NewConfigurationBuilder
318319
.WithApplication(_defaultApplicationId)
319320
.WithEnvironment(_defaultEnvironmentId)
320321
.WithConfigProfile(_defaultConfigProfileId);
321-
322+
322323
}
323324

324325
/// <summary>
@@ -353,29 +354,29 @@ protected override AppConfigProviderConfigurationBuilder NewConfigurationBuilder
353354
.GetAsync()
354355
.ConfigureAwait(false);
355356
}
356-
357+
357358
/// <summary>
358359
/// Get last AppConfig value and transform it to JSON value.
359360
/// </summary>
360361
/// <typeparam name="T">JSON value type.</typeparam>
361362
/// <returns>The AppConfig JSON value.</returns>
362-
public T? Get<T>() where T: class
363+
public T? Get<T>() where T : class
363364
{
364365
return GetAsync<T>().GetAwaiter().GetResult();
365366
}
366-
367+
367368
/// <summary>
368369
/// Get last AppConfig value and transform it to JSON value.
369370
/// </summary>
370371
/// <typeparam name="T">JSON value type.</typeparam>
371372
/// <returns>The AppConfig JSON value.</returns>
372-
public async Task<T?> GetAsync<T>() where T: class
373+
public async Task<T?> GetAsync<T>() where T : class
373374
{
374375
return await NewConfigurationBuilder()
375376
.GetAsync<T>()
376377
.ConfigureAwait(false);
377378
}
378-
379+
379380
/// <summary>
380381
/// Get parameter value for the provided key.
381382
/// </summary>
@@ -384,7 +385,7 @@ protected override AppConfigProviderConfigurationBuilder NewConfigurationBuilder
384385
/// <returns>The parameter value.</returns>
385386
protected override async Task<string?> GetAsync(string key, ParameterProviderConfiguration? config)
386387
{
387-
if(config is not AppConfigProviderConfiguration configuration)
388+
if (config is not AppConfigProviderConfiguration configuration)
388389
throw new ArgumentNullException(nameof(config));
389390

390391
var cacheKey = AppConfigProviderCacheHelper.GetCacheKey
@@ -395,7 +396,7 @@ protected override AppConfigProviderConfigurationBuilder NewConfigurationBuilder
395396
);
396397

397398
var result = GetAppConfigResult(cacheKey);
398-
399+
399400
if (_dateTimeWrapper.UtcNow < result.NextAllowedPollTime)
400401
{
401402
if (!config.ForceFetch)
@@ -427,7 +428,7 @@ await Client.GetLatestConfigurationAsync(request)
427428

428429
using (var reader = new StreamReader(response.Configuration))
429430
{
430-
result.LastConfig =
431+
result.LastConfig =
431432
await reader.ReadToEndAsync()
432433
.ConfigureAwait(false);
433434
}
@@ -446,7 +447,7 @@ await reader.ReadToEndAsync()
446447
{
447448
throw new NotSupportedException("Impossible to get multiple values from AWS AppConfig");
448449
}
449-
450+
450451
/// <summary>
451452
/// Gets Or Adds AppConfigResult with provided key
452453
/// </summary>
@@ -462,7 +463,7 @@ private AppConfigResult GetAppConfigResult(string cacheKey)
462463

463464
return cachedResult;
464465
}
465-
466+
466467
/// <summary>
467468
/// Starts a configuration session used to retrieve a deployed configuration.
468469
/// </summary>
@@ -479,4 +480,57 @@ private async Task<string> GetInitialConfigurationTokenAsync(AppConfigProviderCo
479480

480481
return (await Client.StartConfigurationSessionAsync(request).ConfigureAwait(false)).InitialConfigurationToken;
481482
}
483+
484+
/// <summary>
485+
/// Check if the feature flag is enabled.
486+
/// </summary>
487+
/// <param name="key">The unique feature key for the feature flag</param>
488+
/// <param name="defaultValue">The default value of the flag</param>
489+
/// <returns>The feature flag value, or defaultValue if the flag cannot be evaluated</returns>
490+
public bool IsFeatureFlagEnabled(string key, bool defaultValue = false)
491+
{
492+
return IsFeatureFlagEnabledAsync(key, defaultValue).GetAwaiter().GetResult();
493+
}
494+
495+
/// <summary>
496+
/// Check if the feature flag is enabled.
497+
/// </summary>
498+
/// <param name="key">The unique feature key for the feature flag</param>
499+
/// <param name="defaultValue">The default value of the flag</param>
500+
/// <returns>The feature flag value, or defaultValue if the flag cannot be evaluated</returns>
501+
public async Task<bool> IsFeatureFlagEnabledAsync(string key, bool defaultValue = false)
502+
{
503+
return await GetFeatureFlagAttributeValueAsync(key, AppConfigFeatureFlagHelper.EnabledAttributeKey,
504+
defaultValue).ConfigureAwait(false);
505+
}
506+
507+
/// <summary>
508+
/// Get feature flag's attribute value.
509+
/// </summary>
510+
/// <param name="key">The unique feature key for the feature flag</param>
511+
/// <param name="attributeKey">The unique attribute key for the feature flag</param>
512+
/// <param name="defaultValue">The default value of the feature flag's attribute value</param>
513+
/// <typeparam name="T">The type of the value to obtain from feature flag's attribute.</typeparam>
514+
/// <returns>The feature flag's attribute value.</returns>
515+
public T? GetFeatureFlagAttributeValue<T>(string key, string attributeKey, T? defaultValue = default)
516+
{
517+
return GetFeatureFlagAttributeValueAsync(key, attributeKey, defaultValue).GetAwaiter().GetResult();
518+
}
519+
520+
/// <summary>
521+
/// Get feature flag's attribute value.
522+
/// </summary>
523+
/// <param name="key">The unique feature key for the feature flag</param>
524+
/// <param name="attributeKey">The unique attribute key for the feature flag</param>
525+
/// <param name="defaultValue">The default value of the feature flag's attribute value</param>
526+
/// <typeparam name="T">The type of the value to obtain from feature flag's attribute.</typeparam>
527+
/// <returns>The feature flag's attribute value.</returns>
528+
public async Task<T?> GetFeatureFlagAttributeValueAsync<T>(string key, string attributeKey,
529+
T? defaultValue = default)
530+
{
531+
return string.IsNullOrWhiteSpace(key)
532+
? defaultValue
533+
: AppConfigFeatureFlagHelper.GetFeatureFlagAttributeValueAsync(key, attributeKey, defaultValue,
534+
await GetAsync<JsonObject>().ConfigureAwait(false));
535+
}
482536
}

libraries/src/AWS.Lambda.Powertools.Parameters/AppConfig/AppConfigProviderConfigurationBuilder.cs

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
* permissions and limitations under the License.
1414
*/
1515

16+
using System.Text.Json.Nodes;
1617
using AWS.Lambda.Powertools.Parameters.Internal.AppConfig;
1718
using AWS.Lambda.Powertools.Parameters.Configuration;
1819
using AWS.Lambda.Powertools.Parameters.Provider;
@@ -164,4 +165,56 @@ protected override ParameterProviderConfiguration NewConfiguration()
164165
return await base.GetAsync<T>(AppConfigProviderCacheHelper.GetCacheKey(_applicationId, _environmentId,
165166
_configProfileId)).ConfigureAwait(false);
166167
}
168+
169+
/// <summary>
170+
/// Check if the feature flag is enabled.
171+
/// </summary>
172+
/// <param name="key">The unique feature key for the feature flag</param>
173+
/// <param name="defaultValue">The default value of the flag</param>
174+
/// <returns>The feature flag value, or defaultValue if the flag cannot be evaluated</returns>
175+
public bool IsFeatureFlagEnabled(string key, bool defaultValue = false)
176+
{
177+
return IsFeatureFlagEnabledAsync(key, defaultValue).GetAwaiter().GetResult();
178+
}
179+
180+
/// <summary>
181+
/// Check if the feature flag is enabled.
182+
/// </summary>
183+
/// <param name="key">The unique feature key for the feature flag</param>
184+
/// <param name="defaultValue">The default value of the flag</param>
185+
/// <returns>The feature flag value, or defaultValue if the flag cannot be evaluated</returns>
186+
public async Task<bool> IsFeatureFlagEnabledAsync(string key, bool defaultValue = false)
187+
{
188+
return await GetFeatureFlagAttributeValueAsync(key, AppConfigFeatureFlagHelper.EnabledAttributeKey,
189+
defaultValue).ConfigureAwait(false);
190+
}
191+
192+
/// <summary>
193+
/// Get feature flag's attribute value.
194+
/// </summary>
195+
/// <param name="key">The unique feature key for the feature flag</param>
196+
/// <param name="attributeKey">The unique attribute key for the feature flag</param>
197+
/// <param name="defaultValue">The default value of the feature flag's attribute value</param>
198+
/// <typeparam name="T">The type of the value to obtain from feature flag's attribute.</typeparam>
199+
/// <returns>The feature flag's attribute value.</returns>
200+
public T? GetFeatureFlagAttributeValue<T>(string key, string attributeKey, T? defaultValue = default)
201+
{
202+
return GetFeatureFlagAttributeValueAsync(key, attributeKey, defaultValue).GetAwaiter().GetResult();
203+
}
204+
205+
/// <summary>
206+
/// Get feature flag's attribute value.
207+
/// </summary>
208+
/// <param name="key">The unique feature key for the feature flag</param>
209+
/// <param name="attributeKey">The unique attribute key for the feature flag</param>
210+
/// <param name="defaultValue">The default value of the feature flag's attribute value</param>
211+
/// <typeparam name="T">The type of the value to obtain from feature flag's attribute.</typeparam>
212+
/// <returns>The feature flag's attribute value.</returns>
213+
public async Task<T?> GetFeatureFlagAttributeValueAsync<T>(string key, string attributeKey, T? defaultValue = default)
214+
{
215+
return string.IsNullOrWhiteSpace(key)
216+
? defaultValue
217+
: AppConfigFeatureFlagHelper.GetFeatureFlagAttributeValueAsync(key, attributeKey, defaultValue,
218+
await GetAsync<JsonObject>().ConfigureAwait(false));
219+
}
167220
}

libraries/src/AWS.Lambda.Powertools.Parameters/AppConfig/IAppConfigProvider.cs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,5 +77,41 @@ public interface IAppConfigProvider : IParameterProvider<AppConfigProviderConfig
7777
/// <typeparam name="T">JSON value type.</typeparam>
7878
/// <returns>The AppConfig JSON value.</returns>
7979
Task<T?> GetAsync<T>() where T : class;
80+
81+
/// <summary>
82+
/// Check if the feature flag is enabled.
83+
/// </summary>
84+
/// <param name="key">The unique feature key for the feature flag</param>
85+
/// <param name="defaultValue">The default value of the flag</param>
86+
/// <returns>The feature flag value, or defaultValue if the flag cannot be evaluated</returns>
87+
bool IsFeatureFlagEnabled(string key, bool defaultValue = false);
88+
89+
/// <summary>
90+
/// Check if the feature flag is enabled.
91+
/// </summary>
92+
/// <param name="key">The unique feature key for the feature flag</param>
93+
/// <param name="defaultValue">The default value of the flag</param>
94+
/// <returns>The feature flag value, or defaultValue if the flag cannot be evaluated</returns>
95+
Task<bool> IsFeatureFlagEnabledAsync(string key, bool defaultValue = false);
96+
97+
/// <summary>
98+
/// Get feature flag's attribute value.
99+
/// </summary>
100+
/// <param name="key">The unique feature key for the feature flag</param>
101+
/// <param name="attributeKey">The unique attribute key for the feature flag</param>
102+
/// <param name="defaultValue">The default value of the feature flag's attribute value</param>
103+
/// <typeparam name="T">The type of the value to obtain from feature flag's attribute.</typeparam>
104+
/// <returns>The feature flag's attribute value.</returns>
105+
T? GetFeatureFlagAttributeValue<T>(string key, string attributeKey, T? defaultValue = default);
106+
107+
/// <summary>
108+
/// Get feature flag's attribute value.
109+
/// </summary>
110+
/// <param name="key">The unique feature key for the feature flag</param>
111+
/// <param name="attributeKey">The unique attribute key for the feature flag</param>
112+
/// <param name="defaultValue">The default value of the feature flag's attribute value</param>
113+
/// <typeparam name="T">The type of the value to obtain from feature flag's attribute.</typeparam>
114+
/// <returns>The feature flag's attribute value.</returns>
115+
Task<T?> GetFeatureFlagAttributeValueAsync<T>(string key, string attributeKey, T? defaultValue = default);
80116
}
81117

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License").
5+
* You may not use this file except in compliance with the License.
6+
* A copy of the License is located at
7+
*
8+
* http://aws.amazon.com/apache2.0
9+
*
10+
* or in the "license" file accompanying this file. This file is distributed
11+
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12+
* express or implied. See the License for the specific language governing
13+
* permissions and limitations under the License.
14+
*/
15+
16+
using System.Text.Json.Nodes;
17+
18+
namespace AWS.Lambda.Powertools.Parameters.Internal.AppConfig;
19+
20+
/// <summary>
21+
/// AppConfigProviderCacheHelper class.
22+
/// </summary>
23+
internal static class AppConfigFeatureFlagHelper
24+
{
25+
internal const string EnabledAttributeKey = "enabled";
26+
27+
/// <summary>
28+
/// Get feature flag's attribute value.
29+
/// </summary>
30+
/// <param name="key">The unique feature key for the feature flag</param>
31+
/// <param name="attributeKey">The unique attribute key for the feature flag</param>
32+
/// <param name="defaultValue">The default value of the feature flag's attribute value</param>
33+
/// <param name="featureFlag">The AppConfig JSON value of the feature flag</param>
34+
/// <typeparam name="T">The type of the value to obtain from feature flag's attribute.</typeparam>
35+
/// <returns>The feature flag's attribute value.</returns>
36+
internal static T? GetFeatureFlagAttributeValueAsync<T>(string key, string attributeKey, T? defaultValue,
37+
JsonObject? featureFlag)
38+
{
39+
if (string.IsNullOrWhiteSpace(key) || string.IsNullOrWhiteSpace(attributeKey) || featureFlag is null)
40+
return defaultValue;
41+
42+
var keyElement = featureFlag[key];
43+
if (keyElement is null)
44+
return defaultValue;
45+
46+
var attributeElement = keyElement[attributeKey];
47+
if (attributeElement is null)
48+
return defaultValue;
49+
50+
return attributeElement.GetValue<T>();
51+
}
52+
}

0 commit comments

Comments
 (0)