Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ public sealed class ConfigurationFeatureDefinitionProvider : IFeatureDefinitionP
private readonly ConcurrentDictionary<string, FeatureDefinition> _definitions;
private IDisposable _changeSubscription;
private int _stale = 0;
private readonly bool _microsoftFeatureManagementSchemaEnabled;
private long _initialized = 0;
private bool _microsoftFeatureManagementSchemaEnabled;
Comment thread
zhiyuanliang-ms marked this conversation as resolved.
private readonly object _lock = new object();

const string ParseValueErrorString = "Invalid setting '{0}' with value '{1}' for feature '{2}'.";

Expand Down Expand Up @@ -95,6 +97,8 @@ public Task<FeatureDefinition> GetFeatureDefinitionAsync(string featureName)
throw new ArgumentException($"The value '{ConfigurationPath.KeyDelimiter}' is not allowed in the feature name.", nameof(featureName));
}

EnsureInit();

if (Interlocked.Exchange(ref _stale, 0) != 0)
{
_definitions.Clear();
Expand All @@ -118,6 +122,8 @@ public Task<FeatureDefinition> GetFeatureDefinitionAsync(string featureName)
public async IAsyncEnumerable<FeatureDefinition> GetAllFeatureDefinitionsAsync()
#pragma warning restore CS1998
{
EnsureInit();

if (Interlocked.Exchange(ref _stale, 0) != 0)
{
_definitions.Clear();
Expand All @@ -136,7 +142,46 @@ public async IAsyncEnumerable<FeatureDefinition> GetAllFeatureDefinitionsAsync()

//
// Underlying IConfigurationSection data is dynamic so latest feature definitions are returned
yield return _definitions.GetOrAdd(featureName, (_) => ReadFeatureDefinition(featureSection));
yield return _definitions.GetOrAdd(featureName, (_) => ReadFeatureDefinition(featureSection));
}
}

private void EnsureInit()
{
if (_initialized == 0)
{
IConfiguration MicrosoftFeatureManagementConfigurationSection = _configuration
.GetChildren()
.FirstOrDefault(section =>
string.Equals(
section.Key,
MicrosoftFeatureManagementFields.FeatureManagementSectionName,
StringComparison.OrdinalIgnoreCase));

bool hasMicrosoftFeatureManagementSchema = MicrosoftFeatureManagementConfigurationSection != null;

if (MicrosoftFeatureManagementConfigurationSection == null & RootConfigurationFallbackEnabled)
{
IConfiguration featureFlagsSection = _configuration
.GetChildren()
.FirstOrDefault(section =>
string.Equals(
section.Key,
MicrosoftFeatureManagementFields.FeatureFlagsSectionName,
StringComparison.OrdinalIgnoreCase));

hasMicrosoftFeatureManagementSchema = featureFlagsSection != null;
}

lock (_lock)
{
if (Interlocked.Read(ref _initialized) == 0)
{
_microsoftFeatureManagementSchemaEnabled = hasMicrosoftFeatureManagementSchema;

Interlocked.Exchange(ref _initialized, 1);
}
Comment thread
amerjusupovic marked this conversation as resolved.
}
}
}

Expand All @@ -160,10 +205,10 @@ private FeatureDefinition ReadFeatureDefinition(IConfigurationSection configurat
return ParseMicrosoftFeatureDefinition(configurationSection);
}

return ParseFeatureDefinition(configurationSection);
return ParseDotnetFeatureDefinition(configurationSection);
}

private FeatureDefinition ParseFeatureDefinition(IConfigurationSection configurationSection)
private FeatureDefinition ParseDotnetFeatureDefinition(IConfigurationSection configurationSection)
{
/*

Expand Down Expand Up @@ -210,7 +255,7 @@ We support

if (string.IsNullOrEmpty(val))
{
val = configurationSection[ConfigurationFields.FeatureFiltersSectionName];
val = configurationSection[DotnetFeatureManagementFields.FeatureFiltersSectionName];
}

if (!string.IsNullOrEmpty(val) && bool.TryParse(val, out bool result) && result)
Expand All @@ -228,139 +273,29 @@ We support
}
else
{
string rawRequirementType = configurationSection[ConfigurationFields.RequirementType];

string rawFeatureStatus = configurationSection[ConfigurationFields.FeatureStatus];
string rawRequirementType = configurationSection[DotnetFeatureManagementFields.RequirementType];

if (!string.IsNullOrEmpty(rawRequirementType))
{
requirementType = ParseEnum<RequirementType>(featureName, rawRequirementType, ConfigurationFields.RequirementType);
requirementType = ParseEnum<RequirementType>(featureName, rawRequirementType, DotnetFeatureManagementFields.RequirementType);
}

if (!string.IsNullOrEmpty(rawFeatureStatus))
{
featureStatus = ParseEnum<FeatureStatus>(featureName, rawFeatureStatus, ConfigurationFields.FeatureStatus);
}

IEnumerable<IConfigurationSection> filterSections = configurationSection.GetSection(ConfigurationFields.FeatureFiltersSectionName).GetChildren();
IEnumerable<IConfigurationSection> filterSections = configurationSection.GetSection(DotnetFeatureManagementFields.FeatureFiltersSectionName).GetChildren();

foreach (IConfigurationSection section in filterSections)
{
//
// Arrays in json such as "myKey": [ "some", "values" ]
// Are accessed through the configuration system by using the array index as the property name, e.g. "myKey": { "0": "some", "1": "values" }
if (int.TryParse(section.Key, out int _) && !string.IsNullOrEmpty(section[ConfigurationFields.NameKeyword]))
if (int.TryParse(section.Key, out int _) && !string.IsNullOrEmpty(section[DotnetFeatureManagementFields.NameKeyword]))
{
enabledFor.Add(new FeatureFilterConfiguration()
{
Name = section[ConfigurationFields.NameKeyword],
Parameters = new ConfigurationWrapper(section.GetSection(ConfigurationFields.FeatureFilterConfigurationParameters))
Name = section[DotnetFeatureManagementFields.NameKeyword],
Parameters = new ConfigurationWrapper(section.GetSection(DotnetFeatureManagementFields.FeatureFilterConfigurationParameters))
});
}
}

IConfigurationSection allocationSection = configurationSection.GetSection(ConfigurationFields.AllocationSectionName);

if (allocationSection.Exists())
{
allocation = new Allocation()
{
DefaultWhenDisabled = allocationSection[ConfigurationFields.AllocationDefaultWhenDisabled],
DefaultWhenEnabled = allocationSection[ConfigurationFields.AllocationDefaultWhenEnabled],
User = allocationSection.GetSection(ConfigurationFields.UserAllocationSectionName).GetChildren().Select(userAllocation =>
{
return new UserAllocation()
{
Variant = userAllocation[ConfigurationFields.AllocationVariantKeyword],
Users = userAllocation.GetSection(ConfigurationFields.UserAllocationUsers).Get<IEnumerable<string>>()
};
}),
Group = allocationSection.GetSection(ConfigurationFields.GroupAllocationSectionName).GetChildren().Select(groupAllocation =>
{
return new GroupAllocation()
{
Variant = groupAllocation[ConfigurationFields.AllocationVariantKeyword],
Groups = groupAllocation.GetSection(ConfigurationFields.GroupAllocationGroups).Get<IEnumerable<string>>()
};
}),
Percentile = allocationSection.GetSection(ConfigurationFields.PercentileAllocationSectionName).GetChildren().Select(percentileAllocation =>
{
double from = 0;

double to = 0;

string rawFrom = percentileAllocation[ConfigurationFields.PercentileAllocationFrom];

string rawTo = percentileAllocation[ConfigurationFields.PercentileAllocationTo];

if (!string.IsNullOrEmpty(rawFrom))
{
from = ParseDouble(featureName, rawFrom, ConfigurationFields.PercentileAllocationFrom);
}

if (!string.IsNullOrEmpty(rawTo))
{
to = ParseDouble(featureName, rawTo, ConfigurationFields.PercentileAllocationTo);
}

return new PercentileAllocation()
{
Variant = percentileAllocation[ConfigurationFields.AllocationVariantKeyword],
From = from,
To = to
};
}),
Seed = allocationSection[ConfigurationFields.AllocationSeed]
};
}

IEnumerable<IConfigurationSection> variantsSections = configurationSection.GetSection(ConfigurationFields.VariantsSectionName).GetChildren();

foreach (IConfigurationSection section in variantsSections)
{
if (int.TryParse(section.Key, out int _) && !string.IsNullOrEmpty(section[ConfigurationFields.NameKeyword]))
{
StatusOverride statusOverride = StatusOverride.None;

string rawStatusOverride = section[ConfigurationFields.VariantDefinitionStatusOverride];

if (!string.IsNullOrEmpty(rawStatusOverride))
{
statusOverride = ParseEnum<StatusOverride>(configurationSection.Key, rawStatusOverride, ConfigurationFields.VariantDefinitionStatusOverride);
}

var variant = new VariantDefinition()
{
Name = section[ConfigurationFields.NameKeyword],
ConfigurationValue = section.GetSection(ConfigurationFields.VariantDefinitionConfigurationValue),
ConfigurationReference = section[ConfigurationFields.VariantDefinitionConfigurationReference],
StatusOverride = statusOverride
};

variants.Add(variant);
}
}

IConfigurationSection telemetrySection = configurationSection.GetSection(ConfigurationFields.Telemetry);

if (telemetrySection.Exists())
{
string rawTelemetryEnabled = telemetrySection[ConfigurationFields.Enabled];

if (!string.IsNullOrEmpty(rawTelemetryEnabled))
{
telemetryEnabled = ParseBool(featureName, rawTelemetryEnabled, ConfigurationFields.Enabled);
}

IConfigurationSection telemetryMetadataSection = telemetrySection.GetSection(ConfigurationFields.Metadata);

if (telemetryMetadataSection.Exists())
{
telemetryMetadata = new Dictionary<string, string>();

telemetryMetadata = telemetryMetadataSection.GetChildren().ToDictionary(x => x.Key, x => x.Value);
}
}
}

return new FeatureDefinition()
Expand Down Expand Up @@ -622,14 +557,14 @@ private IEnumerable<IConfigurationSection> GetFeatureDefinitionSections()
.FirstOrDefault(section =>
string.Equals(
section.Key,
_microsoftFeatureManagementSchemaEnabled ?
MicrosoftFeatureManagementFields.FeatureManagementSectionName :
ConfigurationFields.FeatureManagementSectionName,
_microsoftFeatureManagementSchemaEnabled ?
MicrosoftFeatureManagementFields.FeatureManagementSectionName :
DotnetFeatureManagementFields.FeatureManagementSectionName,
StringComparison.OrdinalIgnoreCase));

if (featureManagementConfigurationSection == null)
{
if (RootConfigurationFallbackEnabled && !_microsoftFeatureManagementSchemaEnabled)
Comment thread
amerjusupovic marked this conversation as resolved.
if (RootConfigurationFallbackEnabled)
{
featureManagementConfigurationSection = _configuration;
}
Expand All @@ -652,7 +587,7 @@ private IEnumerable<IConfigurationSection> GetFeatureDefinitionSections()
}

private T ParseEnum<T>(string feature, string rawValue, string fieldKeyword)
where T: struct, Enum
where T : struct, Enum
{
Debug.Assert(!string.IsNullOrEmpty(rawValue));

Expand Down
45 changes: 0 additions & 45 deletions src/Microsoft.FeatureManagement/ConfigurationFields.cs

This file was deleted.

19 changes: 19 additions & 0 deletions src/Microsoft.FeatureManagement/DotnetFeatureManagementFields.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
//

namespace Microsoft.FeatureManagement
{
internal static class DotnetFeatureManagementFields
{
public const string RequirementType = "RequirementType";

// Feature filters keywords
public const string FeatureFiltersSectionName = "EnabledFor";
public const string FeatureFilterConfigurationParameters = "Parameters";

// Other keywords
public const string NameKeyword = "Name";
public const string FeatureManagementSectionName = "FeatureManagement";
}
}
Loading