Skip to content

Commit e09cb23

Browse files
amerjusupovicrossgramboSamSadfasamsadsam
authored
Add support for filtering by tags (#637)
* Adding allocation id * serialize with sorted keys * use string empty * nit * rename ff id to TelemetryVariantPercentile * add more values * dotnet format * Version bump * Add `RegisterAll` API to enable monitoring collections of key-values for refresh (#574) * WIP * WIP testing out client extensions methods * WIP added selectors to multikeywatchers * remove unused property * WIP check for registerall changes to change refreshall * WIP * WIP fixing types and reslving errors * WIP fixing client extensions class * WIP * WIP update feature flag logic * WIP client extensions * WIP reload all flags on change * WIP * WIP fixing tests to return response for getconfigurationsettingsasync * WIP etag for tests * fix watchedcollections null * WIP tests, working for examples * remove unused variables * update to newest sdk version, remove unused * WIP fixing tests * WIP reworking testing to work with new etag approach * tests passing, fix mockasyncpageable * update sdk package version * fix loghelper, tests * WIP fixing aspages tests * revert watchesfeatureflags test * update test again * WIP * fixing watchconditions * separate selected key value collections from feature flag collections, separate selectors, add new methods to support new logic * comment and naming updates * fixing unit tests, namespace of defining/calling code needs to be same * fixing tests using AsPages * fix tests with pageablemanager * format * fix tests * fix tests * remove unused extension test class * fix comment, capitalization * check etag on 200, fix tests * add registerall test, fix refresh tests * fix condition for pages and old match conditions * WIP fixing PR comments, tests * check status after advancing existing etag enumerator * move around refresh logic * null check page etag, revert break to existing keys check in getrefreshedcollections * fix loadselected, replace selectedkvwatchers with registerall refresh time * fix comment in options * clean up tests * PR comments * PR comments * don't allow both registerall and register * fix check for calls to both register methods * PR comments for rename/small changes * fix compile error * simplify refreshasync path, fix naming from comments * remove redundant if check * simplify logic for minrefreshinterval * fix smaller comments * call loadselected when refreshing collection, separate data for individual refresh * in progress change to registerall include ff * fix load order * fix comments, rename logging constants to match new behavior * pr comments, refactor refreshasync * clean up etags dictionary creation * PR comments * add uncommitted changes to testhelper * update tests for registerall with feature flags, check ff keys to remove flags on refresh * PR comments * PR comments * use invalidoperationexception in configurerefresh, update loggingconstants to match behavior * remove unused changes * Give the users the ability to have control over ConfigurationClient instance(s) used by the provider (#598) (#617) * Introduced a new `AzureAppConfigurationClientFactory` class to handle the creation of `ConfigurationClient` instances * remove clients dictionary since we will not have hits and clients are already stored in ConfigurationClientManager * revert * add license + remove unused usings * ran dotnet format * add capability of fallback to different stores * add explicit type * address comments * remove scheme validation --------- Co-authored-by: Sami Sadfa <[email protected]> Co-authored-by: Sami Sadfa <[email protected]> * first draft tag filtering support * add alternate APIs * change to use ienumerable * update featureflagoptions to match main options * update keyvalueselector equals and hashcode * update param comments for selects * fix merge conflict errors * add validation for tagsfilter param, add to comment * edit error message for format * edit comment * add unit tests * remove unused file * revert versions * update tests to include feature flag select * add refresh test * ff only refresh test * update equals for selector * fix equals * update equals * reorder properties in keyvalueselector * upgrade to 8.2.0-preview (#638) * fix incorrect test * fix equals for selector * update gethashcode for keyvalueselector * PR comments, in progress * update tests from PR comments * add validation for number of tags, add test * rename tagsFilter to tagsFilters everywhere * fix usings, missing updates to ffoptions * update ffoptions select again * fix tests * update sdk version * update tagsfilters to tagfilters * remove tagsfilters again * PR comments * PR comments * Revert "Merge pull request #600 from Azure/rossgrambo/allocation_id" This reverts commit 51d4ad7, reversing changes made to d551536. * Revert "Give the users the ability to have control over ConfigurationClient instance(s) used by the provider (#598) (#617)" This reverts commit 6dc9ae2. --------- Co-authored-by: Ross Grambo <[email protected]> Co-authored-by: Sami Sadfa <[email protected]> Co-authored-by: Sami Sadfa <[email protected]> Co-authored-by: Sami Sadfa <[email protected]>
1 parent bf8b06b commit e09cb23

File tree

8 files changed

+1250
-523
lines changed

8 files changed

+1250
-523
lines changed

src/Microsoft.Extensions.Configuration.AzureAppConfiguration/AzureAppConfigurationOptions.cs

Lines changed: 537 additions & 517 deletions
Large diffs are not rendered by default.

src/Microsoft.Extensions.Configuration.AzureAppConfiguration/AzureAppConfigurationProvider.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -346,6 +346,7 @@ await ExecuteWithFailOverPolicyAsync(clients, async (client) =>
346346
{
347347
KeyFilter = watcher.Key,
348348
LabelFilter = watcher.Label,
349+
TagFilters = watcher.Tags,
349350
IsFeatureFlagSelector = true
350351
}),
351352
_ffEtags,
@@ -828,6 +829,14 @@ private async Task<Dictionary<string, ConfigurationSetting>> LoadSelected(
828829
LabelFilter = loadOption.LabelFilter
829830
};
830831

832+
if (loadOption.TagFilters != null)
833+
{
834+
foreach (string tagFilter in loadOption.TagFilters)
835+
{
836+
selector.TagsFilter.Add(tagFilter);
837+
}
838+
}
839+
831840
var matchConditions = new List<MatchConditions>();
832841

833842
await CallWithRequestTracing(async () =>

src/Microsoft.Extensions.Configuration.AzureAppConfiguration/FeatureManagement/FeatureFlagOptions.cs

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,14 @@ public FeatureFlagOptions SetRefreshInterval(TimeSpan refreshInterval)
7474
/// The label filter to apply when querying Azure App Configuration for feature flags. By default the null label will be used. Built-in label filter options: <see cref="LabelFilter"/>
7575
/// The characters asterisk (*) and comma (,) are not supported. Backslash (\) character is reserved and must be escaped using another backslash (\).
7676
/// </param>
77-
public FeatureFlagOptions Select(string featureFlagFilter, string labelFilter = LabelFilter.Null)
77+
/// <param name="tagFilters">
78+
/// In addition to key and label filters, feature flags from Azure App Configuration can be filtered based on their tag names and values.
79+
/// Each tag filter must follow the format "tagName=tagValue". Only those feature flags will be loaded whose tags match all the tags provided here.
80+
/// Built in tag filter values: <see cref="TagValue"/>. For example, $"tagName={<see cref="TagValue.Null"/>}".
81+
/// The characters asterisk (*), comma (,) and backslash (\) are reserved and must be escaped using a backslash (\).
82+
/// Up to 5 tag filters can be provided. If no tag filters are provided, feature flags will not be filtered based on tags.
83+
/// </param>
84+
public FeatureFlagOptions Select(string featureFlagFilter, string labelFilter = LabelFilter.Null, IEnumerable<string> tagFilters = null)
7885
{
7986
if (string.IsNullOrEmpty(featureFlagFilter))
8087
{
@@ -97,12 +104,24 @@ public FeatureFlagOptions Select(string featureFlagFilter, string labelFilter =
97104
throw new ArgumentException("The characters '*' and ',' are not supported in label filters.", nameof(labelFilter));
98105
}
99106

107+
if (tagFilters != null)
108+
{
109+
foreach (string tag in tagFilters)
110+
{
111+
if (string.IsNullOrEmpty(tag) || !tag.Contains('=') || tag.IndexOf('=') == 0)
112+
{
113+
throw new ArgumentException($"Tag filter '{tag}' does not follow the format \"tagName=tagValue\".", nameof(tagFilters));
114+
}
115+
}
116+
}
117+
100118
string featureFlagPrefix = FeatureManagementConstants.FeatureFlagMarker + featureFlagFilter;
101119

102120
FeatureFlagSelectors.AppendUnique(new KeyValueSelector
103121
{
104122
KeyFilter = featureFlagPrefix,
105123
LabelFilter = labelFilter,
124+
TagFilters = tagFilters,
106125
IsFeatureFlagSelector = true
107126
});
108127

src/Microsoft.Extensions.Configuration.AzureAppConfiguration/Microsoft.Extensions.Configuration.AzureAppConfiguration.csproj

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,12 @@
1515
</PropertyGroup>
1616

1717
<ItemGroup>
18-
<PackageReference Include="Azure.Data.AppConfiguration" Version="1.4.1" />
18+
<PackageReference Include="Azure.Data.AppConfiguration" Version="1.6.0" />
1919
<PackageReference Include="Azure.Messaging.EventGrid" Version="4.7.0" />
2020
<PackageReference Include="Azure.Security.KeyVault.Secrets" Version="4.6.0" />
2121
<PackageReference Include="DnsClient" Version="1.7.0" />
22+
<PackageReference Include="Microsoft.Bcl.HashCode" Version="6.0.0" />
23+
<PackageReference Include="Microsoft.Extensions.Azure" Version="1.7.6" />
2224
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="6.0.0" />
2325
<PackageReference Include="Microsoft.Extensions.Logging" Version="6.0.0" />
2426
<PackageReference Include="Microsoft.Extensions.Configuration" Version="6.0.1" />

src/Microsoft.Extensions.Configuration.AzureAppConfiguration/Models/KeyValueSelector.cs

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
// Copyright (c) Microsoft Corporation.
22
// Licensed under the MIT license.
33
//
4+
using System;
5+
using System.Collections.Generic;
6+
using System.Linq;
7+
48
namespace Microsoft.Extensions.Configuration.AzureAppConfiguration.Models
59
{
610
/// <summary>
@@ -24,6 +28,11 @@ public class KeyValueSelector
2428
/// </summary>
2529
public string SnapshotName { get; set; }
2630

31+
/// <summary>
32+
/// A filter that determines what tags to require when selecting key-values for the the configuration provider.
33+
/// </summary>
34+
public IEnumerable<string> TagFilters { get; set; }
35+
2736
/// <summary>
2837
/// A boolean that signifies whether this selector is intended to select feature flags.
2938
/// </summary>
@@ -40,7 +49,11 @@ public override bool Equals(object obj)
4049
{
4150
return KeyFilter == selector.KeyFilter
4251
&& LabelFilter == selector.LabelFilter
43-
&& SnapshotName == selector.SnapshotName;
52+
&& SnapshotName == selector.SnapshotName
53+
&& (TagFilters == null
54+
? selector.TagFilters == null
55+
: selector.TagFilters != null && new HashSet<string>(TagFilters).SetEquals(selector.TagFilters))
56+
&& IsFeatureFlagSelector == selector.IsFeatureFlagSelector;
4457
}
4558

4659
return false;
@@ -52,9 +65,22 @@ public override bool Equals(object obj)
5265
/// <returns>A hash code for the current object.</returns>
5366
public override int GetHashCode()
5467
{
55-
return (KeyFilter?.GetHashCode() ?? 0) ^
56-
(LabelFilter?.GetHashCode() ?? 1) ^
57-
(SnapshotName?.GetHashCode() ?? 2);
68+
string tagFiltersString = string.Empty;
69+
70+
if (TagFilters != null && TagFilters.Any())
71+
{
72+
var sortedTags = new SortedSet<string>(TagFilters);
73+
74+
// Concatenate tags into a single string with a delimiter
75+
tagFiltersString = string.Join("\n", sortedTags);
76+
}
77+
78+
return HashCode.Combine(
79+
KeyFilter,
80+
LabelFilter,
81+
SnapshotName,
82+
tagFiltersString,
83+
IsFeatureFlagSelector);
5884
}
5985
}
6086
}

src/Microsoft.Extensions.Configuration.AzureAppConfiguration/Models/KeyValueWatcher.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
//
44
using Microsoft.Extensions.Configuration.AzureAppConfiguration.Extensions;
55
using System;
6+
using System.Collections.Generic;
67

78
namespace Microsoft.Extensions.Configuration.AzureAppConfiguration.Models
89
{
@@ -18,6 +19,11 @@ internal class KeyValueWatcher
1819
/// </summary>
1920
public string Label { get; set; }
2021

22+
/// <summary>
23+
/// Tags of the key-value to be watched.
24+
/// </summary>
25+
public IEnumerable<string> Tags { get; set; }
26+
2127
/// <summary>
2228
/// A flag to refresh all key-values.
2329
/// </summary>
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT license.
3+
//
4+
namespace Microsoft.Extensions.Configuration.AzureAppConfiguration
5+
{
6+
/// <summary>
7+
/// Defines well known tag values that are used within Azure App Configuration.
8+
/// </summary>
9+
public class TagValue
10+
{
11+
/// <summary>
12+
/// Matches null tag values.
13+
/// </summary>
14+
public const string Null = "\0";
15+
}
16+
}

0 commit comments

Comments
 (0)