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

Support variant for FeatureTagHelper #407

Merged
merged 13 commits into from
May 19, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Licensed under the MIT license.
//
using Microsoft.AspNetCore.Razor.TagHelpers;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
Expand All @@ -13,7 +14,7 @@ namespace Microsoft.FeatureManagement.Mvc.TagHelpers
/// </summary>
public class FeatureTagHelper : TagHelper
{
private readonly IFeatureManager _featureManager;
private readonly IVariantFeatureManager _featureManager;

/// <summary>
/// A feature name, or comma separated list of feature names, for which the content should be rendered. By default, all specified features must be enabled to render the content, but this requirement can be controlled by the <see cref="Requirement"/> property.
Expand All @@ -30,13 +31,19 @@ public class FeatureTagHelper : TagHelper
/// </summary>
public bool Negate { get; set; }

/// <summary>
/// A variant name, or comma separated list of variant names. If any of specified variants is assigned, the content should be rendered.
/// If variant is specified, <see cref="Requirement"/> will be ignored. Besides, <see cref="Name"/> should be exactly one feature name.
zhiyuanliang-ms marked this conversation as resolved.
Show resolved Hide resolved
/// </summary>
public string Variant { get; set; }

/// <summary>
/// Creates a feature tag helper.
/// </summary>
/// <param name="featureManager">The feature manager snapshot to use to evaluate feature state.</param>
public FeatureTagHelper(IFeatureManagerSnapshot featureManager)
public FeatureTagHelper(IVariantFeatureManagerSnapshot featureManager)
{
_featureManager = featureManager;
_featureManager = featureManager ?? throw new ArgumentNullException(nameof(featureManager));
}

/// <summary>
Expand All @@ -52,11 +59,30 @@ public override async Task ProcessAsync(TagHelperContext context, TagHelperOutpu

if (!string.IsNullOrEmpty(Name))
{
IEnumerable<string> names = Name.Split(',').Select(n => n.Trim());
IEnumerable<string> features = Name.Split(',').Select(n => n.Trim());

if (string.IsNullOrEmpty(Variant))
{
enabled = Requirement == RequirementType.All ?
await features.All(async feature => await _featureManager.IsEnabledAsync(feature).ConfigureAwait(false)) :
await features.Any(async feature => await _featureManager.IsEnabledAsync(feature).ConfigureAwait(false));
}
else
{
if (features.Count() != 1)
{
throw new ArgumentException("Variant cannot be associated with multiple feature flags.", nameof(Name));
}

IEnumerable<string> variants = Variant.Split(',').Select(n => n.Trim());

enabled = await variants.Any(
async variant => {
Variant assignedVariant = await _featureManager.GetVariantAsync(features.First());

enabled = Requirement == RequirementType.All ?
await names.All(async n => await _featureManager.IsEnabledAsync(n).ConfigureAwait(false)) :
await names.Any(async n => await _featureManager.IsEnabledAsync(n).ConfigureAwait(false));
return variant == assignedVariant?.Name;
});
}
zhiyuanliang-ms marked this conversation as resolved.
Show resolved Hide resolved
}

if (Negate)
Expand Down