Skip to content

Commit e11db12

Browse files
Merge branch 'preview' of https://github.com/microsoft/FeatureManagement-Dotnet into zhiyuanliang/recurring-time-window
2 parents 9f10985 + e7184f2 commit e11db12

35 files changed

+905
-431
lines changed

Microsoft.FeatureManagement.sln

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConsoleApp", "examples\Cons
1919
EndProject
2020
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TargetingConsoleApp", "examples\TargetingConsoleApp\TargetingConsoleApp.csproj", "{6558C21E-CF20-4278-AA08-EB9D1DF29D66}"
2121
EndProject
22-
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RazorPages", "examples\RazorPages\RazorPages.csproj", "{BA29A1BB-81D5-4EB1-AF37-6ECF64AF27E2}"
22+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RazorPages", "examples\RazorPages\RazorPages.csproj", "{BA29A1BB-81D5-4EB1-AF37-6ECF64AF27E2}"
23+
EndProject
24+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.FeatureManagement.AspNetCore", "tests\Tests.FeatureManagement.AspNetCore\Tests.FeatureManagement.AspNetCore.csproj", "{FC0DC3E2-5646-4AEC-A7DB-2D6167BC3BB4}"
2325
EndProject
2426
Global
2527
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -55,6 +57,10 @@ Global
5557
{BA29A1BB-81D5-4EB1-AF37-6ECF64AF27E2}.Debug|Any CPU.Build.0 = Debug|Any CPU
5658
{BA29A1BB-81D5-4EB1-AF37-6ECF64AF27E2}.Release|Any CPU.ActiveCfg = Release|Any CPU
5759
{BA29A1BB-81D5-4EB1-AF37-6ECF64AF27E2}.Release|Any CPU.Build.0 = Release|Any CPU
60+
{FC0DC3E2-5646-4AEC-A7DB-2D6167BC3BB4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
61+
{FC0DC3E2-5646-4AEC-A7DB-2D6167BC3BB4}.Debug|Any CPU.Build.0 = Debug|Any CPU
62+
{FC0DC3E2-5646-4AEC-A7DB-2D6167BC3BB4}.Release|Any CPU.ActiveCfg = Release|Any CPU
63+
{FC0DC3E2-5646-4AEC-A7DB-2D6167BC3BB4}.Release|Any CPU.Build.0 = Release|Any CPU
5864
EndGlobalSection
5965
GlobalSection(SolutionProperties) = preSolution
6066
HideSolutionNode = FALSE
@@ -65,6 +71,7 @@ Global
6571
{E50FB931-7A42-440E-AC47-B8DFE5E15394} = {FB5C34DF-695C-4DF9-8AED-B3EA2516EA72}
6672
{6558C21E-CF20-4278-AA08-EB9D1DF29D66} = {FB5C34DF-695C-4DF9-8AED-B3EA2516EA72}
6773
{BA29A1BB-81D5-4EB1-AF37-6ECF64AF27E2} = {FB5C34DF-695C-4DF9-8AED-B3EA2516EA72}
74+
{FC0DC3E2-5646-4AEC-A7DB-2D6167BC3BB4} = {8ED6FFEE-4037-49A2-9709-BC519C104A90}
6875
EndGlobalSection
6976
GlobalSection(ExtensibilityGlobals) = postSolution
7077
SolutionGuid = {84DA6C54-F140-4518-A1B4-E4CF42117FBD}

build/install-dotnet.ps1

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
1-
# Installs .NET Core 2.1, .NET 5 and .NET 6 for CI/CD environment
1+
# Installs .NET 6 and .NET 7 for CI/CD environment
22
# see: https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet-install-script#examples
33

44
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12;
55

6-
&([scriptblock]::Create((Invoke-WebRequest -UseBasicParsing 'https://dot.net/v1/dotnet-install.ps1'))) -Version 2.1.816
6+
&([scriptblock]::Create((Invoke-WebRequest -UseBasicParsing 'https://dot.net/v1/dotnet-install.ps1'))) -Channel 6.0
77

8-
&([scriptblock]::Create((Invoke-WebRequest -UseBasicParsing 'https://dot.net/v1/dotnet-install.ps1'))) -Version 5.0.408
9-
10-
&([scriptblock]::Create((Invoke-WebRequest -UseBasicParsing 'https://dot.net/v1/dotnet-install.ps1')))
8+
&([scriptblock]::Create((Invoke-WebRequest -UseBasicParsing 'https://dot.net/v1/dotnet-install.ps1'))) -Channel 7.0

examples/RazorPages/RazorPages.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<Project Sdk="Microsoft.NET.Sdk.Web">
22

33
<PropertyGroup>
4-
<TargetFramework>netcoreapp3.1</TargetFramework>
4+
<TargetFramework>net6.0</TargetFramework>
55
</PropertyGroup>
66

77
<ItemGroup>

src/Microsoft.FeatureManagement.AspNetCore/AspNetCoreFeatureManagementBuilderExtensions.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
//
44
using Microsoft.AspNetCore.Mvc.Filters;
55
using Microsoft.Extensions.DependencyInjection;
6-
using Microsoft.FeatureManagement.FeatureFilters;
76
using Microsoft.FeatureManagement.Mvc;
87
using System;
98
using System.Collections.Generic;

src/Microsoft.FeatureManagement.AspNetCore/Microsoft.FeatureManagement.AspNetCore.csproj

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
<Import Project="..\..\build\Versioning.props" />
1212

1313
<PropertyGroup>
14-
<TargetFrameworks>netstandard2.0;netcoreapp3.1;net5.0;net6.0</TargetFrameworks>
14+
<TargetFrameworks>net6.0;net7.0</TargetFrameworks>
1515
<SignAssembly>true</SignAssembly>
1616
<DelaySign>false</DelaySign>
1717
<AssemblyOriginatorKeyFile>..\..\build\Microsoft.FeatureManagement.snk</AssemblyOriginatorKeyFile>
@@ -28,17 +28,9 @@
2828
<PackageIconUrl>https://aka.ms/AzureAppConfigurationPackageIcon</PackageIconUrl>
2929
<Copyright>© Microsoft Corporation. All rights reserved.</Copyright>
3030
</PropertyGroup>
31-
31+
3232
<ItemGroup>
3333
<ProjectReference Include="..\Microsoft.FeatureManagement\Microsoft.FeatureManagement.csproj" />
34-
</ItemGroup>
35-
36-
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
37-
<PackageReference Include="Microsoft.AspNetCore.Mvc.RazorPages" Version="2.1.11" />
38-
<PackageReference Include="Microsoft.AspNetCore.Mvc.TagHelpers" Version="2.1.3" />
39-
</ItemGroup>
40-
41-
<ItemGroup Condition="'$(TargetFramework)' != 'netstandard2.0'">
4234
<FrameworkReference Include="Microsoft.AspNetCore.App" />
4335
</ItemGroup>
4436

src/Microsoft.FeatureManagement/ConfigurationFeatureDefinitionProvider.cs

Lines changed: 32 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Licensed under the MIT license.
33
//
44
using Microsoft.Extensions.Configuration;
5+
using Microsoft.Extensions.Logging;
56
using Microsoft.Extensions.Primitives;
67
using System;
78
using System.Collections.Concurrent;
@@ -25,13 +26,15 @@ sealed class ConfigurationFeatureDefinitionProvider : IFeatureDefinitionProvider
2526
private readonly IConfiguration _configuration;
2627
private readonly ConcurrentDictionary<string, FeatureDefinition> _definitions;
2728
private IDisposable _changeSubscription;
29+
private readonly ILogger _logger;
2830
private int _stale = 0;
2931

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

32-
public ConfigurationFeatureDefinitionProvider(IConfiguration configuration)
34+
public ConfigurationFeatureDefinitionProvider(IConfiguration configuration, ILoggerFactory loggerFactory)
3335
{
3436
_configuration = configuration ?? throw new ArgumentNullException(nameof(configuration));
37+
_logger = loggerFactory?.CreateLogger<ConfigurationFeatureDefinitionProvider>() ?? throw new ArgumentNullException(nameof(loggerFactory));
3538
_definitions = new ConcurrentDictionary<string, FeatureDefinition>();
3639

3740
_changeSubscription = ChangeToken.OnChange(
@@ -145,6 +148,10 @@ We support
145148

146149
var enabledFor = new List<FeatureFilterConfiguration>();
147150

151+
bool telemetryEnabled = false;
152+
153+
Dictionary<string, string> telemetryMetadata = null;
154+
148155
string val = configurationSection.Value; // configuration[$"{featureName}"];
149156

150157
if (string.IsNullOrEmpty(val))
@@ -280,6 +287,17 @@ We support
280287
variants.Add(variant);
281288
}
282289
}
290+
291+
telemetryEnabled = configurationSection.GetValue<bool>("TelemetryEnabled");
292+
293+
IConfigurationSection telemetryMetadataSection = configurationSection.GetSection("TelemetryMetadata");
294+
295+
if (telemetryMetadataSection.Exists())
296+
{
297+
telemetryMetadata = new Dictionary<string, string>();
298+
299+
telemetryMetadata = telemetryMetadataSection.GetChildren().ToDictionary(x => x.Key, x => x.Value);
300+
}
283301
}
284302

285303
return new FeatureDefinition()
@@ -289,22 +307,26 @@ We support
289307
RequirementType = requirementType,
290308
Status = featureStatus,
291309
Allocation = allocation,
292-
Variants = variants
310+
Variants = variants,
311+
TelemetryEnabled = telemetryEnabled,
312+
TelemetryMetadata = telemetryMetadata
293313
};
294314
}
295315

296316
private IEnumerable<IConfigurationSection> GetFeatureDefinitionSections()
297317
{
298-
if (_configuration.GetChildren().Any(s => s.Key.Equals(ConfigurationFields.FeatureManagementSectionName, StringComparison.OrdinalIgnoreCase)))
299-
{
300-
//
301-
// Look for feature definitions under the "FeatureManagement" section
302-
return _configuration.GetSection(ConfigurationFields.FeatureManagementSectionName).GetChildren();
303-
}
304-
else
318+
//
319+
// Look for feature definitions under the "FeatureManagement" section
320+
IConfigurationSection featureManagementConfigurationSection = _configuration.GetSection(ConfigurationFields.FeatureManagementSectionName);
321+
322+
if (featureManagementConfigurationSection.Exists())
305323
{
306-
return _configuration.GetChildren();
324+
return featureManagementConfigurationSection.GetChildren();
307325
}
326+
327+
_logger.LogDebug($"No configuration section named '{ConfigurationFields.FeatureManagementSectionName}' was found.");
328+
329+
return Enumerable.Empty<IConfigurationSection>();
308330
}
309331

310332
private T ParseEnum<T>(string feature, string rawValue, string fieldKeyword)

src/Microsoft.FeatureManagement/FeatureDefinition.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,5 +42,15 @@ public class FeatureDefinition
4242
/// A list of variant definitions that specify a configuration to return when assigned.
4343
/// </summary>
4444
public IEnumerable<VariantDefinition> Variants { get; set; } = Enumerable.Empty<VariantDefinition>();
45+
46+
/// <summary>
47+
/// A flag to enable or disable sending telemetry events to the registered <see cref="ITelemetryPublisher">.
48+
/// </summary>
49+
public bool TelemetryEnabled { get; set; }
50+
51+
/// <summary>
52+
/// A container for metadata relevant to telemetry.
53+
/// </summary>
54+
public IReadOnlyDictionary<string, string> TelemetryMetadata { get; set; }
4555
}
4656
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
using Microsoft.Extensions.DependencyInjection;
2+
using Microsoft.FeatureManagement.Telemetry;
3+
using System;
4+
using System.Collections.Generic;
5+
6+
namespace Microsoft.FeatureManagement
7+
{
8+
/// <summary>
9+
/// Extensions used to add feature management functionality.
10+
/// </summary>
11+
public static class FeatureManagementBuilderExtensions
12+
{
13+
/// <summary>
14+
/// Adds a telemetry publisher to the feature management system.
15+
/// </summary>
16+
/// <param name="builder">The <see cref="IFeatureManagementBuilder"/> used to customize feature management functionality.</param>
17+
/// <returns>A <see cref="IFeatureManagementBuilder"/> that can be used to customize feature management functionality.</returns>
18+
public static IFeatureManagementBuilder AddTelemetryPublisher<T>(this IFeatureManagementBuilder builder) where T : ITelemetryPublisher
19+
{
20+
builder.AddTelemetryPublisher(sp => ActivatorUtilities.CreateInstance(sp, typeof(T)) as ITelemetryPublisher);
21+
22+
return builder;
23+
}
24+
25+
private static IFeatureManagementBuilder AddTelemetryPublisher(this IFeatureManagementBuilder builder, Func<IServiceProvider, ITelemetryPublisher> factory)
26+
{
27+
builder.Services.Configure<FeatureManagementOptions>(options =>
28+
{
29+
if (options.TelemetryPublisherFactories == null)
30+
{
31+
options.TelemetryPublisherFactories = new List<Func<IServiceProvider, ITelemetryPublisher>>();
32+
}
33+
34+
options.TelemetryPublisherFactories.Add(factory);
35+
});
36+
37+
return builder;
38+
}
39+
}
40+
}

src/Microsoft.FeatureManagement/FeatureManagementOptions.cs

Lines changed: 10 additions & 0 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 Microsoft.FeatureManagement.Telemetry;
5+
using System;
6+
using System.Collections.Generic;
7+
48
namespace Microsoft.FeatureManagement
59
{
610
/// <summary>
@@ -22,5 +26,11 @@ public class FeatureManagementOptions
2226
/// The default value is true.
2327
/// </summary>
2428
public bool IgnoreMissingFeatures { get; set; } = true;
29+
30+
/// <summary>
31+
/// Holds a collection of factories that can be used to create <see cref="ITelemetryPublisher"/> instances.
32+
/// This avoids the need to add the publishers to the service collection.
33+
/// </summary>
34+
internal ICollection<Func<IServiceProvider, ITelemetryPublisher>> TelemetryPublisherFactories { get; set; }
2535
}
2636
}

0 commit comments

Comments
 (0)