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

Allow different type of filters to share the same Alias #262

Merged
merged 10 commits into from
Oct 26, 2023

Conversation

zhiyuanliang-ms
Copy link
Contributor

@zhiyuanliang-ms zhiyuanliang-ms commented Sep 14, 2023

Allow filters to share the same Alias. This PR will resolve #98.
One FeatureFilter and multiple ContextualFeatureFilters using different types of context can share the same Alias.

We plan to register built-in filter including percentage, time window and contextual targeting filters by default when AddFeatureManagement() is called.
However, the contextual targeting and targeting filter share the same alias "Microsoft.Targeting". Currently, feature management does not allow filters share the same alias. This PR will also un-block this.

@zhiyuanliang-ms zhiyuanliang-ms changed the title In Progress: Allow filters share the same Alias Allow different type of filters to share the same Alias Sep 18, 2023
@zhiyuanliang-ms zhiyuanliang-ms changed the base branch from main to preview October 17, 2023 06:14
@zhiyuanliang-ms zhiyuanliang-ms changed the base branch from preview to main October 19, 2023 06:25
@zhiyuanliang-ms
Copy link
Contributor Author

zhiyuanliang-ms commented Oct 23, 2023

Tuple of filter name and context type will be used as the key for the filter metadata cache:
ConcurrentDictionary<ValueTuple<string, Type>, IFeatureFilterMetadata> _filterMetadataCache;
For non-contextual filter, the key will be ("FilterName", null).

No matter whether the feature flag contains any contextual filter, IsEnabledAsync(string feature, TContext appContext) can be called to evaluate this feature flag.

One IFeatureFIlter and multiple IContextualFeatureFilters with different types of context can share one alias.

If multiple IFeatureFIlters or multiple IContextualFeatureFilters use the same alias, FeatureManagementError.AmbiguousFeatureFilter will be thrown.

Let's say we have a non-contextual filter called FilterA and two contextual filters FilterB and FilterC which accept TypeB and TypeC contexts respectively. All of three filters share the same alias "SharedFilterName".
We also have a feature flag "MyFeature" which uses the feature filter "SharedFilterName" in its configuration.

If all of three filters are registered:
When we call IsEnabledAsync("MyFeature"), the FilterA will be used to evaluate the feature flag.
When we call IsEnabledAsync("MyFeature", context), if context's type is TypeB, FilterB will be used and if context's type is TypeC, FilterC will be used.
When we call IsEnabledAsync("MyFeature", context), if context's type is TypeF, FilterA will be used.

If only FilterA is registered:
When we call IsEnabledAsync("MyFeature", context), FilterA will be used.

If FilterC is registered, but neither Filter A nor FIlterB is registered,
When we call IsEnabledAsync("MyFeature", context) where context's type is TypeB, no filter will be used. MissFeatureFilter error will be thrown if the IgnoreMissingFeatureFilter option is false.

@rossgrambo
Copy link
Contributor

Just noting this down for myself. For targeting context accessor, nothing changes. Because the filter's code controls what accessor/context it depends on. This means you wouldn't be able to have two filters with aliases "AdvancedTargetingFilter" if you wanted them to use accessors to get their context.

IFeatureFilterMetadata filter = GetFeatureFilterMetadata(featureFilterConfiguration.Name);
IFeatureFilterMetadata filter;

if (useAppContext)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we're now saying a call to IsEnabledAsync("name", Context); will use "name" even if it has no context, then I don't think the useAppContext bool needs to exist anymore.

Just use contextual filter if its defined for the context, otherwise use the no contextual filter.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Context may be null. I think we still need to distinguish the case that users intend to pass null as the context.

@@ -165,7 +175,7 @@ private async Task<bool> IsEnabledAsync<TContext>(string feature, TContext appCo

//
// IContextualFeatureFilter
if (useAppContext)
if (useAppContext && ContextualFeatureFilterEvaluator.IsContextualFilter(filter, typeof(TContext)))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is the IsContextualFilter function written like it is? Couldn't it just be interfaceType.IsAssignableFrom(classType) ? I know it's not a part of this PR, but curious.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a staic method IsContextualFilter() of ContextualFeatureFilterEvaluator.
In my previous commit, my code looked like:
if (useAppContext && ! (filter is IFeatureFilter))
I think it is better to use IsContextualFilter() because it is more readable.

//
// Feature filters can have namespaces in their alias
// If a feature is configured to use a filter without a namespace such as 'MyFilter', then it can match 'MyOrg.MyProduct.MyFilter' or simply 'MyFilter'
// If a feature is configured to use a filter with a namespace such as 'MyOrg.MyProduct.MyFilter' then it can only match 'MyOrg.MyProduct.MyFilter'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Out of curiosity, what if there is a filter MyProduct.MyFilter as well as MyOrg.MyProduct.MyFilter? We treat them as distinct names?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. If the configuration of a feature flag contains any filters with a namespace, the feature manager will try to find a registered filter whose alias is the same as the filter name in the feature flag configuration.

@@ -267,48 +277,72 @@ private void BindSettings(IFeatureFilterMetadata filter, FeatureFilterEvaluation
context.Settings = settings;
}

private IFeatureFilterMetadata GetFeatureFilterMetadata(string filterName)
private bool IsFilterNameMatched(Type filterType, string filterName)
Copy link
Member

@jimmyca15 jimmyca15 Oct 24, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs to move after the private methods that call it to preserve consistent scroll-flow.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
private bool IsFilterNameMatched(Type filterType, string filterName)
private bool IsMatchingName(Type filterType, string filterName)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see an update to the name

@@ -165,7 +175,7 @@ private async Task<bool> IsEnabledAsync<TContext>(string feature, TContext appCo

//
// IContextualFeatureFilter
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if (useAppContext && ContextualFeatureFilterEvaluator.IsContextualFilter(filter, typeof(TContext)))

At this point we should already know whether the filter is a contextual filter or not by whether filter was populated with or without an app context.

@zhiyuanliang-ms zhiyuanliang-ms merged commit ed3fce0 into main Oct 26, 2023
2 checks passed
@zhiyuanliang-ms zhiyuanliang-ms deleted the zhiyuanliang/allow-dup-alias branch November 10, 2023 01:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Unable to create both IFeatureFilter and IContextualFeatureFilter with same FilterAlias.
4 participants