public ContextualFeatureFilterEvaluator(IFeatureFilterMetadata filter, Type appContextType) { if (filter == null) { throw new ArgumentNullException(nameof(filter)); } if (appContextType == null) { throw new ArgumentNullException(nameof(appContextType)); } Type targetInterface = GetContextualFilterInterface(filter, appContextType); // // Extract IContextualFeatureFilter<T>.EvaluateAsync method. if (targetInterface != null) { MethodInfo evaluateMethod = targetInterface.GetMethod(nameof(IContextualFeatureFilter <object> .EvaluateAsync), BindingFlags.Public | BindingFlags.Instance); _evaluateFunc = TypeAgnosticEvaluate(filter.GetType(), evaluateMethod); } _filter = filter; }
private static Type GetContextualFilterInterface(IFeatureFilterMetadata filter, Type appContextType) { IEnumerable <Type> contextualFilterInterfaces = filter.GetType().GetInterfaces().Where(i => i.IsGenericType && i.GetGenericTypeDefinition().IsAssignableFrom(typeof(IContextualFeatureFilter <>))); Type targetInterface = contextualFilterInterfaces.FirstOrDefault(i => i.GetGenericArguments()[0].IsAssignableFrom(appContextType)); return(targetInterface); }
private IFeatureFilterMetadata GetFeatureFilterMetadata(string filterName) { const string filterSuffix = "filter"; IFeatureFilterMetadata filter = _filterMetadataCache.GetOrAdd( filterName, _ => { IEnumerable <IFeatureFilterMetadata> matchingFilters = _featureFilters.Where(f => { Type t = f.GetType(); string name = ((FilterAliasAttribute)Attribute.GetCustomAttribute(t, typeof(FilterAliasAttribute))) ?.Alias; if (name == null) { name = t.Name.EndsWith(filterSuffix, StringComparison.OrdinalIgnoreCase) ? t.Name.Substring(0, t.Name.Length - filterSuffix.Length) : t.Name; } // // 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' if (filterName.Contains('.')) { // // The configured filter name is namespaced. It must be an exact match. return(string.Equals(name, filterName, StringComparison.OrdinalIgnoreCase)); } // // We take the simple name of a filter, E.g. 'MyFilter' for 'MyOrg.MyProduct.MyFilter' string simpleName = name.Contains('.') ? name.Split('.').Last() : name; return(string.Equals(simpleName, filterName, StringComparison.OrdinalIgnoreCase)); }); if (matchingFilters.Count() > 1) { throw new FeatureManagementException(FeatureManagementError.AmbiguousFeatureFilter, $"Multiple feature filters match the configured filter named '{filterName}'."); } return(matchingFilters.FirstOrDefault()); } ); return(filter); }
private ContextualFeatureFilterEvaluator GetContextualFeatureFilter(string filterName, Type appContextType) { if (appContextType == null) { throw new ArgumentNullException(nameof(appContextType)); } ContextualFeatureFilterEvaluator filter = _contextualFeatureFilterCache.GetOrAdd( $"{filterName}{Environment.NewLine}{appContextType.FullName}", (_) => { IFeatureFilterMetadata metadata = GetFeatureFilterMetadata(filterName); return(ContextualFeatureFilterEvaluator.IsContextualFilter(metadata, appContextType) ? new ContextualFeatureFilterEvaluator(metadata, appContextType) : null); } ); return(filter); }
private async Task <bool> IsEnabledAsync <TContext>(string feature, TContext appContext, bool useAppContext) { foreach (ISessionManager sessionManager in _sessionManagers) { bool?readSessionResult = await sessionManager.GetAsync(feature).ConfigureAwait(false); if (readSessionResult.HasValue) { return(readSessionResult.Value); } } bool enabled = false; FeatureSettings settings = await _settingsProvider.GetFeatureSettingsAsync(feature).ConfigureAwait(false); if (settings != null) { // // Check if feature is always on // If it is, result is true, goto: cache if (settings.EnabledFor.Any(featureFilter => string.Equals(featureFilter.Name, "AlwaysOn", StringComparison.OrdinalIgnoreCase))) { enabled = true; } else { // // For all enabling filters listed in the feature's state calculate if they return true // If any executed filters return true, return true foreach (FeatureFilterSettings featureFilterSettings in settings.EnabledFor) { IFeatureFilterMetadata filter = GetFeatureFilterMetadata(featureFilterSettings.Name); if (filter == null) { string errorMessage = $"The feature filter '{featureFilterSettings.Name}' specified for feature '{feature}' was not found."; if (!_options.IgnoreMissingFeatureFilters) { throw new FeatureManagementException(FeatureManagementError.MissingFeatureFilter, errorMessage); } else { _logger.LogWarning(errorMessage); } continue; } var context = new FeatureFilterEvaluationContext() { FeatureName = feature, Parameters = featureFilterSettings.Parameters }; // // IContextualFeatureFilter if (useAppContext) { ContextualFeatureFilterEvaluator contextualFilter = GetContextualFeatureFilter(featureFilterSettings.Name, typeof(TContext)); if (contextualFilter != null && await contextualFilter.EvaluateAsync(context, appContext).ConfigureAwait(false)) { enabled = true; break; } } // // IFeatureFilter if (filter is IFeatureFilter featureFilter && await featureFilter.EvaluateAsync(context).ConfigureAwait(false)) { enabled = true; break; } } } } foreach (ISessionManager sessionManager in _sessionManagers) { await sessionManager.SetAsync(feature, enabled).ConfigureAwait(false); } return(enabled); }
public static bool IsContextualFilter(IFeatureFilterMetadata filter, Type appContextType) { return(GetContextualFilterInterface(filter, appContextType) != null); }