├── examples ├── RazorPages │ ├── Pages │ │ ├── _ViewStart.cshtml │ │ ├── _ViewImports.cshtml │ │ ├── Privacy.cshtml │ │ ├── Shared │ │ │ ├── _ValidationScriptsPartial.cshtml │ │ │ └── _Layout.cshtml.css │ │ ├── Privacy.cshtml.cs │ │ ├── Index.cshtml.cs │ │ ├── Index.cshtml │ │ ├── Error.cshtml.cs │ │ └── Error.cshtml │ ├── wwwroot │ │ ├── favicon.ico │ │ ├── js │ │ │ └── site.js │ │ ├── css │ │ │ └── site.css │ │ └── lib │ │ │ ├── jquery-validation-unobtrusive │ │ │ └── LICENSE.txt │ │ │ ├── jquery-validation │ │ │ └── LICENSE.md │ │ │ ├── bootstrap │ │ │ └── LICENSE │ │ │ └── jquery │ │ │ └── LICENSE.txt │ ├── appsettings.Development.json │ ├── appsettings.json │ ├── RazorPages.csproj │ └── Program.cs ├── FeatureFlagDemo │ ├── Views │ │ ├── _ViewStart.cshtml │ │ ├── Shared │ │ │ ├── FeatureNotEnabled.cshtml │ │ │ ├── _ValidationScriptsPartial.cshtml │ │ │ ├── Error.cshtml.cs │ │ │ ├── Error.cshtml │ │ │ ├── _Layout.cshtml.css │ │ │ └── _CookieConsentPartial.cshtml │ │ ├── Beta │ │ │ └── Index.cshtml │ │ ├── Home │ │ │ ├── Privacy.cshtml │ │ │ ├── About.cshtml │ │ │ ├── Contact.cshtml │ │ │ └── Beta.cshtml │ │ └── _ViewImports.cshtml │ ├── wwwroot │ │ ├── favicon.ico │ │ ├── js │ │ │ └── site.js │ │ ├── lib │ │ │ ├── jquery-validation-unobtrusive │ │ │ │ └── LICENSE.txt │ │ │ ├── jquery-validation │ │ │ │ └── LICENSE.md │ │ │ ├── bootstrap │ │ │ │ └── LICENSE │ │ │ └── jquery │ │ │ │ └── LICENSE.txt │ │ └── css │ │ │ └── site.css │ ├── appsettings.Development.json │ ├── Authentication │ │ ├── Schemes.cs │ │ ├── QueryStringAuthenticationOptions.cs │ │ └── QueryStringAuthenticationExtensions.cs │ ├── BrowserFilterSettings.cs │ ├── Models │ │ └── ErrorViewModel.cs │ ├── SuperUserFilter.cs │ ├── MyFeatureFlags.cs │ ├── Controllers │ │ ├── BetaController.cs │ │ └── HomeController.cs │ ├── ThirdPartyActionFilter.cs │ ├── ThirdPartyMiddleware.cs │ ├── FeatureNotEnabledDisabledHandler.cs │ ├── FeatureFlagDemo.csproj │ ├── Program.cs │ └── BrowserFilter.cs ├── VariantServiceDemo │ ├── Pages │ │ ├── _ViewStart.cshtml │ │ ├── _ViewImports.cshtml │ │ ├── Privacy.cshtml │ │ ├── Shared │ │ │ ├── _ValidationScriptsPartial.cshtml │ │ │ └── _Layout.cshtml.css │ │ ├── Privacy.cshtml.cs │ │ ├── Error.cshtml.cs │ │ ├── Error.cshtml │ │ ├── Index.cshtml │ │ └── Index.cshtml.cs │ ├── wwwroot │ │ ├── favicon.ico │ │ ├── js │ │ │ └── site.js │ │ ├── lib │ │ │ ├── jquery-validation-unobtrusive │ │ │ │ └── LICENSE.txt │ │ │ ├── jquery-validation │ │ │ │ └── LICENSE.md │ │ │ ├── bootstrap │ │ │ │ └── LICENSE │ │ │ └── jquery │ │ │ │ └── LICENSE.txt │ │ └── css │ │ │ └── site.css │ ├── appsettings.Development.json │ ├── ICalculator.cs │ ├── DefaultCalculator.cs │ ├── RemoteCalculator.cs │ ├── VariantServiceDemo.csproj │ ├── appsettings.json │ └── Program.cs ├── VariantAndTelemetryDemo │ ├── Pages │ │ ├── _ViewStart.cshtml │ │ ├── RandomizeUser.cshtml │ │ ├── Checkout.cshtml.cs │ │ ├── Shared │ │ │ ├── _ValidationScriptsPartial.cshtml │ │ │ └── _Layout.cshtml.css │ │ ├── _ViewImports.cshtml │ │ ├── Checkout.cshtml │ │ ├── Error.cshtml.cs │ │ ├── Error.cshtml │ │ └── RandomizeUser.cshtml.cs │ ├── wwwroot │ │ ├── favicon.ico │ │ ├── js │ │ │ └── site.js │ │ ├── css │ │ │ └── site.css │ │ └── lib │ │ │ ├── jquery-validation-unobtrusive │ │ │ └── LICENSE.txt │ │ │ ├── jquery-validation │ │ │ └── LICENSE.md │ │ │ ├── bootstrap │ │ │ └── LICENSE │ │ │ └── jquery │ │ │ └── LICENSE.txt │ ├── appsettings.Development.json │ ├── VariantAndTelemetryDemo.csproj │ ├── Program.cs │ └── appsettings.json ├── BlazorServerApp │ ├── wwwroot │ │ ├── favicon.ico │ │ └── css │ │ │ └── open-iconic │ │ │ ├── font │ │ │ └── fonts │ │ │ │ ├── open-iconic.eot │ │ │ │ ├── open-iconic.otf │ │ │ │ ├── open-iconic.ttf │ │ │ │ └── open-iconic.woff │ │ │ └── ICON-LICENSE │ ├── UserAgentContext.cs │ ├── BrowserFilterSettings.cs │ ├── appsettings.Development.json │ ├── Data │ │ ├── WeatherForecast.cs │ │ └── WeatherForecastService.cs │ ├── BlazorServerApp.csproj │ ├── _Imports.razor │ ├── Pages │ │ ├── Counter.razor │ │ ├── Error.cshtml.cs │ │ ├── _Layout.cshtml │ │ ├── Index.razor │ │ ├── FetchData.razor │ │ ├── _Host.cshtml │ │ └── Error.cshtml │ ├── Shared │ │ ├── SurveyPrompt.razor │ │ ├── MainLayout.razor │ │ ├── NavMenu.razor.css │ │ ├── NavMenu.razor │ │ └── MainLayout.razor.css │ ├── App.razor │ ├── appsettings.json │ ├── MyTargetingContextAccessor.cs │ └── Program.cs ├── ConsoleApp │ ├── FeatureFilters │ │ ├── IAccountContext.cs │ │ └── AccountIdFilter.cs │ ├── AccountServiceContext.cs │ ├── appsettings.json │ ├── ConsoleApp.csproj │ └── Program.cs └── TargetingConsoleApp │ ├── Identity │ ├── IUserRepository.cs │ └── User.cs │ ├── TargetingConsoleApp.csproj │ ├── appsettings.json │ └── Program.cs ├── tests ├── Tests.FeatureManagement.AspNetCore │ ├── Pages │ │ ├── RazorTestAll.cshtml │ │ ├── RazorTestAny.cshtml │ │ ├── RazorTestAllNegate.cshtml │ │ ├── RazorTestAnyNegate.cshtml │ │ ├── _ViewImports.cshtml │ │ ├── RazorTestAll.cshtml.cs │ │ ├── RazorTestAllNegate.cshtml.cs │ │ ├── RazorTestAny.cshtml.cs │ │ └── RazorTestAnyNegate.cshtml.cs │ ├── Features.cs │ ├── appsettings.json │ ├── MvcFilter.cs │ ├── TestFilter.cs │ └── TestController.cs └── Tests.FeatureManagement │ ├── IAccountContext.cs │ ├── OnDemandClock.cs │ ├── appsettings1.json │ ├── appsettings3.json │ ├── DotnetFeatureManagementSchema.json │ ├── appsettings2.json │ ├── AppContext.cs │ ├── OnDemandTargetingContextAccessor.cs │ ├── ContextualTestFilter.cs │ ├── VariantServices.cs │ ├── TestFilter.cs │ ├── TestCache.cs │ ├── CustomTargetingFilter.cs │ ├── InvalidFeatureFilter.cs │ └── InMemoryFeatureDefinitionProvider.cs ├── Microsoft.FeatureManagement.sln.licenseheader ├── Directory.Build.props ├── .gitattributes ├── .github ├── pull_request_template.md └── workflows │ └── ci.yml ├── NuGet.Config ├── test.ps1 ├── src ├── Microsoft.FeatureManagement │ ├── IFeatureFilterMetadata.cs │ ├── IFeatureManagerSnapshot.cs │ ├── IVariantFeatureManagerSnapshot.cs │ ├── IFeatureDefinitionProviderCacheable.cs │ ├── AssemblyInfo.cs │ ├── Targeting │ │ ├── TargetingEvaluationOptions.cs │ │ ├── TargetingFilterSettings.cs │ │ ├── ITargetingContextAccessor.cs │ │ ├── BasicAudience.cs │ │ ├── GroupRollout.cs │ │ ├── ITargetingContext.cs │ │ ├── TargetingContext.cs │ │ └── Audience.cs │ ├── FeatureStatus.cs │ ├── Variant.cs │ ├── FeatureFilters │ │ ├── PercentageFilterSettings.cs │ │ ├── Recurrence │ │ │ ├── Recurrence.cs │ │ │ ├── RecurrencePatternType.cs │ │ │ ├── RecurrenceRange.cs │ │ │ ├── RecurrenceRangeType.cs │ │ │ └── RecurrencePattern.cs │ │ └── TimeWindowFilterSettings.cs │ ├── Allocation │ │ ├── GroupAllocation.cs │ │ ├── UserAllocation.cs │ │ ├── PercentileAllocation.cs │ │ └── Allocation.cs │ ├── RequirementType.cs │ ├── DotnetFeatureManagementFields.cs │ ├── OnDemandConfigurationProvider.cs │ ├── StatusOverride.cs │ ├── TelemetryConfiguration.cs │ ├── IVariantServiceProvider.cs │ ├── FeatureFilterConfiguration.cs │ ├── Utils │ │ └── RandomGenerator.cs │ ├── VariantDefinition.cs │ ├── IFilterParametersBinder.cs │ ├── IFeatureDefinitionProvider.cs │ ├── FilterAliasAttribute.cs │ ├── FeatureManagementException.cs │ ├── VariantServiceAliasAttribute.cs │ ├── ISessionManager.cs │ ├── FeatureManagementError.cs │ ├── IFeatureFilter.cs │ ├── FeatureManagementOptions.cs │ ├── Telemetry │ │ ├── EvaluationEvent.cs │ │ └── VariantAssignmentReason.cs │ ├── ConfigurationWrapper.cs │ ├── VariantFeatureManagerExtensions.cs │ ├── FeatureFilterEvaluationContext.cs │ ├── IFeatureManager.cs │ ├── IFeatureManagementBuilder.cs │ └── IContextualFeatureFilter.cs ├── Microsoft.FeatureManagement.AspNetCore │ ├── DisabledFeatureFilter.cs │ ├── NotFoundDisabledFeatureHandler.cs │ ├── IDisabledFeatureHandler.cs │ ├── InlineDisabledFeatureHandler.cs │ └── Extensions │ │ └── EnumerableExtensions.cs └── Microsoft.FeatureManagement.Telemetry.ApplicationInsights │ ├── TargetingTelemetryInitializer.cs │ └── ApplicationInsightsHostedService.cs ├── LICENSE ├── pack.ps1 └── .editorconfig /examples/RazorPages/Pages/_ViewStart.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | Layout = "_Layout"; 3 | } 4 | -------------------------------------------------------------------------------- /examples/FeatureFlagDemo/Views/_ViewStart.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | Layout = "_Layout"; 3 | } 4 | -------------------------------------------------------------------------------- /examples/VariantServiceDemo/Pages/_ViewStart.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | Layout = "_Layout"; 3 | } 4 | -------------------------------------------------------------------------------- /examples/VariantAndTelemetryDemo/Pages/_ViewStart.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | Layout = "_Layout"; 3 | } 4 | -------------------------------------------------------------------------------- /tests/Tests.FeatureManagement.AspNetCore/Pages/RazorTestAll.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model RazorTestAllModel -------------------------------------------------------------------------------- /tests/Tests.FeatureManagement.AspNetCore/Pages/RazorTestAny.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model RazorTestAnyModel -------------------------------------------------------------------------------- /tests/Tests.FeatureManagement.AspNetCore/Pages/RazorTestAllNegate.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model RazorTestAllNegateModel 3 | -------------------------------------------------------------------------------- /examples/VariantAndTelemetryDemo/Pages/RandomizeUser.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model VariantAndTelemetryDemo.Pages.RandomizeUserModel 3 | @{ 4 | } 5 | -------------------------------------------------------------------------------- /examples/FeatureFlagDemo/Views/Shared/FeatureNotEnabled.cshtml: -------------------------------------------------------------------------------- 1 |

:(

2 |

The feature @ViewData["FeatureName"] is not enabled.

3 | -------------------------------------------------------------------------------- /examples/RazorPages/wwwroot/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/FeatureManagement-Dotnet/HEAD/examples/RazorPages/wwwroot/favicon.ico -------------------------------------------------------------------------------- /Microsoft.FeatureManagement.sln.licenseheader: -------------------------------------------------------------------------------- 1 | extensions: .cs 2 | // Copyright (c) Microsoft Corporation. 3 | // Licensed under the MIT license. 4 | // -------------------------------------------------------------------------------- /examples/BlazorServerApp/wwwroot/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/FeatureManagement-Dotnet/HEAD/examples/BlazorServerApp/wwwroot/favicon.ico -------------------------------------------------------------------------------- /examples/FeatureFlagDemo/wwwroot/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/FeatureManagement-Dotnet/HEAD/examples/FeatureFlagDemo/wwwroot/favicon.ico -------------------------------------------------------------------------------- /examples/RazorPages/Pages/_ViewImports.cshtml: -------------------------------------------------------------------------------- 1 | @using RazorPages 2 | @namespace RazorPages.Pages 3 | @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers 4 | -------------------------------------------------------------------------------- /examples/FeatureFlagDemo/Views/Beta/Index.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewData["Title"] = "Beta Home Page"; 3 | } 4 | 5 |

6 | This is the beta website. 7 |

-------------------------------------------------------------------------------- /Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | True 5 | 6 | 7 | -------------------------------------------------------------------------------- /examples/VariantServiceDemo/wwwroot/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/FeatureManagement-Dotnet/HEAD/examples/VariantServiceDemo/wwwroot/favicon.ico -------------------------------------------------------------------------------- /tests/Tests.FeatureManagement.AspNetCore/Pages/RazorTestAnyNegate.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model Tests.FeatureManagement.AspNetCore.Pages.RazorTestAnyNegateModel 3 | @{ 4 | } 5 | -------------------------------------------------------------------------------- /examples/VariantAndTelemetryDemo/wwwroot/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/FeatureManagement-Dotnet/HEAD/examples/VariantAndTelemetryDemo/wwwroot/favicon.ico -------------------------------------------------------------------------------- /examples/VariantServiceDemo/Pages/_ViewImports.cshtml: -------------------------------------------------------------------------------- 1 | @using VariantServiceDemo 2 | @namespace VariantServiceDemo.Pages 3 | @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers 4 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # If there are abnormal line endings in any file, run "git add --renormalize ", 2 | # review the changes, and commit them to fix the line endings. 3 | * text=auto 4 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ## Why this PR? 2 | 3 | -motivation for making this change- 4 | 5 | ## Visible Changes 6 | 7 | -changes that are visible to developers using this library- 8 | -------------------------------------------------------------------------------- /examples/BlazorServerApp/UserAgentContext.cs: -------------------------------------------------------------------------------- 1 | namespace BlazorServerApp 2 | { 3 | public class UserAgentContext 4 | { 5 | public string UserAgent { get; set; } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /examples/FeatureFlagDemo/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Error" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /examples/FeatureFlagDemo/Views/Home/Privacy.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewData["Title"] = "Privacy Policy"; 3 | } 4 |

@ViewData["Title"]

5 | 6 |

Use this page to detail your site's privacy policy.

7 | -------------------------------------------------------------------------------- /examples/BlazorServerApp/wwwroot/css/open-iconic/font/fonts/open-iconic.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/FeatureManagement-Dotnet/HEAD/examples/BlazorServerApp/wwwroot/css/open-iconic/font/fonts/open-iconic.eot -------------------------------------------------------------------------------- /examples/BlazorServerApp/wwwroot/css/open-iconic/font/fonts/open-iconic.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/FeatureManagement-Dotnet/HEAD/examples/BlazorServerApp/wwwroot/css/open-iconic/font/fonts/open-iconic.otf -------------------------------------------------------------------------------- /examples/BlazorServerApp/wwwroot/css/open-iconic/font/fonts/open-iconic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/FeatureManagement-Dotnet/HEAD/examples/BlazorServerApp/wwwroot/css/open-iconic/font/fonts/open-iconic.ttf -------------------------------------------------------------------------------- /examples/BlazorServerApp/wwwroot/css/open-iconic/font/fonts/open-iconic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/FeatureManagement-Dotnet/HEAD/examples/BlazorServerApp/wwwroot/css/open-iconic/font/fonts/open-iconic.woff -------------------------------------------------------------------------------- /examples/ConsoleApp/FeatureFilters/IAccountContext.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | // 4 | public interface IAccountContext 5 | { 6 | string AccountId { get; } 7 | } 8 | -------------------------------------------------------------------------------- /tests/Tests.FeatureManagement.AspNetCore/Pages/_ViewImports.cshtml: -------------------------------------------------------------------------------- 1 | @using Tests.FeatureManagement.AspNetCore 2 | @namespace Tests.FeatureManagement.AspNetCore.Pages 3 | @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers 4 | -------------------------------------------------------------------------------- /examples/RazorPages/Pages/Privacy.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model PrivacyModel 3 | @{ 4 | ViewData["Title"] = "Privacy Policy"; 5 | } 6 |

@ViewData["Title"]

7 | 8 |

Use this page to detail your site's privacy policy.

9 | -------------------------------------------------------------------------------- /examples/VariantAndTelemetryDemo/Pages/Checkout.cshtml.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc.RazorPages; 2 | 3 | namespace VariantAndTelemetryDemo.Pages 4 | { 5 | public class CheckoutModel : PageModel 6 | { 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /NuGet.Config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /examples/BlazorServerApp/BrowserFilterSettings.cs: -------------------------------------------------------------------------------- 1 | namespace BlazorServerApp 2 | { 3 | public class BrowserFilterSettings 4 | { 5 | public IList AllowedBrowsers { get; set; } = new List(); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /examples/ConsoleApp/AccountServiceContext.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | // 4 | class AccountServiceContext : IAccountContext 5 | { 6 | public string AccountId { get; set; } 7 | } 8 | -------------------------------------------------------------------------------- /examples/RazorPages/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "DetailedErrors": true, 3 | "Logging": { 4 | "LogLevel": { 5 | "Default": "Information", 6 | "Microsoft.AspNetCore": "Warning" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /examples/VariantServiceDemo/Pages/Privacy.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model PrivacyModel 3 | @{ 4 | ViewData["Title"] = "Privacy Policy"; 5 | } 6 |

@ViewData["Title"]

7 | 8 |

Use this page to detail your site's privacy policy.

9 | -------------------------------------------------------------------------------- /test.ps1: -------------------------------------------------------------------------------- 1 | $ErrorActionPreference = "Stop" 2 | 3 | $dotnet = & "$PSScriptRoot/build/resolve-dotnet.ps1" 4 | 5 | & $dotnet test "$PSScriptRoot\tests\Tests.FeatureManagement\Tests.FeatureManagement.csproj" --logger trx 6 | 7 | exit $LASTEXITCODE -------------------------------------------------------------------------------- /examples/BlazorServerApp/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "DetailedErrors": true, 3 | "Logging": { 4 | "LogLevel": { 5 | "Default": "Information", 6 | "Microsoft.AspNetCore": "Warning" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /examples/RazorPages/Pages/Shared/_ValidationScriptsPartial.cshtml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /examples/VariantServiceDemo/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "DetailedErrors": true, 3 | "Logging": { 4 | "LogLevel": { 5 | "Default": "Information", 6 | "Microsoft.AspNetCore": "Warning" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /examples/FeatureFlagDemo/Views/Shared/_ValidationScriptsPartial.cshtml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /examples/VariantAndTelemetryDemo/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "DetailedErrors": true, 3 | "Logging": { 4 | "LogLevel": { 5 | "Default": "Information", 6 | "Microsoft.AspNetCore": "Warning" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /examples/VariantServiceDemo/Pages/Shared/_ValidationScriptsPartial.cshtml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /examples/VariantAndTelemetryDemo/Pages/Shared/_ValidationScriptsPartial.cshtml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /examples/FeatureFlagDemo/Views/_ViewImports.cshtml: -------------------------------------------------------------------------------- 1 | @using FeatureFlagDemo 2 | @using FeatureFlagDemo.Models 3 | @namespace FeatureFlagDemo.Pages 4 | @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers 5 | @addTagHelper *, Microsoft.FeatureManagement.AspNetCore 6 | -------------------------------------------------------------------------------- /examples/RazorPages/wwwroot/js/site.js: -------------------------------------------------------------------------------- 1 | // Please see documentation at https://docs.microsoft.com/aspnet/core/client-side/bundling-and-minification 2 | // for details on configuring this project to bundle and minify static web assets. 3 | 4 | // Write your JavaScript code. 5 | -------------------------------------------------------------------------------- /examples/FeatureFlagDemo/wwwroot/js/site.js: -------------------------------------------------------------------------------- 1 | // Please see documentation at https://docs.microsoft.com/aspnet/core/client-side/bundling-and-minification 2 | // for details on configuring this project to bundle and minify static web assets. 3 | 4 | // Write your JavaScript code. 5 | -------------------------------------------------------------------------------- /examples/VariantServiceDemo/wwwroot/js/site.js: -------------------------------------------------------------------------------- 1 | // Please see documentation at https://docs.microsoft.com/aspnet/core/client-side/bundling-and-minification 2 | // for details on configuring this project to bundle and minify static web assets. 3 | 4 | // Write your JavaScript code. 5 | -------------------------------------------------------------------------------- /examples/VariantAndTelemetryDemo/wwwroot/js/site.js: -------------------------------------------------------------------------------- 1 | // Please see documentation at https://docs.microsoft.com/aspnet/core/client-side/bundling-and-minification 2 | // for details on configuring this project to bundle and minify static web assets. 3 | 4 | // Write your JavaScript code. 5 | -------------------------------------------------------------------------------- /tests/Tests.FeatureManagement/IAccountContext.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | // 4 | namespace Tests.FeatureManagement 5 | { 6 | interface IAccountContext 7 | { 8 | string AccountId { get; } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /examples/RazorPages/wwwroot/css/site.css: -------------------------------------------------------------------------------- 1 | html { 2 | font-size: 14px; 3 | } 4 | 5 | @media (min-width: 768px) { 6 | html { 7 | font-size: 16px; 8 | } 9 | } 10 | 11 | html { 12 | position: relative; 13 | min-height: 100%; 14 | } 15 | 16 | body { 17 | margin-bottom: 60px; 18 | } -------------------------------------------------------------------------------- /examples/VariantServiceDemo/ICalculator.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | // 4 | namespace VariantServiceDemo 5 | { 6 | public interface ICalculator 7 | { 8 | public Task AddAsync(double a, double b); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /examples/TargetingConsoleApp/Identity/IUserRepository.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | // 4 | namespace TargetingConsoleApp.Identity 5 | { 6 | interface IUserRepository 7 | { 8 | Task GetUser(string id); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /examples/FeatureFlagDemo/Authentication/Schemes.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | // 4 | namespace FeatureFlagDemo.Authentication 5 | { 6 | public class Schemes 7 | { 8 | public const string QueryString = "QueryString"; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /examples/VariantAndTelemetryDemo/Pages/_ViewImports.cshtml: -------------------------------------------------------------------------------- 1 | @using VariantAndTelemetryDemo 2 | @namespace VariantAndTelemetryDemo.Pages 3 | @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers 4 | @addTagHelper *, Microsoft.FeatureManagement.AspNetCore 5 | @inject Microsoft.ApplicationInsights.AspNetCore.JavaScriptSnippet JavaScriptSnippet 6 | -------------------------------------------------------------------------------- /examples/TargetingConsoleApp/Identity/User.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | // 4 | namespace TargetingConsoleApp.Identity 5 | { 6 | class User 7 | { 8 | public string Id { get; set; } 9 | 10 | public IEnumerable Groups { get; set; } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /examples/FeatureFlagDemo/BrowserFilterSettings.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | // 4 | namespace FeatureFlagDemo.FeatureManagement.FeatureFilters 5 | { 6 | public class BrowserFilterSettings 7 | { 8 | public IList AllowedBrowsers { get; set; } = new List(); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /tests/Tests.FeatureManagement/OnDemandClock.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Tests.FeatureManagement 4 | { 5 | class OnDemandClock : TimeProvider 6 | { 7 | public DateTimeOffset UtcNow { get; set; } 8 | 9 | public override DateTimeOffset GetUtcNow() 10 | { 11 | return UtcNow; 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /examples/FeatureFlagDemo/Models/ErrorViewModel.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | // 4 | namespace FeatureFlagDemo.Models 5 | { 6 | public class ErrorViewModel 7 | { 8 | public string RequestId { get; set; } 9 | 10 | public bool ShowRequestId => !string.IsNullOrEmpty(RequestId); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /examples/FeatureFlagDemo/Views/Home/About.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewData["Title"] = "About"; 3 | } 4 |

@ViewData["Title"]

5 |

@ViewData["Message"]

6 | 7 |

Use this area to provide additional information.

8 | 9 | 10 | 11 |

Cool stuff you can only see if Content Enhancement is enabled.

12 |
13 | -------------------------------------------------------------------------------- /examples/RazorPages/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | }, 8 | "AllowedHosts": "*", 9 | "feature_management": { 10 | "feature_flags": [ 11 | { 12 | "id": "Home", 13 | "enabled": true 14 | } 15 | ] 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /examples/BlazorServerApp/Data/WeatherForecast.cs: -------------------------------------------------------------------------------- 1 | namespace BlazorServerApp.Data 2 | { 3 | public class WeatherForecast 4 | { 5 | public DateTime Date { get; set; } 6 | 7 | public int TemperatureC { get; set; } 8 | 9 | public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); 10 | 11 | public string Summary { get; set; } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /tests/Tests.FeatureManagement/appsettings1.json: -------------------------------------------------------------------------------- 1 | { 2 | "feature_management": { 3 | "feature_flags": [ 4 | { 5 | "id": "Feature1", 6 | "enabled": true 7 | }, 8 | { 9 | "id": "Feature2", 10 | "enabled": true 11 | }, 12 | { 13 | "id": "FeatureA", 14 | "enabled": true 15 | } 16 | ] 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /tests/Tests.FeatureManagement/appsettings3.json: -------------------------------------------------------------------------------- 1 | { 2 | "feature_management": { 3 | "feature_flags": [ 4 | { 5 | "id": "Feature1", 6 | "enabled": false 7 | }, 8 | { 9 | "id": "Feature2", 10 | "enabled": false 11 | }, 12 | { 13 | "id": "FeatureC", 14 | "enabled": true 15 | } 16 | ] 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /tests/Tests.FeatureManagement/DotnetFeatureManagementSchema.json: -------------------------------------------------------------------------------- 1 | { 2 | "FeatureManagement": { 3 | "OnTestFeature": true, 4 | "OffTestFeature": false, 5 | "ConditionalFeature": { 6 | "EnabledFor": [ 7 | { 8 | "Name": "Test", 9 | "Parameters": { 10 | "P1": "V1" 11 | } 12 | } 13 | ] 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/Microsoft.FeatureManagement/IFeatureFilterMetadata.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | // 4 | namespace Microsoft.FeatureManagement 5 | { 6 | /// 7 | /// Marker interface for feature filters used to evaluate the state of a feature 8 | /// 9 | public interface IFeatureFilterMetadata 10 | { 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /tests/Tests.FeatureManagement.AspNetCore/Features.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | // 4 | namespace Tests.FeatureManagement.AspNetCore 5 | { 6 | static class Features 7 | { 8 | public const string ConditionalFeature = "ConditionalFeature"; 9 | public const string ConditionalFeature2 = "ConditionalFeature2"; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /examples/BlazorServerApp/BlazorServerApp.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | enable 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/Microsoft.FeatureManagement/IFeatureManagerSnapshot.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | // 4 | namespace Microsoft.FeatureManagement 5 | { 6 | /// 7 | /// Provides a snapshot of feature state to ensure consistency across a given request. 8 | /// 9 | public interface IFeatureManagerSnapshot : IFeatureManager 10 | { 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/Microsoft.FeatureManagement/IVariantFeatureManagerSnapshot.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | // 4 | namespace Microsoft.FeatureManagement 5 | { 6 | /// 7 | /// Provides a snapshot of feature state to ensure consistency across a given request. 8 | /// 9 | public interface IVariantFeatureManagerSnapshot : IVariantFeatureManager 10 | { 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /examples/RazorPages/RazorPages.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | enable 6 | enable 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /examples/RazorPages/Pages/Privacy.cshtml.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc.RazorPages; 2 | 3 | namespace RazorPages.Pages 4 | { 5 | public class PrivacyModel : PageModel 6 | { 7 | private readonly ILogger _logger; 8 | 9 | public PrivacyModel(ILogger logger) 10 | { 11 | _logger = logger; 12 | } 13 | 14 | public void OnGet() 15 | { 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /examples/VariantServiceDemo/Pages/Privacy.cshtml.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc.RazorPages; 2 | 3 | namespace VariantServiceDemo.Pages 4 | { 5 | public class PrivacyModel : PageModel 6 | { 7 | private readonly ILogger _logger; 8 | 9 | public PrivacyModel(ILogger logger) 10 | { 11 | _logger = logger; 12 | } 13 | 14 | public void OnGet() 15 | { 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /examples/VariantAndTelemetryDemo/wwwroot/css/site.css: -------------------------------------------------------------------------------- 1 | html { 2 | font-size: 14px; 3 | } 4 | 5 | @media (min-width: 768px) { 6 | html { 7 | font-size: 16px; 8 | } 9 | } 10 | 11 | html { 12 | position: relative; 13 | min-height: 100%; 14 | } 15 | 16 | body { 17 | margin-bottom: 60px; 18 | } 19 | 20 | img { 21 | width: 100%; 22 | } 23 | 24 | .mb-5 { 25 | margin-bottom: 5px!important; 26 | } 27 | 28 | .mb-40 { 29 | margin-bottom: 40px!important; 30 | } -------------------------------------------------------------------------------- /tests/Tests.FeatureManagement/appsettings2.json: -------------------------------------------------------------------------------- 1 | { 2 | "feature_management": { 3 | "feature_flags": [ 4 | { 5 | "id": "Feature1", 6 | "enabled": true 7 | }, 8 | { 9 | "id": "Feature2", 10 | "enabled": false 11 | }, 12 | { 13 | "id": "FeatureB", 14 | "enabled": true 15 | } 16 | ] 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /examples/FeatureFlagDemo/SuperUserFilter.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | // 4 | using Microsoft.FeatureManagement; 5 | 6 | namespace FeatureFlagDemo.FeatureManagement.FeatureFilters 7 | { 8 | public class SuperUserFilter : IFeatureFilter 9 | { 10 | public Task EvaluateAsync(FeatureFilterEvaluationContext context) 11 | { 12 | return Task.FromResult(false); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /examples/VariantServiceDemo/DefaultCalculator.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | // 4 | using Microsoft.FeatureManagement; 5 | 6 | namespace VariantServiceDemo 7 | { 8 | [VariantServiceAlias("DefaultCalculator")] 9 | public class DefaultCalculator : ICalculator 10 | { 11 | public Task AddAsync(double a, double b) 12 | { 13 | return Task.FromResult(a + b); 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /examples/ConsoleApp/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "feature_management": { 3 | "feature_flags": [ 4 | { 5 | "id": "Beta", 6 | "enabled": true, 7 | "conditions": { 8 | "client_filters": [ 9 | { 10 | "name": "AccountId", 11 | "parameters": { 12 | "AllowedAccounts": [ "abcdefghijklmnopqrstuvwxyz" ] 13 | } 14 | } 15 | ] 16 | } 17 | } 18 | ] 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Microsoft.FeatureManagement/IFeatureDefinitionProviderCacheable.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | // 4 | 5 | namespace Microsoft.FeatureManagement 6 | { 7 | /// 8 | /// An interface that marks this provider's parameters are cacheable. This was implemented to allow the provider in our test suite to be cacheable. 9 | /// 10 | internal interface IFeatureDefinitionProviderCacheable 11 | { 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /examples/BlazorServerApp/_Imports.razor: -------------------------------------------------------------------------------- 1 | @using System.Net.Http 2 | @using Microsoft.AspNetCore.Authorization 3 | @using Microsoft.AspNetCore.Components.Authorization 4 | @using Microsoft.AspNetCore.Components.Forms 5 | @using Microsoft.AspNetCore.Components.Routing 6 | @using Microsoft.AspNetCore.Components.Web 7 | @using Microsoft.AspNetCore.Components.Web.Virtualization 8 | @using Microsoft.FeatureManagement 9 | @using Microsoft.JSInterop 10 | @using BlazorServerApp 11 | @using BlazorServerApp.Shared 12 | -------------------------------------------------------------------------------- /examples/FeatureFlagDemo/Authentication/QueryStringAuthenticationOptions.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | // 4 | using Microsoft.AspNetCore.Authentication; 5 | 6 | namespace FeatureFlagDemo.Authentication 7 | { 8 | class QueryStringAuthenticationOptions : AuthenticationSchemeOptions 9 | { 10 | public string UsernameParameterName { get; set; } = "username"; 11 | 12 | public string GroupsParameterName { get; set; } = "groups"; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /examples/RazorPages/Pages/Index.cshtml.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc.RazorPages; 2 | using Microsoft.FeatureManagement.Mvc; 3 | 4 | namespace RazorPages.Pages 5 | { 6 | [FeatureGate("Home")] 7 | public class IndexModel : PageModel 8 | { 9 | private readonly ILogger _logger; 10 | 11 | public IndexModel(ILogger logger) 12 | { 13 | _logger = logger; 14 | } 15 | 16 | public void OnGet() 17 | { 18 | 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /tests/Tests.FeatureManagement/AppContext.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | // 4 | using Microsoft.FeatureManagement.FeatureFilters; 5 | using System.Collections.Generic; 6 | 7 | namespace Tests.FeatureManagement 8 | { 9 | class AppContext : IAccountContext, ITargetingContext 10 | { 11 | public string AccountId { get; set; } 12 | 13 | public string UserId { get; set; } 14 | 15 | public IEnumerable Groups { get; set; } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /examples/BlazorServerApp/Pages/Counter.razor: -------------------------------------------------------------------------------- 1 | @page "/counter" 2 | 3 | Counter 4 | 5 | 6 | 7 |

Counter

8 | 9 |

Current count: @currentCount

10 | 11 | 12 |
13 |
14 | 15 | @code { 16 | private int currentCount = 0; 17 | 18 | private void IncrementCount() 19 | { 20 | currentCount++; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /examples/FeatureFlagDemo/Views/Home/Contact.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewData["Title"] = "Contact"; 3 | } 4 |

@ViewData["Title"]

5 |

@ViewData["Message"]

6 | 7 |
8 | One Microsoft Way
9 | Redmond, WA 98052-6399
10 | P: 11 | 425.555.0100 12 |
13 | 14 |
15 | Support: Support@example.com
16 | Marketing: Marketing@example.com 17 |
18 | -------------------------------------------------------------------------------- /examples/FeatureFlagDemo/MyFeatureFlags.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | // 4 | namespace FeatureFlagDemo 5 | { 6 | // 7 | // Define feature flags in an enum 8 | static class MyFeatureFlags 9 | { 10 | public const string Home = "Home"; 11 | public const string Beta = "Beta"; 12 | public const string CustomViewData = "CustomViewData"; 13 | public const string ContentEnhancement = "ContentEnhancement"; 14 | public const string EnhancedPipeline = "EnhancedPipeline"; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /tests/Tests.FeatureManagement.AspNetCore/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Warning" 5 | } 6 | }, 7 | "AllowedHosts": "*", 8 | 9 | "FeatureManagement": { 10 | "ConditionalFeature": { 11 | "EnabledFor": [ 12 | { 13 | "Name": "Test", 14 | "Parameters": { 15 | "P1": "V1" 16 | } 17 | } 18 | ] 19 | }, 20 | "ConditionalFeature2": { 21 | "EnabledFor": [ 22 | { 23 | "Name": "Test" 24 | } 25 | ] 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /tests/Tests.FeatureManagement/OnDemandTargetingContextAccessor.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | // 4 | using Microsoft.FeatureManagement.FeatureFilters; 5 | using System.Threading.Tasks; 6 | 7 | namespace Tests.FeatureManagement 8 | { 9 | class OnDemandTargetingContextAccessor : ITargetingContextAccessor 10 | { 11 | public TargetingContext Current { get; set; } 12 | 13 | public ValueTask GetContextAsync() 14 | { 15 | return new ValueTask(Current); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /examples/BlazorServerApp/Shared/SurveyPrompt.razor: -------------------------------------------------------------------------------- 1 |
2 | 3 | @Title 4 | 5 | 6 | Please take our 7 | brief survey 8 | 9 | and tell us what you think. 10 |
11 | 12 | @code { 13 | // Demonstrates how a parent component can supply parameters 14 | [Parameter] 15 | public string Title { get; set; } 16 | } 17 | -------------------------------------------------------------------------------- /examples/FeatureFlagDemo/Authentication/QueryStringAuthenticationExtensions.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | // 4 | using Microsoft.AspNetCore.Authentication; 5 | 6 | namespace FeatureFlagDemo.Authentication 7 | { 8 | static class QueryStringAuthenticationExtensions 9 | { 10 | public static AuthenticationBuilder AddQueryString(this AuthenticationBuilder builder) 11 | { 12 | return builder.AddScheme(Schemes.QueryString, null); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /tests/Tests.FeatureManagement.AspNetCore/Pages/RazorTestAll.cshtml.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | // 4 | using Microsoft.AspNetCore.Mvc; 5 | using Microsoft.AspNetCore.Mvc.RazorPages; 6 | using Microsoft.FeatureManagement.Mvc; 7 | 8 | namespace Tests.FeatureManagement.AspNetCore.Pages 9 | { 10 | [FeatureGate(Features.ConditionalFeature, Features.ConditionalFeature2)] 11 | public class RazorTestAllModel : PageModel 12 | { 13 | public IActionResult OnGet() 14 | { 15 | return new OkResult(); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /examples/VariantServiceDemo/RemoteCalculator.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | // 4 | using Microsoft.FeatureManagement; 5 | 6 | namespace VariantServiceDemo 7 | { 8 | [VariantServiceAlias("RemoteCalculator")] 9 | public class RemoteCalculator : ICalculator 10 | { 11 | public async Task AddAsync(double a, double b) 12 | { 13 | // 14 | // simulate the latency caused by calling API from a remote server 15 | await Task.Delay(1000); 16 | 17 | return a + b; 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /tests/Tests.FeatureManagement.AspNetCore/MvcFilter.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | // 4 | using Microsoft.AspNetCore.Mvc.Filters; 5 | using System.Threading.Tasks; 6 | 7 | namespace Tests.FeatureManagement.AspNetCore 8 | { 9 | public class MvcFilter : IAsyncActionFilter 10 | { 11 | public Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) 12 | { 13 | context.HttpContext.Response.Headers[nameof(MvcFilter)] = bool.TrueString; 14 | 15 | return next(); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /examples/VariantAndTelemetryDemo/Pages/Checkout.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model CheckoutModel 3 | @{ 4 | ViewData["Title"] = "Checkout"; 5 | } 6 |

@ViewData["Title"]

7 | 8 |

Click Below To Check Out!

9 | 10 | 17 | 18 | 21 | -------------------------------------------------------------------------------- /src/Microsoft.FeatureManagement/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | // 4 | using System.Runtime.CompilerServices; 5 | 6 | // Tests 7 | [assembly: InternalsVisibleTo("Tests.FeatureManagement,PublicKey=" + 8 | "0024000004800000940000000602000000240000525341310004000001000100895524f60b44ff" + 9 | "3ae70fbea5662f61dd9d640de2205b7bd5359a43dda006e51d83d1f5f7a7d3f849267a0a28676d" + 10 | "cf49727a32487d4c75c4aacd5febb0069e1adc66ec63bbd18ec2276091a0e3c1326aa626c9e4db" + 11 | "800714a134f2a81e405f35752b55220021923429cb61776cd2fa66d25c335f8dc27bb92292905a" + 12 | "3798d896")] 13 | -------------------------------------------------------------------------------- /src/Microsoft.FeatureManagement/Targeting/TargetingEvaluationOptions.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | // 4 | namespace Microsoft.FeatureManagement.FeatureFilters 5 | { 6 | /// 7 | /// Options that apply to all uses of a target filter inside a service collection. 8 | /// 9 | public class TargetingEvaluationOptions 10 | { 11 | /// 12 | /// Used to ignore case when comparing user id and group names during targeting evaluation. 13 | /// 14 | public bool IgnoreCase { get; set; } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /tests/Tests.FeatureManagement.AspNetCore/Pages/RazorTestAllNegate.cshtml.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | // 4 | using Microsoft.AspNetCore.Mvc; 5 | using Microsoft.AspNetCore.Mvc.RazorPages; 6 | using Microsoft.FeatureManagement.Mvc; 7 | 8 | namespace Tests.FeatureManagement.AspNetCore.Pages 9 | { 10 | [FeatureGate(negate: true, Features.ConditionalFeature, Features.ConditionalFeature2)] 11 | public class RazorTestAllNegateModel : PageModel 12 | { 13 | public IActionResult OnGet() 14 | { 15 | return new OkResult(); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/Microsoft.FeatureManagement/Targeting/TargetingFilterSettings.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | // 4 | namespace Microsoft.FeatureManagement.FeatureFilters 5 | { 6 | /// 7 | /// The settings that are used to configure the feature filter. 8 | /// 9 | public class TargetingFilterSettings 10 | { 11 | /// 12 | /// The audience that a feature configured to use the should be enabled for. 13 | /// 14 | public Audience Audience { get; set; } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/Microsoft.FeatureManagement/FeatureStatus.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | // 4 | namespace Microsoft.FeatureManagement 5 | { 6 | /// 7 | /// Describes how a feature's state will be evaluated. 8 | /// 9 | public enum FeatureStatus 10 | { 11 | /// 12 | /// The state of the feature is conditional upon the feature evaluation pipeline. 13 | /// 14 | Conditional, 15 | 16 | /// 17 | /// The state of the feature is always disabled. 18 | /// 19 | Disabled 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /tests/Tests.FeatureManagement.AspNetCore/Pages/RazorTestAny.cshtml.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | // 4 | using Microsoft.AspNetCore.Mvc; 5 | using Microsoft.AspNetCore.Mvc.RazorPages; 6 | using Microsoft.FeatureManagement; 7 | using Microsoft.FeatureManagement.Mvc; 8 | 9 | namespace Tests.FeatureManagement.AspNetCore.Pages 10 | { 11 | [FeatureGate(RequirementType.Any, Features.ConditionalFeature, Features.ConditionalFeature2)] 12 | public class RazorTestAnyModel : PageModel 13 | { 14 | public IActionResult OnGet() 15 | { 16 | return new OkResult(); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/Microsoft.FeatureManagement/Targeting/ITargetingContextAccessor.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | // 4 | using System.Threading.Tasks; 5 | 6 | namespace Microsoft.FeatureManagement.FeatureFilters 7 | { 8 | /// 9 | /// Provides access to the current targeting context. 10 | /// 11 | public interface ITargetingContextAccessor 12 | { 13 | /// 14 | /// Retrieves the current targeting context. 15 | /// 16 | /// The current targeting context. 17 | ValueTask GetContextAsync(); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/Microsoft.FeatureManagement/Variant.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | // 4 | using Microsoft.Extensions.Configuration; 5 | 6 | namespace Microsoft.FeatureManagement 7 | { 8 | /// 9 | /// A variant for a feature. 10 | /// 11 | public class Variant 12 | { 13 | /// 14 | /// The name of the variant. 15 | /// 16 | public string Name { get; set; } 17 | 18 | /// 19 | /// The configuration of the variant. 20 | /// 21 | public IConfigurationSection Configuration { get; set; } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /examples/RazorPages/wwwroot/lib/jquery-validation-unobtrusive/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) .NET Foundation. All rights reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use 4 | these files except in compliance with the License. You may obtain a copy of the 5 | License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software distributed 10 | under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 11 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 12 | specific language governing permissions and limitations under the License. 13 | -------------------------------------------------------------------------------- /examples/FeatureFlagDemo/wwwroot/lib/jquery-validation-unobtrusive/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) .NET Foundation. All rights reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use 4 | these files except in compliance with the License. You may obtain a copy of the 5 | License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software distributed 10 | under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 11 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 12 | specific language governing permissions and limitations under the License. 13 | -------------------------------------------------------------------------------- /examples/VariantServiceDemo/wwwroot/lib/jquery-validation-unobtrusive/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) .NET Foundation. All rights reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use 4 | these files except in compliance with the License. You may obtain a copy of the 5 | License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software distributed 10 | under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 11 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 12 | specific language governing permissions and limitations under the License. 13 | -------------------------------------------------------------------------------- /src/Microsoft.FeatureManagement/FeatureFilters/PercentageFilterSettings.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | // 4 | namespace Microsoft.FeatureManagement.FeatureFilters 5 | { 6 | /// 7 | /// The settings that are used to configure the feature filter. 8 | /// 9 | public class PercentageFilterSettings 10 | { 11 | /// 12 | /// A value between 0 and 100 specifying the chance that a feature configured to use the should be enabled. 13 | /// 14 | public int Value { get; set; } = -1; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /examples/VariantAndTelemetryDemo/wwwroot/lib/jquery-validation-unobtrusive/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) .NET Foundation. All rights reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use 4 | these files except in compliance with the License. You may obtain a copy of the 5 | License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software distributed 10 | under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 11 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 12 | specific language governing permissions and limitations under the License. 13 | -------------------------------------------------------------------------------- /tests/Tests.FeatureManagement.AspNetCore/Pages/RazorTestAnyNegate.cshtml.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | // 4 | using Microsoft.AspNetCore.Mvc; 5 | using Microsoft.AspNetCore.Mvc.RazorPages; 6 | using Microsoft.FeatureManagement; 7 | using Microsoft.FeatureManagement.Mvc; 8 | 9 | namespace Tests.FeatureManagement.AspNetCore.Pages 10 | { 11 | [FeatureGate(requirementType: RequirementType.Any, negate: true, Features.ConditionalFeature, Features.ConditionalFeature2)] 12 | public class RazorTestAnyNegateModel : PageModel 13 | { 14 | public IActionResult OnGet() 15 | { 16 | return new OkResult(); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/Microsoft.FeatureManagement/Allocation/GroupAllocation.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | // 4 | 5 | using System.Collections.Generic; 6 | 7 | namespace Microsoft.FeatureManagement 8 | { 9 | /// 10 | /// The definition of a group allocation. 11 | /// 12 | public class GroupAllocation 13 | { 14 | /// 15 | /// The name of the variant. 16 | /// 17 | public string Variant { get; set; } 18 | 19 | /// 20 | /// A list of groups that can be assigned this variant. 21 | /// 22 | public IEnumerable Groups { get; set; } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Microsoft.FeatureManagement/Allocation/UserAllocation.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | // 4 | 5 | using System.Collections.Generic; 6 | 7 | namespace Microsoft.FeatureManagement 8 | { 9 | /// 10 | /// The definition of a user allocation. 11 | /// 12 | public class UserAllocation 13 | { 14 | /// 15 | /// The name of the variant. 16 | /// 17 | public string Variant { get; set; } 18 | 19 | /// 20 | /// A list of users that will be assigned this variant. 21 | /// 22 | public IEnumerable Users { get; set; } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Microsoft.FeatureManagement/RequirementType.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | // 4 | namespace Microsoft.FeatureManagement 5 | { 6 | /// 7 | /// Describes whether any or all conditions in a set should be required to be true. 8 | /// 9 | public enum RequirementType 10 | { 11 | /// 12 | /// The set of conditions will be evaluated as true if any condition in the set is true. 13 | /// 14 | Any, 15 | /// 16 | /// The set of conditions will be evaluated as true if all the conditions in the set are true. 17 | /// 18 | All 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Microsoft.FeatureManagement/DotnetFeatureManagementFields.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | // 4 | 5 | namespace Microsoft.FeatureManagement 6 | { 7 | internal static class DotnetFeatureManagementFields 8 | { 9 | public const string RequirementType = "RequirementType"; 10 | 11 | // Feature filters keywords 12 | public const string FeatureFiltersSectionName = "EnabledFor"; 13 | public const string FeatureFilterConfigurationParameters = "Parameters"; 14 | 15 | // Other keywords 16 | public const string NameKeyword = "Name"; 17 | public const string FeatureManagementSectionName = "FeatureManagement"; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /examples/VariantServiceDemo/VariantServiceDemo.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | enable 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /examples/RazorPages/Pages/Index.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model IndexModel 3 | @{ 4 | ViewData["Title"] = "Home page"; 5 | } 6 | 7 | 8 | 13 | 14 | 15 |

16 | Razor Page Example 17 |

18 |

19 | This page uses the 'PageGateAttribute' to conditionally display this web page. 20 | Since you are seeing this, that means the 'Home' feature is enabled. 21 | If the 'Home' feature is disabled then the browser will display an HTTP 404 (Not Found) error page. 22 |

23 | -------------------------------------------------------------------------------- /tests/Tests.FeatureManagement/ContextualTestFilter.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | // 4 | using Microsoft.FeatureManagement; 5 | using System; 6 | using System.Threading.Tasks; 7 | 8 | namespace Tests.FeatureManagement 9 | { 10 | class ContextualTestFilter : IContextualFeatureFilter 11 | { 12 | public Func ContextualCallback { get; set; } 13 | 14 | public Task EvaluateAsync(FeatureFilterEvaluationContext context, IAccountContext accountContext) 15 | { 16 | return Task.FromResult(ContextualCallback?.Invoke(context, accountContext) ?? false); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /examples/VariantAndTelemetryDemo/VariantAndTelemetryDemo.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | enable 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /examples/FeatureFlagDemo/Controllers/BetaController.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | // 4 | using Microsoft.AspNetCore.Mvc; 5 | using Microsoft.FeatureManagement; 6 | using Microsoft.FeatureManagement.Mvc; 7 | 8 | namespace FeatureFlagDemo.Controllers 9 | { 10 | public class BetaController : Controller 11 | { 12 | private readonly IFeatureManager _featureManager; 13 | 14 | public BetaController(IFeatureManagerSnapshot featureManager) 15 | { 16 | _featureManager = featureManager; 17 | } 18 | 19 | [FeatureGate(MyFeatureFlags.Beta)] 20 | public IActionResult Index() 21 | { 22 | return View(); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/Microsoft.FeatureManagement/OnDemandConfigurationProvider.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Configuration; 2 | using System.Collections.Generic; 3 | using System.Reflection; 4 | 5 | namespace Microsoft.FeatureManagement 6 | { 7 | internal class OnDemandConfigurationProvider : ConfigurationProvider 8 | { 9 | private static readonly PropertyInfo _DataProperty = typeof(ConfigurationProvider).GetProperty(nameof(Data), BindingFlags.NonPublic | BindingFlags.Instance); 10 | 11 | public OnDemandConfigurationProvider(ConfigurationProvider configurationProvider) 12 | { 13 | var data = _DataProperty.GetValue(configurationProvider) as IDictionary; 14 | 15 | Data = data; 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /examples/RazorPages/Program.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.FeatureManagement; 2 | 3 | var builder = WebApplication.CreateBuilder(args); 4 | 5 | // Add services to the container. 6 | builder.Services.AddRazorPages(); 7 | builder.Services.AddFeatureManagement(); 8 | 9 | var app = builder.Build(); 10 | 11 | // Configure the HTTP request pipeline. 12 | if (!app.Environment.IsDevelopment()) 13 | { 14 | app.UseExceptionHandler("/Error"); 15 | // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. 16 | app.UseHsts(); 17 | } 18 | 19 | app.UseHttpsRedirection(); 20 | app.UseStaticFiles(); 21 | 22 | app.UseRouting(); 23 | 24 | app.UseAuthorization(); 25 | 26 | app.MapRazorPages(); 27 | 28 | app.Run(); 29 | -------------------------------------------------------------------------------- /src/Microsoft.FeatureManagement/Targeting/BasicAudience.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | // 4 | using System.Collections.Generic; 5 | 6 | namespace Microsoft.FeatureManagement.FeatureFilters 7 | { 8 | /// 9 | /// A basic audience definition describing a set of users and groups. 10 | /// 11 | public class BasicAudience 12 | { 13 | /// 14 | /// Includes users in the audience by name. 15 | /// 16 | public List Users { get; set; } 17 | 18 | /// 19 | /// Includes users in the audience by group name. 20 | /// 21 | public List Groups { get; set; } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Microsoft.FeatureManagement/Targeting/GroupRollout.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | // 4 | namespace Microsoft.FeatureManagement.FeatureFilters 5 | { 6 | /// 7 | /// Defines a percentage of a group to be included in a rollout. 8 | /// 9 | public class GroupRollout 10 | { 11 | /// 12 | /// The name of the group. 13 | /// 14 | public string Name { get; set; } 15 | 16 | /// 17 | /// The percentage of the group that should be considered part of the rollout. Valid values range from 0 to 100 inclusive. 18 | /// 19 | public double RolloutPercentage { get; set; } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/Microsoft.FeatureManagement/FeatureFilters/Recurrence/Recurrence.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | // 4 | namespace Microsoft.FeatureManagement.FeatureFilters 5 | { 6 | /// 7 | /// A recurrence definition describing how time window recurs 8 | /// 9 | public class Recurrence 10 | { 11 | /// 12 | /// The recurrence pattern specifying how often the time window repeats 13 | /// 14 | public RecurrencePattern Pattern { get; set; } 15 | 16 | /// 17 | /// The recurrence range specifying how long the recurrence pattern repeats 18 | /// 19 | public RecurrenceRange Range { get; set; } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/Microsoft.FeatureManagement/StatusOverride.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | // 4 | namespace Microsoft.FeatureManagement 5 | { 6 | /// 7 | /// Provides the capability to override whether a feature is considered enabled or disabled when a variant is assigned. 8 | /// 9 | public enum StatusOverride 10 | { 11 | /// 12 | /// Does not affect the feature state. 13 | /// 14 | None, 15 | 16 | /// 17 | /// The feature will be considered enabled. 18 | /// 19 | Enabled, 20 | 21 | /// 22 | /// The feature will be considered disabled. 23 | /// 24 | Disabled 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /examples/BlazorServerApp/Data/WeatherForecastService.cs: -------------------------------------------------------------------------------- 1 | namespace BlazorServerApp.Data 2 | { 3 | public class WeatherForecastService 4 | { 5 | private static readonly string[] Summaries = new[] 6 | { 7 | "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" 8 | }; 9 | 10 | public Task GetForecastAsync(DateTime startDate) 11 | { 12 | return Task.FromResult(Enumerable.Range(1, 5).Select(index => new WeatherForecast 13 | { 14 | Date = startDate.AddDays(index), 15 | TemperatureC = Random.Shared.Next(-20, 55), 16 | Summary = Summaries[Random.Shared.Next(Summaries.Length)] 17 | }).ToArray()); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Microsoft.FeatureManagement/TelemetryConfiguration.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.FeatureManagement.Telemetry; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | 5 | namespace Microsoft.FeatureManagement 6 | { 7 | /// 8 | /// Defines telemetry related configuration settings available for features. 9 | /// 10 | public class TelemetryConfiguration 11 | { 12 | /// 13 | /// A flag to enable or disable sending events as s. 14 | /// 15 | public bool Enabled { get; set; } 16 | 17 | /// 18 | /// A container for metadata relevant to telemetry. 19 | /// 20 | public IReadOnlyDictionary Metadata { get; set; } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/Microsoft.FeatureManagement/IVariantServiceProvider.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | // 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | 7 | namespace Microsoft.FeatureManagement 8 | { 9 | /// 10 | /// Used to get different implementation variants of TService. 11 | /// 12 | public interface IVariantServiceProvider where TService : class 13 | { 14 | /// 15 | /// Gets an implementation variant of TService. 16 | /// 17 | /// The cancellation token to cancel the operation. 18 | /// An implementation of TService. 19 | ValueTask GetServiceAsync(CancellationToken cancellationToken); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /examples/RazorPages/Pages/Error.cshtml.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using Microsoft.AspNetCore.Mvc.RazorPages; 3 | using System.Diagnostics; 4 | 5 | namespace RazorPages.Pages 6 | { 7 | [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] 8 | [IgnoreAntiforgeryToken] 9 | public class ErrorModel : PageModel 10 | { 11 | public string? RequestId { get; set; } 12 | 13 | public bool ShowRequestId => !string.IsNullOrEmpty(RequestId); 14 | 15 | private readonly ILogger _logger; 16 | 17 | public ErrorModel(ILogger logger) 18 | { 19 | _logger = logger; 20 | } 21 | 22 | public void OnGet() 23 | { 24 | RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier; 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /examples/BlazorServerApp/Shared/MainLayout.razor: -------------------------------------------------------------------------------- 1 | @inherits LayoutComponentBase 2 | 3 | BlazorServerApp 4 | 5 |
6 | 9 | 10 |
11 |
12 | About 13 |
14 | 15 |
16 | @Body 17 |
18 |
19 |
20 | 21 | @inject IFeatureManagerSnapshot _featureManager 22 | 23 | @code { 24 | private bool isEnhancementEnabled; 25 | 26 | protected override async Task OnInitializedAsync() 27 | { 28 | isEnhancementEnabled = await _featureManager.IsEnabledAsync("BrowserEnhancement"); 29 | } 30 | } -------------------------------------------------------------------------------- /examples/BlazorServerApp/Pages/Error.cshtml.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using Microsoft.AspNetCore.Mvc.RazorPages; 3 | using System.Diagnostics; 4 | 5 | namespace BlazorServerApp.Pages 6 | { 7 | [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] 8 | [IgnoreAntiforgeryToken] 9 | public class ErrorModel : PageModel 10 | { 11 | public string RequestId { get; set; } 12 | 13 | public bool ShowRequestId => !string.IsNullOrEmpty(RequestId); 14 | 15 | private readonly ILogger _logger; 16 | 17 | public ErrorModel(ILogger logger) 18 | { 19 | _logger = logger; 20 | } 21 | 22 | public void OnGet() 23 | { 24 | RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier; 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Microsoft.FeatureManagement/Targeting/ITargetingContext.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | // 4 | using System.Collections.Generic; 5 | 6 | namespace Microsoft.FeatureManagement.FeatureFilters 7 | { 8 | /// 9 | /// Contextual information that is required to perform a targeting evaluation. 10 | /// 11 | public interface ITargetingContext 12 | { 13 | /// 14 | /// The user id that should be considered when evaluating if the context is being targeted. 15 | /// 16 | string UserId { get; set; } 17 | 18 | /// 19 | /// The groups that should be considered when evaluating if the context is being targeted. 20 | /// 21 | IEnumerable Groups { get; set; } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /examples/VariantServiceDemo/Pages/Error.cshtml.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using Microsoft.AspNetCore.Mvc.RazorPages; 3 | using System.Diagnostics; 4 | 5 | namespace VariantServiceDemo.Pages 6 | { 7 | [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] 8 | [IgnoreAntiforgeryToken] 9 | public class ErrorModel : PageModel 10 | { 11 | public string RequestId { get; set; } 12 | 13 | public bool ShowRequestId => !string.IsNullOrEmpty(RequestId); 14 | 15 | private readonly ILogger _logger; 16 | 17 | public ErrorModel(ILogger logger) 18 | { 19 | _logger = logger; 20 | } 21 | 22 | public void OnGet() 23 | { 24 | RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier; 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Microsoft.FeatureManagement/FeatureFilterConfiguration.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | // 4 | using Microsoft.Extensions.Configuration; 5 | using System.Collections.Generic; 6 | 7 | namespace Microsoft.FeatureManagement 8 | { 9 | /// 10 | /// The configuration of a feature filter. 11 | /// 12 | public class FeatureFilterConfiguration 13 | { 14 | /// 15 | /// The name of the feature filter. 16 | /// 17 | public string Name { get; set; } 18 | 19 | /// 20 | /// Configurable parameters that can change across instances of a feature filter. 21 | /// 22 | public IConfiguration Parameters { get; set; } = new ConfigurationRoot(new List()); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /examples/FeatureFlagDemo/Views/Shared/Error.cshtml.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using Microsoft.AspNetCore.Mvc.RazorPages; 3 | using System.Diagnostics; 4 | 5 | namespace FeatureFlagDemo.Pages.Shared 6 | { 7 | [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] 8 | [IgnoreAntiforgeryToken] 9 | public class ErrorModel : PageModel 10 | { 11 | public string RequestId { get; set; } 12 | 13 | public bool ShowRequestId => !string.IsNullOrEmpty(RequestId); 14 | 15 | private readonly ILogger _logger; 16 | 17 | public ErrorModel(ILogger logger) 18 | { 19 | _logger = logger; 20 | } 21 | 22 | public void OnGet() 23 | { 24 | RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier; 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /examples/VariantAndTelemetryDemo/Pages/Error.cshtml.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using Microsoft.AspNetCore.Mvc.RazorPages; 3 | using System.Diagnostics; 4 | 5 | namespace VariantAndTelemetryDemo.Pages 6 | { 7 | [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] 8 | [IgnoreAntiforgeryToken] 9 | public class ErrorModel : PageModel 10 | { 11 | public string RequestId { get; set; } 12 | 13 | public bool ShowRequestId => !string.IsNullOrEmpty(RequestId); 14 | 15 | private readonly ILogger _logger; 16 | 17 | public ErrorModel(ILogger logger) 18 | { 19 | _logger = logger; 20 | } 21 | 22 | public void OnGet() 23 | { 24 | RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier; 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Microsoft.FeatureManagement.AspNetCore/DisabledFeatureFilter.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | // 4 | using Microsoft.AspNetCore.Mvc.Filters; 5 | 6 | namespace Microsoft.FeatureManagement 7 | { 8 | /// 9 | /// An MVC filter that will run in place of any filter that requires a feature that is disabled to be enabled. 10 | /// 11 | class DisabledFeatureFilter : IActionFilter 12 | { 13 | public DisabledFeatureFilter(string featureName) 14 | { 15 | FeatureName = featureName; 16 | } 17 | 18 | public string FeatureName { get; } 19 | 20 | public void OnActionExecuted(ActionExecutedContext context) 21 | { 22 | } 23 | 24 | public void OnActionExecuting(ActionExecutingContext context) 25 | { 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Microsoft.FeatureManagement/Targeting/TargetingContext.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | // 4 | using System.Collections.Generic; 5 | 6 | namespace Microsoft.FeatureManagement.FeatureFilters 7 | { 8 | /// 9 | /// Contextual information that is required to perform a targeting evaluation. 10 | /// 11 | public class TargetingContext : ITargetingContext 12 | { 13 | /// 14 | /// The user id that should be considered when evaluating if the context is being targeted. 15 | /// 16 | public string UserId { get; set; } 17 | 18 | /// 19 | /// The groups that should be considered when evaluating if the context is being targeted. 20 | /// 21 | public IEnumerable Groups { get; set; } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /examples/ConsoleApp/ConsoleApp.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net8.0 6 | enable 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | Always 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /src/Microsoft.FeatureManagement/FeatureFilters/Recurrence/RecurrencePatternType.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | // 4 | namespace Microsoft.FeatureManagement.FeatureFilters 5 | { 6 | /// 7 | /// The type of specifying the frequency by which the time window repeats. 8 | /// 9 | public enum RecurrencePatternType 10 | { 11 | /// 12 | /// The pattern where the time window will repeat based on the number of days specified by interval between occurrences. 13 | /// 14 | Daily, 15 | 16 | /// 17 | /// The pattern where the time window will repeat on the same day or days of the week, based on the number of weeks between each set of occurrences. 18 | /// 19 | Weekly 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /examples/FeatureFlagDemo/wwwroot/css/site.css: -------------------------------------------------------------------------------- 1 | /* Please see documentation at https://docs.microsoft.com/aspnet/core/client-side/bundling-and-minification\ 2 | for details on configuring this project to bundle and minify static web assets. */ 3 | body { 4 | padding-bottom: 20px; 5 | } 6 | 7 | /* Wrapping element */ 8 | /* Set some basic padding to keep content from hitting the edges */ 9 | .body-content { 10 | padding-left: 15px; 11 | padding-right: 15px; 12 | } 13 | 14 | /* Carousel */ 15 | .carousel-caption p { 16 | font-size: 20px; 17 | line-height: 1.4; 18 | } 19 | 20 | /* QR code generator */ 21 | #qrCode { 22 | margin: 15px; 23 | } 24 | 25 | /* Hide/rearrange for smaller screens */ 26 | @media screen and (max-width: 767px) { 27 | /* Hide captions */ 28 | .carousel-caption { 29 | display: none; 30 | } 31 | } 32 | 33 | h2 { 34 | margin-top: 20px; 35 | } 36 | -------------------------------------------------------------------------------- /examples/FeatureFlagDemo/ThirdPartyActionFilter.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | // 4 | using Microsoft.AspNetCore.Mvc.Filters; 5 | 6 | namespace FeatureFlagDemo 7 | { 8 | public class ThirdPartyActionFilter : IAsyncActionFilter 9 | { 10 | private ILogger _logger; 11 | 12 | public ThirdPartyActionFilter(ILoggerFactory loggerFactory) 13 | { 14 | _logger = loggerFactory.CreateLogger(); 15 | } 16 | 17 | public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) 18 | { 19 | _logger.LogInformation("Third party action filter inward path."); 20 | 21 | await next().ConfigureAwait(false); 22 | 23 | _logger.LogInformation("Third party action filter outward path."); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /examples/TargetingConsoleApp/TargetingConsoleApp.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net8.0 6 | enable 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | Always 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /src/Microsoft.FeatureManagement/Allocation/PercentileAllocation.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | // 4 | 5 | namespace Microsoft.FeatureManagement 6 | { 7 | /// 8 | /// The definition of a percentile allocation. 9 | /// 10 | public class PercentileAllocation 11 | { 12 | /// 13 | /// The name of the variant. 14 | /// 15 | public string Variant { get; set; } 16 | 17 | /// 18 | /// The inclusive lower bound of the percentage to which the variant will be assigned. 19 | /// 20 | public double From { get; set; } 21 | 22 | /// 23 | /// The exclusive upper bound of the percentage to which the variant will be assigned. 24 | /// 25 | public double To { get; set; } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Microsoft.FeatureManagement/Utils/RandomGenerator.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | // 4 | using System; 5 | using System.Threading; 6 | 7 | namespace Microsoft.FeatureManagement.Utils 8 | { 9 | static class RandomGenerator 10 | { 11 | private static Random _global = new Random(); 12 | 13 | private static ThreadLocal _rnd = new ThreadLocal(() => 14 | { 15 | int seed; 16 | 17 | lock (_global) 18 | { 19 | seed = _global.Next(); 20 | } 21 | 22 | return new Random(seed); 23 | }); 24 | 25 | public static int Next() 26 | { 27 | return _rnd.Value.Next(); 28 | } 29 | 30 | public static double NextDouble() 31 | { 32 | return _rnd.Value.NextDouble(); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /tests/Tests.FeatureManagement/VariantServices.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.FeatureManagement; 2 | 3 | namespace Tests.FeatureManagement 4 | { 5 | interface IAlgorithm 6 | { 7 | public string Style { get; } 8 | } 9 | 10 | class AlgorithmBeta : IAlgorithm 11 | { 12 | public string Style { get; set; } 13 | 14 | public AlgorithmBeta() 15 | { 16 | Style = "Beta"; 17 | } 18 | } 19 | 20 | class AlgorithmSigma : IAlgorithm 21 | { 22 | public string Style { get; set; } 23 | 24 | public AlgorithmSigma() 25 | { 26 | Style = "Sigma"; 27 | } 28 | } 29 | 30 | [VariantServiceAlias("Omega")] 31 | class AlgorithmOmega : IAlgorithm 32 | { 33 | public string Style { get; set; } 34 | 35 | public AlgorithmOmega(string style) 36 | { 37 | Style = style; 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/Microsoft.FeatureManagement.AspNetCore/NotFoundDisabledFeatureHandler.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | // 4 | using Microsoft.AspNetCore.Http; 5 | using Microsoft.AspNetCore.Mvc; 6 | using Microsoft.AspNetCore.Mvc.Filters; 7 | using System.Collections.Generic; 8 | using System.Threading.Tasks; 9 | 10 | namespace Microsoft.FeatureManagement.Mvc 11 | { 12 | /// 13 | /// A default disabled feature handler that performs a minimal amount of work for disabled feature requests. 14 | /// 15 | class NotFoundDisabledFeaturesHandler : IDisabledFeaturesHandler 16 | { 17 | public Task HandleDisabledFeatures(IEnumerable features, ActionExecutingContext context) 18 | { 19 | context.Result = new StatusCodeResult(StatusCodes.Status404NotFound); 20 | 21 | return Task.CompletedTask; 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /examples/BlazorServerApp/App.razor: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Not found 9 | 10 |

Sorry, there's nothing at this address.

11 |
12 |
13 |
14 |
15 | 16 | @inject UserAgentContext UserAgentContext 17 | 18 | @code { 19 | [Parameter] 20 | public string UserAgent { get; set; } 21 | 22 | protected override Task OnInitializedAsync() 23 | { 24 | UserAgentContext.UserAgent = UserAgent; 25 | 26 | return base.OnInitializedAsync(); 27 | } 28 | } -------------------------------------------------------------------------------- /examples/RazorPages/Pages/Error.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model ErrorModel 3 | @{ 4 | ViewData["Title"] = "Error"; 5 | } 6 | 7 |

Error.

8 |

An error occurred while processing your request.

9 | 10 | @if (Model.ShowRequestId) 11 | { 12 |

13 | Request ID: @Model.RequestId 14 |

15 | } 16 | 17 |

Development Mode

18 |

19 | Swapping to the Development environment displays detailed information about the error that occurred. 20 |

21 |

22 | The Development environment shouldn't be enabled for deployed applications. 23 | It can result in displaying sensitive information from exceptions to end users. 24 | For local debugging, enable the Development environment by setting the ASPNETCORE_ENVIRONMENT environment variable to Development 25 | and restarting the app. 26 |

27 | -------------------------------------------------------------------------------- /src/Microsoft.FeatureManagement/VariantDefinition.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | // 4 | 5 | using Microsoft.Extensions.Configuration; 6 | 7 | namespace Microsoft.FeatureManagement 8 | { 9 | /// 10 | /// The definition for a variant of a feature. 11 | /// 12 | public class VariantDefinition 13 | { 14 | /// 15 | /// The name of the variant. 16 | /// 17 | public string Name { get; set; } 18 | 19 | /// 20 | /// The value of the configuration for this variant of the feature. 21 | /// 22 | public IConfigurationSection ConfigurationValue { get; set; } 23 | 24 | /// 25 | /// Overrides the state of the feature if this variant has been assigned. 26 | /// 27 | public StatusOverride StatusOverride { get; set; } = StatusOverride.None; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /examples/VariantServiceDemo/Pages/Error.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model ErrorModel 3 | @{ 4 | ViewData["Title"] = "Error"; 5 | } 6 | 7 |

Error.

8 |

An error occurred while processing your request.

9 | 10 | @if (Model.ShowRequestId) 11 | { 12 |

13 | Request ID: @Model.RequestId 14 |

15 | } 16 | 17 |

Development Mode

18 |

19 | Swapping to the Development environment displays detailed information about the error that occurred. 20 |

21 |

22 | The Development environment shouldn't be enabled for deployed applications. 23 | It can result in displaying sensitive information from exceptions to end users. 24 | For local debugging, enable the Development environment by setting the ASPNETCORE_ENVIRONMENT environment variable to Development 25 | and restarting the app. 26 |

27 | -------------------------------------------------------------------------------- /examples/FeatureFlagDemo/Views/Shared/Error.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model ErrorModel 3 | @{ 4 | ViewData["Title"] = "Error"; 5 | } 6 | 7 |

Error.

8 |

An error occurred while processing your request.

9 | 10 | @if (Model.ShowRequestId) 11 | { 12 |

13 | Request ID: @Model.RequestId 14 |

15 | } 16 | 17 |

Development Mode

18 |

19 | Swapping to the Development environment displays detailed information about the error that occurred. 20 |

21 |

22 | The Development environment shouldn't be enabled for deployed applications. 23 | It can result in displaying sensitive information from exceptions to end users. 24 | For local debugging, enable the Development environment by setting the ASPNETCORE_ENVIRONMENT environment variable to Development 25 | and restarting the app. 26 |

27 | -------------------------------------------------------------------------------- /examples/VariantAndTelemetryDemo/Pages/Error.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model ErrorModel 3 | @{ 4 | ViewData["Title"] = "Error"; 5 | } 6 | 7 |

Error.

8 |

An error occurred while processing your request.

9 | 10 | @if (Model.ShowRequestId) 11 | { 12 |

13 | Request ID: @Model.RequestId 14 |

15 | } 16 | 17 |

Development Mode

18 |

19 | Swapping to the Development environment displays detailed information about the error that occurred. 20 |

21 |

22 | The Development environment shouldn't be enabled for deployed applications. 23 | It can result in displaying sensitive information from exceptions to end users. 24 | For local debugging, enable the Development environment by setting the ASPNETCORE_ENVIRONMENT environment variable to Development 25 | and restarting the app. 26 |

27 | -------------------------------------------------------------------------------- /src/Microsoft.FeatureManagement/FeatureFilters/Recurrence/RecurrenceRange.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | // 4 | using System; 5 | 6 | namespace Microsoft.FeatureManagement.FeatureFilters 7 | { 8 | /// 9 | /// The recurrence range describes a date range over which the time window repeats. 10 | /// 11 | public class RecurrenceRange 12 | { 13 | /// 14 | /// The recurrence range type. 15 | /// 16 | public RecurrenceRangeType Type { get; set; } 17 | 18 | /// 19 | /// The date to stop applying the recurrence pattern. 20 | /// 21 | public DateTimeOffset EndDate { get; set; } = DateTimeOffset.MaxValue; 22 | 23 | /// 24 | /// The number of times to repeat the time window. 25 | /// 26 | public int NumberOfOccurrences { get; set; } = int.MaxValue; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /examples/VariantAndTelemetryDemo/Pages/RandomizeUser.cshtml.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Authentication; 2 | using Microsoft.AspNetCore.Mvc; 3 | using Microsoft.AspNetCore.Mvc.RazorPages; 4 | using System.Security.Claims; 5 | 6 | namespace VariantAndTelemetryDemo.Pages 7 | { 8 | public class RandomizeUserModel : PageModel 9 | { 10 | public IActionResult OnGet() 11 | { 12 | // Clear Application Insights cookies and 13 | Response.Cookies.Delete("ai_user"); 14 | Response.Cookies.Delete("ai_session"); 15 | 16 | // Generate new user claim 17 | var claims = new List 18 | { 19 | new Claim(ClaimTypes.Name, Random.Shared.Next().ToString()) 20 | }; 21 | 22 | var identity = new ClaimsIdentity(claims, "CookieAuth"); 23 | var principal = new ClaimsPrincipal(identity); 24 | 25 | HttpContext.SignInAsync("CookieAuth", principal); 26 | 27 | return RedirectToPage("/Index"); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /examples/FeatureFlagDemo/ThirdPartyMiddleware.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | // 4 | namespace FeatureFlagDemo 5 | { 6 | public class ThirdPartyMiddleware 7 | { 8 | // 9 | // The middleware delegate to call after this one finishes processing 10 | private readonly RequestDelegate _next; 11 | private readonly ILogger _logger; 12 | 13 | public ThirdPartyMiddleware(RequestDelegate next, ILoggerFactory loggerFactory) 14 | { 15 | _next = next; 16 | _logger = loggerFactory.CreateLogger(); 17 | } 18 | 19 | public async Task Invoke(HttpContext httpContext) 20 | { 21 | _logger.LogInformation($"Third party middleware inward path."); 22 | 23 | // 24 | // Call the next middleware delegate in the pipeline 25 | await _next.Invoke(httpContext); 26 | 27 | _logger.LogInformation($"Third party middleware outward path."); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Microsoft.FeatureManagement.AspNetCore/IDisabledFeatureHandler.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | // 4 | using Microsoft.AspNetCore.Mvc.Filters; 5 | using System.Collections.Generic; 6 | using System.Threading.Tasks; 7 | 8 | namespace Microsoft.FeatureManagement.Mvc 9 | { 10 | /// 11 | /// A handler that is invoked when an MVC action requires a feature and the feature is not enabled. 12 | /// 13 | public interface IDisabledFeaturesHandler 14 | { 15 | /// 16 | /// Callback used to handle requests to an MVC action that require a feature that is disabled. 17 | /// 18 | /// The name of the features that the action could have been activated for. 19 | /// The action executing context provided by MVC. 20 | /// The task. 21 | Task HandleDisabledFeatures(IEnumerable features, ActionExecutingContext context); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Microsoft.FeatureManagement/IFilterParametersBinder.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | // 4 | using Microsoft.Extensions.Configuration; 5 | 6 | namespace Microsoft.FeatureManagement 7 | { 8 | /// 9 | /// An interface used by the feature management system to pre-bind feature filter parameters to a settings type. 10 | /// s can implement this interface to take advantage of caching of settings by the feature management system. 11 | /// 12 | public interface IFilterParametersBinder 13 | { 14 | /// 15 | /// Binds a set of feature filter parameters to a settings object. 16 | /// 17 | /// The configuration representing filter parameters to bind to a settings object 18 | /// A settings object that is understood by the implementer of . 19 | object BindParameters(IConfiguration parameters); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /tests/Tests.FeatureManagement/TestFilter.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | // 4 | using Microsoft.Extensions.Configuration; 5 | using Microsoft.FeatureManagement; 6 | using System; 7 | using System.Threading.Tasks; 8 | 9 | namespace Tests.FeatureManagement 10 | { 11 | class TestFilter : IFeatureFilter, IFilterParametersBinder 12 | { 13 | public Func ParametersBinderCallback { get; set; } 14 | 15 | public Func> Callback { get; set; } 16 | 17 | public object BindParameters(IConfiguration parameters) 18 | { 19 | if (ParametersBinderCallback != null) 20 | { 21 | return ParametersBinderCallback(parameters); 22 | } 23 | 24 | return parameters; 25 | } 26 | 27 | public Task EvaluateAsync(FeatureFilterEvaluationContext context) 28 | { 29 | return Callback?.Invoke(context) ?? Task.FromResult(false); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /tests/Tests.FeatureManagement.AspNetCore/TestFilter.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | // 4 | using Microsoft.Extensions.Configuration; 5 | using Microsoft.FeatureManagement; 6 | using System; 7 | using System.Threading.Tasks; 8 | 9 | namespace Tests.FeatureManagement.AspNetCore 10 | { 11 | class TestFilter : IFeatureFilter, IFilterParametersBinder 12 | { 13 | public Func ParametersBinderCallback { get; set; } 14 | 15 | public Func> Callback { get; set; } 16 | 17 | public object BindParameters(IConfiguration parameters) 18 | { 19 | if (ParametersBinderCallback != null) 20 | { 21 | return ParametersBinderCallback(parameters); 22 | } 23 | 24 | return parameters; 25 | } 26 | 27 | public Task EvaluateAsync(FeatureFilterEvaluationContext context) 28 | { 29 | return Callback?.Invoke(context) ?? Task.FromResult(false); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/Microsoft.FeatureManagement/IFeatureDefinitionProvider.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | // 4 | using System.Collections.Generic; 5 | using System.Threading.Tasks; 6 | 7 | namespace Microsoft.FeatureManagement 8 | { 9 | /// 10 | /// A provider of feature definitions. 11 | /// 12 | public interface IFeatureDefinitionProvider 13 | { 14 | /// 15 | /// Retrieves the definition for a given feature. 16 | /// 17 | /// The name of the feature to retrieve the definition for. 18 | /// The feature's definition. 19 | Task GetFeatureDefinitionAsync(string featureName); 20 | 21 | /// 22 | /// Retrieves definitions for all features. 23 | /// 24 | /// An enumerator which provides asynchronous iteration over feature definitions. 25 | IAsyncEnumerable GetAllFeatureDefinitionsAsync(); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /examples/FeatureFlagDemo/FeatureNotEnabledDisabledHandler.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | // 4 | using Microsoft.AspNetCore.Mvc; 5 | using Microsoft.AspNetCore.Mvc.Filters; 6 | using Microsoft.AspNetCore.Mvc.ModelBinding; 7 | using Microsoft.AspNetCore.Mvc.ViewFeatures; 8 | using Microsoft.FeatureManagement.Mvc; 9 | 10 | namespace FeatureFlagDemo.FeatureManagement 11 | { 12 | public class FeatureNotEnabledDisabledHandler : IDisabledFeaturesHandler 13 | { 14 | public Task HandleDisabledFeatures(IEnumerable features, ActionExecutingContext context) 15 | { 16 | var result = new ViewResult() 17 | { 18 | ViewName = "Views/Shared/FeatureNotEnabled.cshtml", 19 | ViewData = new ViewDataDictionary(new EmptyModelMetadataProvider(), new ModelStateDictionary()) 20 | }; 21 | 22 | result.ViewData["FeatureName"] = string.Join(", ", features); 23 | 24 | context.Result = result; 25 | 26 | return Task.CompletedTask; 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /examples/VariantServiceDemo/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | }, 8 | "AllowedHosts": "*", 9 | "ApplicationInsights": { 10 | "ConnectionString": "" 11 | }, 12 | "feature_management": { 13 | "feature_flags": [ 14 | { 15 | "id": "Calculator", 16 | "enabled": true, 17 | "telemetry": { 18 | "enabled": true 19 | }, 20 | "variants": [ 21 | { 22 | "name": "DefaultCalculator" 23 | }, 24 | { 25 | "name": "RemoteCalculator" 26 | } 27 | ], 28 | "allocation": { 29 | "percentile": [ 30 | { 31 | "variant": "DefaultCalculator", 32 | "from": 0, 33 | "to": 50 34 | }, 35 | { 36 | "variant": "RemoteCalculator", 37 | "from": 50, 38 | "to": 100 39 | } 40 | ] 41 | } 42 | } 43 | ] 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/Microsoft.FeatureManagement.AspNetCore/InlineDisabledFeatureHandler.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | // 4 | using Microsoft.AspNetCore.Mvc.Filters; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Threading.Tasks; 8 | 9 | namespace Microsoft.FeatureManagement.Mvc 10 | { 11 | /// 12 | /// A disabled feature handler that wraps an inline handler. 13 | /// 14 | class InlineDisabledFeaturesHandler : IDisabledFeaturesHandler 15 | { 16 | private readonly Action, ActionExecutingContext> _handler; 17 | 18 | public InlineDisabledFeaturesHandler(Action, ActionExecutingContext> handler) 19 | { 20 | _handler = handler ?? throw new ArgumentNullException(nameof(handler)); 21 | } 22 | 23 | public Task HandleDisabledFeatures(IEnumerable features, ActionExecutingContext context) 24 | { 25 | _handler(features, context); 26 | 27 | return Task.CompletedTask; 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /examples/RazorPages/Pages/Shared/_Layout.cshtml.css: -------------------------------------------------------------------------------- 1 | /* Please see documentation at https://docs.microsoft.com/aspnet/core/client-side/bundling-and-minification 2 | for details on configuring this project to bundle and minify static web assets. */ 3 | 4 | a.navbar-brand { 5 | white-space: normal; 6 | text-align: center; 7 | word-break: break-all; 8 | } 9 | 10 | a { 11 | color: #0077cc; 12 | } 13 | 14 | .btn-primary { 15 | color: #fff; 16 | background-color: #1b6ec2; 17 | border-color: #1861ac; 18 | } 19 | 20 | .nav-pills .nav-link.active, .nav-pills .show > .nav-link { 21 | color: #fff; 22 | background-color: #1b6ec2; 23 | border-color: #1861ac; 24 | } 25 | 26 | .border-top { 27 | border-top: 1px solid #e5e5e5; 28 | } 29 | .border-bottom { 30 | border-bottom: 1px solid #e5e5e5; 31 | } 32 | 33 | .box-shadow { 34 | box-shadow: 0 .25rem .75rem rgba(0, 0, 0, .05); 35 | } 36 | 37 | button.accept-policy { 38 | font-size: 1rem; 39 | line-height: inherit; 40 | } 41 | 42 | .footer { 43 | position: absolute; 44 | bottom: 0; 45 | width: 100%; 46 | white-space: nowrap; 47 | line-height: 60px; 48 | } 49 | -------------------------------------------------------------------------------- /examples/FeatureFlagDemo/Views/Shared/_Layout.cshtml.css: -------------------------------------------------------------------------------- 1 | /* Please see documentation at https://docs.microsoft.com/aspnet/core/client-side/bundling-and-minification 2 | for details on configuring this project to bundle and minify static web assets. */ 3 | 4 | a.navbar-brand { 5 | white-space: normal; 6 | text-align: center; 7 | word-break: break-all; 8 | } 9 | 10 | a { 11 | color: #0077cc; 12 | } 13 | 14 | .btn-primary { 15 | color: #fff; 16 | background-color: #1b6ec2; 17 | border-color: #1861ac; 18 | } 19 | 20 | .nav-pills .nav-link.active, .nav-pills .show > .nav-link { 21 | color: #fff; 22 | background-color: #1b6ec2; 23 | border-color: #1861ac; 24 | } 25 | 26 | .border-top { 27 | border-top: 1px solid #e5e5e5; 28 | } 29 | .border-bottom { 30 | border-bottom: 1px solid #e5e5e5; 31 | } 32 | 33 | .box-shadow { 34 | box-shadow: 0 .25rem .75rem rgba(0, 0, 0, .05); 35 | } 36 | 37 | button.accept-policy { 38 | font-size: 1rem; 39 | line-height: inherit; 40 | } 41 | 42 | .footer { 43 | position: absolute; 44 | bottom: 0; 45 | width: 100%; 46 | white-space: nowrap; 47 | line-height: 60px; 48 | } 49 | -------------------------------------------------------------------------------- /examples/VariantServiceDemo/Pages/Shared/_Layout.cshtml.css: -------------------------------------------------------------------------------- 1 | /* Please see documentation at https://docs.microsoft.com/aspnet/core/client-side/bundling-and-minification 2 | for details on configuring this project to bundle and minify static web assets. */ 3 | 4 | a.navbar-brand { 5 | white-space: normal; 6 | text-align: center; 7 | word-break: break-all; 8 | } 9 | 10 | a { 11 | color: #0077cc; 12 | } 13 | 14 | .btn-primary { 15 | color: #fff; 16 | background-color: #1b6ec2; 17 | border-color: #1861ac; 18 | } 19 | 20 | .nav-pills .nav-link.active, .nav-pills .show > .nav-link { 21 | color: #fff; 22 | background-color: #1b6ec2; 23 | border-color: #1861ac; 24 | } 25 | 26 | .border-top { 27 | border-top: 1px solid #e5e5e5; 28 | } 29 | .border-bottom { 30 | border-bottom: 1px solid #e5e5e5; 31 | } 32 | 33 | .box-shadow { 34 | box-shadow: 0 .25rem .75rem rgba(0, 0, 0, .05); 35 | } 36 | 37 | button.accept-policy { 38 | font-size: 1rem; 39 | line-height: inherit; 40 | } 41 | 42 | .footer { 43 | position: absolute; 44 | bottom: 0; 45 | width: 100%; 46 | white-space: nowrap; 47 | line-height: 60px; 48 | } 49 | -------------------------------------------------------------------------------- /src/Microsoft.FeatureManagement/FilterAliasAttribute.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | // 4 | using System; 5 | 6 | namespace Microsoft.FeatureManagement 7 | { 8 | /// 9 | /// Allows the name of an to be customized to relate to the name specified in configuration. 10 | /// 11 | public class FilterAliasAttribute : Attribute 12 | { 13 | /// 14 | /// Creates a filter alias using the provided alias. 15 | /// 16 | /// The alias of the feature filter. 17 | public FilterAliasAttribute(string alias) 18 | { 19 | if (string.IsNullOrEmpty(alias)) 20 | { 21 | throw new ArgumentNullException(nameof(alias)); 22 | } 23 | 24 | Alias = alias; 25 | } 26 | 27 | /// 28 | /// The name that will be used to match feature filters specified in the configuration. 29 | /// 30 | public string Alias { get; } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /examples/VariantAndTelemetryDemo/Pages/Shared/_Layout.cshtml.css: -------------------------------------------------------------------------------- 1 | /* Please see documentation at https://docs.microsoft.com/aspnet/core/client-side/bundling-and-minification 2 | for details on configuring this project to bundle and minify static web assets. */ 3 | 4 | a.navbar-brand { 5 | white-space: normal; 6 | text-align: center; 7 | word-break: break-all; 8 | } 9 | 10 | a { 11 | color: #0077cc; 12 | } 13 | 14 | .btn-primary { 15 | color: #fff; 16 | background-color: #1b6ec2; 17 | border-color: #1861ac; 18 | } 19 | 20 | .nav-pills .nav-link.active, .nav-pills .show > .nav-link { 21 | color: #fff; 22 | background-color: #1b6ec2; 23 | border-color: #1861ac; 24 | } 25 | 26 | .border-top { 27 | border-top: 1px solid #e5e5e5; 28 | } 29 | .border-bottom { 30 | border-bottom: 1px solid #e5e5e5; 31 | } 32 | 33 | .box-shadow { 34 | box-shadow: 0 .25rem .75rem rgba(0, 0, 0, .05); 35 | } 36 | 37 | button.accept-policy { 38 | font-size: 1rem; 39 | line-height: inherit; 40 | } 41 | 42 | .footer { 43 | position: absolute; 44 | bottom: 0; 45 | width: 100%; 46 | white-space: nowrap; 47 | line-height: 60px; 48 | } 49 | -------------------------------------------------------------------------------- /src/Microsoft.FeatureManagement/FeatureManagementException.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | // 4 | using System; 5 | 6 | namespace Microsoft.FeatureManagement 7 | { 8 | /// 9 | /// Represents errors that occur during feature management. 10 | /// 11 | public class FeatureManagementException : Exception 12 | { 13 | /// 14 | /// Initializes a new instance of the class. 15 | /// 16 | /// The feature management error that the exception represents. 17 | /// Error message for the exception. 18 | public FeatureManagementException(FeatureManagementError errorType, string message) 19 | : base(message) 20 | { 21 | Error = errorType; 22 | } 23 | 24 | /// 25 | /// The feature management error that the exception represents. 26 | /// 27 | public FeatureManagementError Error { get; set; } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/Microsoft.FeatureManagement/VariantServiceAliasAttribute.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | // 4 | using System; 5 | 6 | namespace Microsoft.FeatureManagement 7 | { 8 | /// 9 | /// Allows the name of a variant service to be customized to relate to the variant name specified in configuration. 10 | /// 11 | public class VariantServiceAliasAttribute : Attribute 12 | { 13 | /// 14 | /// Creates a variant service alias using the provided alias. 15 | /// 16 | /// The alias of the variant service. 17 | public VariantServiceAliasAttribute(string alias) 18 | { 19 | if (string.IsNullOrEmpty(alias)) 20 | { 21 | throw new ArgumentNullException(nameof(alias)); 22 | } 23 | 24 | Alias = alias; 25 | } 26 | 27 | /// 28 | /// The name that will be used to match variant name specified in the configuration. 29 | /// 30 | public string Alias { get; } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/Microsoft.FeatureManagement/FeatureFilters/Recurrence/RecurrenceRangeType.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | // 4 | namespace Microsoft.FeatureManagement.FeatureFilters 5 | { 6 | /// 7 | /// The type of specifying the date range over which the time window repeats. 8 | /// 9 | public enum RecurrenceRangeType 10 | { 11 | /// 12 | /// The time window repeats on all the days that fit the corresponding . 13 | /// 14 | NoEnd, 15 | 16 | /// 17 | /// The time window repeats on all the days that fit the corresponding before or on the end date specified in EndDate of . 18 | /// 19 | EndDate, 20 | 21 | /// 22 | /// The time window repeats for the number specified in the NumberOfOccurrences of that fit based on the . 23 | /// 24 | Numbered 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Microsoft.FeatureManagement/ISessionManager.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | // 4 | using System.Threading.Tasks; 5 | 6 | namespace Microsoft.FeatureManagement 7 | { 8 | /// 9 | /// Used to store feature state across a session. The implementor is free to decide what constitutes a session. 10 | /// 11 | public interface ISessionManager 12 | { 13 | /// 14 | /// Set the state of a feature to be used for a session. 15 | /// 16 | /// The name of the feature. 17 | /// The state of the feature. 18 | Task SetAsync(string featureName, bool enabled); 19 | 20 | /// 21 | /// Queries the session manager for the session's feature state, if any, for the given feature. 22 | /// 23 | /// The name of the feature. 24 | /// The state of the feature if it is present in the session, otherwise null. 25 | Task GetAsync(string featureName); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /examples/BlazorServerApp/wwwroot/css/open-iconic/ICON-LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Waybury 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /src/Microsoft.FeatureManagement/Targeting/Audience.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | // 4 | using System.Collections.Generic; 5 | 6 | namespace Microsoft.FeatureManagement.FeatureFilters 7 | { 8 | /// 9 | /// An audience definition describing a set of users. 10 | /// 11 | public class Audience 12 | { 13 | /// 14 | /// Includes users in the audience by name. 15 | /// 16 | public List Users { get; set; } 17 | 18 | /// 19 | /// Includes users in the audience based off a group rollout. 20 | /// 21 | public List Groups { get; set; } 22 | 23 | /// 24 | /// Includes users in the audience based off a percentage of the total possible audience. Valid values range from 0 to 100 inclusive. 25 | /// 26 | public double DefaultRolloutPercentage { get; set; } 27 | 28 | /// 29 | /// Excludes a basic audience from this audience. 30 | /// 31 | public BasicAudience Exclusion { get; set; } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /tests/Tests.FeatureManagement/TestCache.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | // 4 | using Microsoft.Extensions.Caching.Memory; 5 | 6 | namespace Tests.FeatureManagement 7 | { 8 | class TestCache : IMemoryCache 9 | { 10 | private readonly IMemoryCache _cache; 11 | private int _countOfEntryCreation; 12 | 13 | public TestCache() 14 | { 15 | _cache = new MemoryCache(new MemoryCacheOptions()); 16 | } 17 | 18 | public int CountOfEntryCreation 19 | { 20 | get => _countOfEntryCreation; 21 | } 22 | 23 | public bool TryGetValue(object key, out object value) 24 | { 25 | return _cache.TryGetValue(key, out value); 26 | } 27 | 28 | public ICacheEntry CreateEntry(object key) 29 | { 30 | _countOfEntryCreation += 1; 31 | 32 | return _cache.CreateEntry(key); 33 | } 34 | 35 | public void Remove(object key) 36 | { 37 | _cache.Remove(key); 38 | } 39 | 40 | public void Dispose() 41 | { 42 | _cache.Dispose(); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /tests/Tests.FeatureManagement/CustomTargetingFilter.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | // 4 | using Microsoft.Extensions.Logging; 5 | using Microsoft.Extensions.Options; 6 | using Microsoft.FeatureManagement; 7 | using Microsoft.FeatureManagement.FeatureFilters; 8 | using System; 9 | using System.Threading.Tasks; 10 | 11 | namespace Tests.FeatureManagement 12 | { 13 | [FilterAlias(Alias)] 14 | class CustomTargetingFilter : IFeatureFilter 15 | { 16 | private const string Alias = "CustomTargetingFilter"; 17 | private readonly ContextualTargetingFilter _contextualFilter; 18 | 19 | public CustomTargetingFilter(IOptions options, ILoggerFactory loggerFactory) 20 | { 21 | _contextualFilter = new ContextualTargetingFilter(options, loggerFactory); 22 | } 23 | 24 | public Func> Callback { get; set; } 25 | 26 | public Task EvaluateAsync(FeatureFilterEvaluationContext context) 27 | { 28 | return _contextualFilter.EvaluateAsync(context, new TargetingContext() { UserId = "Jeff" }); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /examples/RazorPages/wwwroot/lib/jquery-validation/LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | ===================== 3 | 4 | Copyright Jörn Zaefferer 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /examples/ConsoleApp/FeatureFilters/AccountIdFilter.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | // 4 | using Microsoft.Extensions.Configuration; 5 | using Microsoft.FeatureManagement; 6 | 7 | /// 8 | /// A filter that uses the feature management context to ensure that the current task has the notion of an account id, and that the account id is allowed. 9 | /// This filter will only be executed if an object implementing is passed in during feature evaluation. 10 | /// 11 | [FilterAlias("AccountId")] 12 | class AccountIdFilter : IContextualFeatureFilter 13 | { 14 | public Task EvaluateAsync(FeatureFilterEvaluationContext featureEvaluationContext, IAccountContext accountContext) 15 | { 16 | if (string.IsNullOrEmpty(accountContext?.AccountId)) 17 | { 18 | throw new ArgumentNullException(nameof(accountContext)); 19 | } 20 | 21 | var allowedAccounts = new List(); 22 | 23 | featureEvaluationContext.Parameters.Bind("AllowedAccounts", allowedAccounts); 24 | 25 | return Task.FromResult(allowedAccounts.Contains(accountContext.AccountId)); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /examples/FeatureFlagDemo/wwwroot/lib/jquery-validation/LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | ===================== 3 | 4 | Copyright Jörn Zaefferer 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /examples/VariantServiceDemo/wwwroot/lib/jquery-validation/LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | ===================== 3 | 4 | Copyright Jörn Zaefferer 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Microsoft Corporation. All rights reserved. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE 22 | -------------------------------------------------------------------------------- /examples/BlazorServerApp/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | }, 8 | "AllowedHosts": "*", 9 | "feature_management": { 10 | "feature_flags": [ 11 | { 12 | "id": "BrowserEnhancement", 13 | "enabled": true, 14 | "conditions": { 15 | "client_filters": [ 16 | { 17 | "name": "Browser", 18 | "parameters": { 19 | "AllowedBrowsers": [ "Edge" ] 20 | } 21 | } 22 | ] 23 | } 24 | }, 25 | { 26 | "id": "Beta", 27 | "enabled": true, 28 | "conditions": { 29 | "client_filters": [ 30 | { 31 | "name": "Targeting", 32 | "parameters": { 33 | "Audience": { 34 | "DefaultRolloutPercentage": 50, 35 | "Exclusion": { 36 | "Groups": [ 37 | "Guests" 38 | ] 39 | } 40 | } 41 | } 42 | } 43 | ] 44 | } 45 | } 46 | ] 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /examples/VariantAndTelemetryDemo/wwwroot/lib/jquery-validation/LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | ===================== 3 | 4 | Copyright Jörn Zaefferer 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /examples/RazorPages/wwwroot/lib/bootstrap/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2011-2021 Twitter, Inc. 4 | Copyright (c) 2011-2021 The Bootstrap Authors 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /src/Microsoft.FeatureManagement/FeatureManagementError.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | // 4 | namespace Microsoft.FeatureManagement 5 | { 6 | /// 7 | /// An error that can occur during feature management. 8 | /// 9 | public enum FeatureManagementError 10 | { 11 | /// 12 | /// A feature filter that was listed for feature evaluation was not found. 13 | /// 14 | MissingFeatureFilter, 15 | 16 | /// 17 | /// A feature filter configured for the feature being evaluated is an ambiguous reference to multiple registered feature filters. 18 | /// 19 | AmbiguousFeatureFilter, 20 | 21 | /// 22 | /// A feature that was requested for evaluation was not found. 23 | /// 24 | MissingFeature, 25 | 26 | /// 27 | /// There was a conflict in the feature management system. 28 | /// 29 | Conflict, 30 | 31 | /// 32 | /// The given configuration setting was invalid. 33 | /// 34 | InvalidConfigurationSetting 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /examples/FeatureFlagDemo/wwwroot/lib/bootstrap/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2011-2021 Twitter, Inc. 4 | Copyright (c) 2011-2021 The Bootstrap Authors 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /examples/VariantServiceDemo/wwwroot/lib/bootstrap/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2011-2021 Twitter, Inc. 4 | Copyright (c) 2011-2021 The Bootstrap Authors 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /examples/BlazorServerApp/Pages/_Layout.cshtml: -------------------------------------------------------------------------------- 1 | @using Microsoft.AspNetCore.Components.Web 2 | @namespace BlazorServerApp.Pages 3 | @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | @RenderBody() 18 | 19 |
20 | 21 | An error has occurred. This application may no longer respond until reloaded. 22 | 23 | 24 | An unhandled exception has occurred. See browser dev tools for details. 25 | 26 | Reload 27 | 🗙 28 |
29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /examples/VariantAndTelemetryDemo/wwwroot/lib/bootstrap/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2011-2021 Twitter, Inc. 4 | Copyright (c) 2011-2021 The Bootstrap Authors 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /pack.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .Synopsis 3 | This script creates NuGet packages from all of the projects in this repo. 4 | 5 | Note: build.cmd should be run before running this script. 6 | 7 | #> 8 | 9 | [CmdletBinding()] 10 | param( 11 | [Parameter()] 12 | [ValidateSet('Debug','Release')] 13 | [string]$BuildConfig = "Release" 14 | ) 15 | 16 | $ErrorActionPreference = "Stop" 17 | 18 | $PublishRelativePath = "bin\PackageOutput" 19 | $LogDirectory = "$PSScriptRoot\buildlogs" 20 | 21 | $targetProjects = @( 22 | 23 | "Microsoft.FeatureManagement", 24 | "Microsoft.FeatureManagement.AspNetCore", 25 | "Microsoft.FeatureManagement.Telemetry.ApplicationInsights" 26 | ) 27 | 28 | # Create the log directory. 29 | if ((Test-Path -Path $LogDirectory) -ne $true) { 30 | New-Item -ItemType Directory -Path $LogDirectory | Write-Verbose 31 | } 32 | 33 | $dotnet = & "$PSScriptRoot/build/resolve-dotnet.ps1" 34 | 35 | foreach ($project in $targetProjects) 36 | { 37 | $projectPath = "$PSScriptRoot\src\$project\$project.csproj" 38 | $outputPath = "$PSScriptRoot\src\$project\$PublishRelativePath" 39 | 40 | & $dotnet pack -c $BuildConfig -o "$outputPath" "$projectPath" --no-build | Tee-Object -FilePath "$LogDirectory\build.log" 41 | } 42 | 43 | exit $LASTEXITCODE 44 | -------------------------------------------------------------------------------- /src/Microsoft.FeatureManagement/IFeatureFilter.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | // 4 | using System.Threading.Tasks; 5 | 6 | namespace Microsoft.FeatureManagement 7 | { 8 | /// 9 | /// A filter that can be used to determine whether some criteria is met to enable a feature. A feature filter is free to use any criteria available, such as process state or request content. Feature filters can be registered for a given feature and if any feature filter evaluates to true, that feature will be considered enabled. 10 | /// 11 | public interface IFeatureFilter : IFeatureFilterMetadata 12 | { 13 | /// 14 | /// Evaluates the feature filter to see if the filter's criteria for being enabled has been satisfied. 15 | /// 16 | /// A feature filter evaluation context that contains information that may be needed to evaluate the filter. This context includes configuration, if any, for this filter for the feature being evaluated. 17 | /// True if the filter's criteria has been met, false otherwise. 18 | Task EvaluateAsync(FeatureFilterEvaluationContext context); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /examples/FeatureFlagDemo/Views/Home/Beta.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewData["Title"] = "Home Page"; 3 | } 4 | 5 |

6 | Welcome to the new and sleek company portal. 7 |

8 | 9 | 10 | 11 |
12 |
13 |

Our Company

14 | 17 |
18 |
19 |

Growing Together

20 | 24 |
25 |
26 |

Moving Forward

27 | 30 |
31 |
32 |

Maintaining Growth

33 | 36 |
37 |
38 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: FeatureManagement-Dotnet CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | - preview 8 | - release/* 9 | pull_request: 10 | branches: 11 | - main 12 | - preview 13 | - release/* 14 | 15 | permissions: 16 | security-events: write 17 | 18 | jobs: 19 | build: 20 | runs-on: windows-latest 21 | 22 | steps: 23 | - name: Checkout code 24 | uses: actions/checkout@v2 25 | 26 | - name: Install .NET 27 | run: pwsh build/install-dotnet.ps1 -RestoreOnly 28 | 29 | - name: Restore 30 | run: pwsh build.ps1 -RestoreOnly 31 | 32 | - name: Initialize CodeQL 33 | uses: github/codeql-action/init@v3 34 | with: 35 | languages: 'csharp' 36 | 37 | - name: Dotnet Build 38 | run: pwsh build.ps1 39 | 40 | - name: Dotnet Pack 41 | run: pwsh pack.ps1 42 | 43 | - name: Dotnet Test 44 | run: pwsh test.ps1 45 | 46 | - name: Publish Test Results 47 | uses: actions/upload-artifact@v4 48 | with: 49 | name: Unit Test Results 50 | path: ${{ github.workspace }}/tests/**/*.trx 51 | 52 | - name: Perform CodeQL Analysis 53 | uses: github/codeql-action/analyze@v3 54 | -------------------------------------------------------------------------------- /src/Microsoft.FeatureManagement/FeatureManagementOptions.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | // 4 | namespace Microsoft.FeatureManagement 5 | { 6 | /// 7 | /// Options that control the behavior of the feature management system. 8 | /// 9 | public class FeatureManagementOptions 10 | { 11 | /// 12 | /// Controls the behavior of feature evaluation when dependent feature filters are missing. 13 | /// If missing feature filters are not ignored an exception will be thrown when attempting to evaluate a feature that depends on a missing feature filter. 14 | /// This option is invalid when used in combination with 15 | /// The default value is false. 16 | /// 17 | public bool IgnoreMissingFeatureFilters { get; set; } 18 | 19 | /// 20 | /// Controls the behavior of feature evaluation when the target feature is missing. 21 | /// If missing features are not ignored an exception will be thrown when attempting to evaluate them. 22 | /// The default value is true. 23 | /// 24 | public bool IgnoreMissingFeatures { get; set; } = true; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /examples/ConsoleApp/Program.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | // 4 | using Microsoft.Extensions.Configuration; 5 | using Microsoft.FeatureManagement; 6 | 7 | // 8 | // Setup configuration 9 | IConfiguration configuration = new ConfigurationBuilder() 10 | .AddJsonFile("appsettings.json") 11 | .Build(); 12 | 13 | var featureManager = new FeatureManager(new ConfigurationFeatureDefinitionProvider(configuration)) 14 | { 15 | FeatureFilters = new List { new AccountIdFilter() } 16 | }; 17 | 18 | var accounts = new List() 19 | { 20 | "abc", 21 | "adef", 22 | "abcdefghijklmnopqrstuvwxyz" 23 | }; 24 | 25 | // 26 | // Mimic work items in a task-driven console application 27 | foreach (var account in accounts) 28 | { 29 | const string FeatureName = "Beta"; 30 | 31 | // 32 | // Check if feature enabled 33 | // 34 | var accountServiceContext = new AccountServiceContext 35 | { 36 | AccountId = account 37 | }; 38 | 39 | bool enabled = await featureManager.IsEnabledAsync(FeatureName, accountServiceContext); 40 | 41 | // 42 | // Output results 43 | Console.WriteLine($"The {FeatureName} feature is {(enabled ? "enabled" : "disabled")} for the '{account}' account."); 44 | } 45 | -------------------------------------------------------------------------------- /examples/TargetingConsoleApp/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "feature_management": { 3 | "feature_flags": [ 4 | { 5 | "id": "Beta", 6 | "enabled": true, 7 | "conditions": { 8 | "client_filters": [ 9 | { 10 | "name": "Microsoft.Targeting", 11 | "parameters": { 12 | "Audience": { 13 | "Users": [ 14 | "Jeff", 15 | "Anne" 16 | ], 17 | "Groups": [ 18 | { 19 | "Name": "Management", 20 | "RolloutPercentage": 100 21 | }, 22 | { 23 | "Name": "TeamMembers", 24 | "RolloutPercentage": 45 25 | } 26 | ], 27 | "DefaultRolloutPercentage": 20, 28 | "Exclusion": { 29 | "Users": [ 30 | "Anne", 31 | "Phil" 32 | ], 33 | "Groups": [ 34 | "Contractor" 35 | ] 36 | } 37 | } 38 | } 39 | } 40 | ] 41 | } 42 | } 43 | ] 44 | } 45 | } -------------------------------------------------------------------------------- /src/Microsoft.FeatureManagement/FeatureFilters/Recurrence/RecurrencePattern.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | // 4 | using System; 5 | using System.Collections.Generic; 6 | 7 | namespace Microsoft.FeatureManagement.FeatureFilters 8 | { 9 | /// 10 | /// The recurrence pattern describes the frequency by which the time window repeats. 11 | /// 12 | public class RecurrencePattern 13 | { 14 | /// 15 | /// The recurrence pattern type. 16 | /// 17 | public RecurrencePatternType Type { get; set; } 18 | 19 | /// 20 | /// The number of units between occurrences, where units can be in days or weeks, depending on the pattern type. 21 | /// 22 | public int Interval { get; set; } = 1; 23 | 24 | /// 25 | /// The days of the week on which the time window occurs. This property is only applicable for weekly pattern. 26 | /// 27 | public IEnumerable DaysOfWeek { get; set; } 28 | 29 | /// 30 | /// The first day of the week. This property is only applicable for weekly pattern. 31 | /// 32 | public DayOfWeek FirstDayOfWeek { get; set; } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/Microsoft.FeatureManagement/Telemetry/EvaluationEvent.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | // 4 | using Microsoft.FeatureManagement.FeatureFilters; 5 | 6 | namespace Microsoft.FeatureManagement.Telemetry 7 | { 8 | /// 9 | /// An event representing the evaluation of a feature. 10 | /// 11 | public class EvaluationEvent 12 | { 13 | /// 14 | /// The definition of the feature that was evaluated. 15 | /// 16 | public FeatureDefinition FeatureDefinition { get; set; } 17 | 18 | /// 19 | /// The targeting context used to evaluate the feature. 20 | /// 21 | public TargetingContext TargetingContext { get; set; } 22 | 23 | /// 24 | /// The enabled state of the feature after evaluation. 25 | /// 26 | public bool Enabled { get; set; } 27 | 28 | /// 29 | /// The variant given after evaluation. 30 | /// 31 | public Variant Variant { get; set; } 32 | 33 | /// 34 | /// The reason why the variant was assigned. 35 | /// 36 | public VariantAssignmentReason VariantAssignmentReason { get; set; } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /examples/BlazorServerApp/Shared/NavMenu.razor.css: -------------------------------------------------------------------------------- 1 | .navbar-toggler { 2 | background-color: rgba(255, 255, 255, 0.1); 3 | } 4 | 5 | .top-row { 6 | height: 3.5rem; 7 | background-color: rgba(0,0,0,0.4); 8 | } 9 | 10 | .navbar-brand { 11 | font-size: 1.1rem; 12 | } 13 | 14 | .oi { 15 | width: 2rem; 16 | font-size: 1.1rem; 17 | vertical-align: text-top; 18 | top: -2px; 19 | } 20 | 21 | .nav-item { 22 | font-size: 0.9rem; 23 | padding-bottom: 0.5rem; 24 | } 25 | 26 | .nav-item:first-of-type { 27 | padding-top: 1rem; 28 | } 29 | 30 | .nav-item:last-of-type { 31 | padding-bottom: 1rem; 32 | } 33 | 34 | .nav-item ::deep a { 35 | color: #d7d7d7; 36 | border-radius: 4px; 37 | height: 3rem; 38 | display: flex; 39 | align-items: center; 40 | line-height: 3rem; 41 | } 42 | 43 | .nav-item ::deep a.active { 44 | background-color: rgba(255,255,255,0.25); 45 | color: white; 46 | } 47 | 48 | .nav-item ::deep a:hover { 49 | background-color: rgba(255,255,255,0.1); 50 | color: white; 51 | } 52 | 53 | @media (min-width: 641px) { 54 | .navbar-toggler { 55 | display: none; 56 | } 57 | 58 | .collapse { 59 | /* Never collapse the sidebar for wide screens */ 60 | display: block; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /examples/VariantServiceDemo/wwwroot/css/site.css: -------------------------------------------------------------------------------- 1 | html { 2 | font-size: 14px; 3 | } 4 | 5 | @media (min-width: 768px) { 6 | html { 7 | font-size: 16px; 8 | } 9 | } 10 | 11 | html { 12 | position: relative; 13 | min-height: 100%; 14 | } 15 | 16 | body { 17 | margin-bottom: 60px; 18 | } 19 | 20 | calculator { 21 | background-color: white; 22 | padding: 20px; 23 | border-radius: 8px; 24 | box-shadow: 0 4px 8px rgba(0,0,0,0.1); 25 | display: flex; 26 | justify-content: center; 27 | align-items: center; 28 | } 29 | 30 | input[type="number"] { 31 | font-size: 1.2em; 32 | padding: 8px; 33 | margin: 0 10px; 34 | width: 80px; 35 | border: 2px solid #ccc; 36 | border-radius: 4px; 37 | text-align: center; 38 | } 39 | 40 | span { 41 | font-size: 1.2em; 42 | margin-left: 10px; 43 | } 44 | 45 | button { 46 | padding: 10px 20px; 47 | font-size: 1.2em; 48 | width: 150px; 49 | color: white; 50 | background-color: #007BFF; 51 | border: none; 52 | border-radius: 4px; 53 | cursor: pointer; 54 | margin-left: 20px; 55 | } 56 | 57 | button:hover { 58 | background-color: #0056b3; 59 | } 60 | 61 | button:disabled { 62 | cursor: not-allowed; 63 | color: #aaa; 64 | background-color: #ccc; 65 | } 66 | -------------------------------------------------------------------------------- /src/Microsoft.FeatureManagement.AspNetCore/Extensions/EnumerableExtensions.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | // 4 | using System.Threading.Tasks; 5 | 6 | namespace System.Collections.Generic 7 | { 8 | static class EnumerableExtensions 9 | { 10 | public static async Task Any(this IEnumerable source, Func> predicate) 11 | { 12 | bool enabled = false; 13 | 14 | foreach (TSource item in source) 15 | { 16 | if (await predicate(item).ConfigureAwait(false)) 17 | { 18 | enabled = true; 19 | 20 | break; 21 | } 22 | } 23 | 24 | return enabled; 25 | } 26 | 27 | public static async Task All(this IEnumerable source, Func> predicate) 28 | { 29 | bool enabled = true; 30 | 31 | foreach (TSource item in source) 32 | { 33 | if (!await predicate(item).ConfigureAwait(false)) 34 | { 35 | enabled = false; 36 | 37 | break; 38 | } 39 | } 40 | 41 | return enabled; 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /examples/FeatureFlagDemo/FeatureFlagDemo.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | enable 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | PreserveNewest 19 | true 20 | PreserveNewest 21 | 22 | 23 | Always 24 | true 25 | PreserveNewest 26 | 27 | 28 | $(IncludeRazorContentInPack) 29 | 30 | 31 | $(IncludeRazorContentInPack) 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | ## Default settings ## 7 | [*] 8 | insert_final_newline = true 9 | indent_style = space 10 | indent_size = 4 11 | trim_trailing_whitespace = true 12 | 13 | ## Formatting rule ## 14 | # https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0055 15 | dotnet_diagnostic.IDE0055.severity = error 16 | 17 | # 'Using' directive preferences 18 | dotnet_sort_system_directives_first = false 19 | 20 | # New line preferences 21 | dotnet_diagnostic.IDE2002.severity = error 22 | csharp_style_allow_blank_lines_between_consecutive_braces_experimental = false 23 | dotnet_diagnostic.IDE2004.severity = error 24 | csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = false 25 | dotnet_diagnostic.IDE2005.severity = error 26 | csharp_style_allow_blank_line_after_token_in_conditional_expression_experimental = false 27 | dotnet_diagnostic.IDE2006.severity = error 28 | csharp_style_allow_blank_line_after_token_in_arrow_expression_clause_experimental = false 29 | dotnet_diagnostic.IDE2000.severity = error 30 | dotnet_style_allow_multiple_blank_lines_experimental = false 31 | dotnet_diagnostic.IDE2003.severity = error 32 | dotnet_style_allow_statement_immediately_after_block_experimental = false 33 | 34 | [*.csproj] 35 | indent_size = 2 36 | charset = utf-8 37 | 38 | [*.json] 39 | indent_size = 2 -------------------------------------------------------------------------------- /tests/Tests.FeatureManagement/InvalidFeatureFilter.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | // 4 | using Microsoft.FeatureManagement; 5 | using System.Threading.Tasks; 6 | 7 | namespace Tests.FeatureManagement 8 | { 9 | // 10 | // Cannot implement more than one IFeatureFilter interface 11 | class InvalidFeatureFilter : IContextualFeatureFilter, IContextualFeatureFilter 12 | { 13 | public Task EvaluateAsync(FeatureFilterEvaluationContext context, IAccountContext accountContext) 14 | { 15 | return Task.FromResult(false); 16 | } 17 | 18 | public Task EvaluateAsync(FeatureFilterEvaluationContext featureFilterContext, object appContext) 19 | { 20 | return Task.FromResult(false); 21 | } 22 | } 23 | 24 | // 25 | // Cannot implement more than one IFeatureFilter interface 26 | class InvalidFeatureFilter2 : IFeatureFilter, IContextualFeatureFilter 27 | { 28 | public Task EvaluateAsync(FeatureFilterEvaluationContext featureFilterContext, object appContext) 29 | { 30 | return Task.FromResult(false); 31 | } 32 | 33 | public Task EvaluateAsync(FeatureFilterEvaluationContext context) 34 | { 35 | return Task.FromResult(false); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /examples/BlazorServerApp/MyTargetingContextAccessor.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Components.Authorization; 2 | using Microsoft.FeatureManagement.FeatureFilters; 3 | 4 | namespace BlazorServerApp 5 | { 6 | public class MyTargetingContextAccessor : ITargetingContextAccessor 7 | { 8 | private readonly AuthenticationStateProvider _authenticationStateProvider; 9 | 10 | public MyTargetingContextAccessor(AuthenticationStateProvider authenticationStateProvider) 11 | { 12 | _authenticationStateProvider = authenticationStateProvider ?? throw new ArgumentNullException(nameof(authenticationStateProvider)); 13 | } 14 | 15 | public async ValueTask GetContextAsync() 16 | { 17 | AuthenticationState authState = await _authenticationStateProvider.GetAuthenticationStateAsync(); 18 | 19 | string username = authState.User.Identity.Name; 20 | 21 | bool isAuthenticated = authState.User.Identity.IsAuthenticated; 22 | 23 | var groups = new List(); 24 | 25 | if (!isAuthenticated) 26 | { 27 | groups.Add("Guests"); 28 | } 29 | 30 | var targetingContext = new TargetingContext 31 | { 32 | UserId = username, 33 | Groups = groups 34 | }; 35 | 36 | return targetingContext; 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /examples/BlazorServerApp/Pages/Index.razor: -------------------------------------------------------------------------------- 1 | @page "/" 2 | 3 | Index 4 | 5 |

Hello, world!

6 | 7 |

@authMessage

8 | 9 | 10 | 11 | Welcome to your new app. 12 | 13 | @if (isBetaEnabled) 14 | { 15 |

You are able to see the BETA content!

16 | 17 | } 18 |
19 |
20 | 21 | 22 | 23 | @inject IFeatureManagerSnapshot _featureManager 24 | 25 | @code { 26 | [CascadingParameter] 27 | private Task authenticationState { get; set; } 28 | 29 | private string authMessage = "The user is NOT authenticated."; 30 | 31 | private bool isBetaEnabled; 32 | 33 | protected override async Task OnInitializedAsync() 34 | { 35 | if (authenticationState != null) 36 | { 37 | var authState = await authenticationState; 38 | 39 | var user = authState?.User; 40 | 41 | if (user?.Identity is not null && user.Identity.IsAuthenticated) 42 | { 43 | authMessage = $"The user {user.Identity.Name} is authenticated."; 44 | } 45 | } 46 | 47 | isBetaEnabled = await _featureManager.IsEnabledAsync("Beta"); 48 | } 49 | } -------------------------------------------------------------------------------- /src/Microsoft.FeatureManagement/ConfigurationWrapper.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | // 4 | using Microsoft.Extensions.Configuration; 5 | using Microsoft.Extensions.Primitives; 6 | using System; 7 | using System.Collections.Generic; 8 | 9 | namespace Microsoft.FeatureManagement 10 | { 11 | /// 12 | /// Wraps an instance of IConfiguration. This allows the reference to be updated when the underlying IConfiguration is updated. 13 | /// This is useful for cache busting based on the reference. 14 | /// 15 | class ConfigurationWrapper : IConfiguration 16 | { 17 | private readonly IConfiguration _configuration; 18 | 19 | public ConfigurationWrapper(IConfiguration configuration) 20 | { 21 | _configuration = configuration ?? throw new ArgumentNullException(nameof(configuration)); 22 | } 23 | 24 | public string this[string key] 25 | { 26 | get => _configuration[key]; 27 | set => _configuration[key] = value; 28 | } 29 | 30 | public IEnumerable GetChildren() 31 | => _configuration.GetChildren(); 32 | 33 | public IChangeToken GetReloadToken() 34 | => _configuration.GetReloadToken(); 35 | 36 | public IConfigurationSection GetSection(string key) 37 | => _configuration.GetSection(key); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /examples/BlazorServerApp/Shared/NavMenu.razor: -------------------------------------------------------------------------------- 1 | 9 | 10 |
11 | 28 |
29 | 30 | @code { 31 | private bool collapseNavMenu = true; 32 | 33 | private string NavMenuCssClass => collapseNavMenu ? "collapse" : null; 34 | 35 | private void ToggleNavMenu() 36 | { 37 | collapseNavMenu = !collapseNavMenu; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /tests/Tests.FeatureManagement/InMemoryFeatureDefinitionProvider.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.FeatureManagement; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | 7 | namespace Tests.FeatureManagement 8 | { 9 | class InMemoryFeatureDefinitionProvider : IFeatureDefinitionProvider, IFeatureDefinitionProviderCacheable 10 | { 11 | private IEnumerable _definitions; 12 | 13 | public InMemoryFeatureDefinitionProvider(IEnumerable featureDefinitions) 14 | { 15 | _definitions = featureDefinitions ?? throw new ArgumentNullException(nameof(featureDefinitions)); 16 | } 17 | 18 | #pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously 19 | public async IAsyncEnumerable GetAllFeatureDefinitionsAsync() 20 | #pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously 21 | { 22 | foreach (FeatureDefinition definition in _definitions) 23 | { 24 | yield return definition; 25 | } 26 | } 27 | 28 | public Task GetFeatureDefinitionAsync(string featureName) 29 | { 30 | return Task.FromResult(_definitions.FirstOrDefault(definitions => definitions.Name.Equals(featureName, StringComparison.OrdinalIgnoreCase))); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/Microsoft.FeatureManagement/FeatureFilters/TimeWindowFilterSettings.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | // 4 | using System; 5 | 6 | namespace Microsoft.FeatureManagement.FeatureFilters 7 | { 8 | /// 9 | /// The settings that are used to configure the feature filter. 10 | /// 11 | public class TimeWindowFilterSettings 12 | { 13 | /// 14 | /// An optional start time used to determine when a feature configured to use the feature filter should be enabled. 15 | /// If no start time is specified the time window is considered to have already started. 16 | /// 17 | public DateTimeOffset? Start { get; set; } 18 | 19 | /// 20 | /// An optional end time used to determine when a feature configured to use the feature filter should be enabled. 21 | /// If no end time is specified the time window is considered to never end. 22 | /// 23 | public DateTimeOffset? End { get; set; } 24 | 25 | /// 26 | /// Add-on recurrence rule allows the time window defined by Start and End to recur. 27 | /// The rule specifies both how often the time window repeats and for how long. 28 | /// 29 | public Recurrence Recurrence { get; set; } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Microsoft.FeatureManagement/Telemetry/VariantAssignmentReason.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | // 4 | namespace Microsoft.FeatureManagement.Telemetry 5 | { 6 | /// 7 | /// The reason the variant was assigned during the evaluation of a feature. 8 | /// 9 | public enum VariantAssignmentReason 10 | { 11 | /// 12 | /// Variant allocation did not happen. No variant is assigned. 13 | /// 14 | None, 15 | 16 | /// 17 | /// The default variant is assigned when a feature flag is disabled. 18 | /// 19 | DefaultWhenDisabled, 20 | 21 | /// 22 | /// The default variant is assigned because of no applicable user/group/percentile allocation when a feature flag is enabled. 23 | /// 24 | DefaultWhenEnabled, 25 | 26 | /// 27 | /// The variant is assigned because of the user allocation when a feature flag is enabled. 28 | /// 29 | User, 30 | 31 | /// 32 | /// The variant is assigned because of the group allocation when a feature flag is enabled. 33 | /// 34 | Group, 35 | 36 | /// 37 | /// The variant is assigned because of the percentile allocation when a feature flag is enabled. 38 | /// 39 | Percentile 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/Microsoft.FeatureManagement/VariantFeatureManagerExtensions.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | // 4 | using Microsoft.FeatureManagement.FeatureFilters; 5 | using System.Threading; 6 | using System.Threading.Tasks; 7 | 8 | namespace Microsoft.FeatureManagement 9 | { 10 | /// 11 | /// Extensions for . 12 | /// 13 | public static class VariantFeatureManagerExtensions 14 | { 15 | /// 16 | /// Gets the assigned variant for a specific feature. 17 | /// 18 | /// The instance. 19 | /// The name of the feature to evaluate. 20 | /// An instance of used to evaluate which variant the user will be assigned. 21 | /// The cancellation token to cancel the operation. 22 | /// A variant assigned to the user based on the feature's configured allocation. 23 | public static ValueTask GetVariantAsync(this IVariantFeatureManager variantFeatureManager, string feature, TargetingContext context, CancellationToken cancellationToken = default) 24 | { 25 | return variantFeatureManager.GetVariantAsync(feature, context, cancellationToken); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /examples/VariantAndTelemetryDemo/Program.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | // 4 | using Microsoft.FeatureManagement; 5 | 6 | var builder = WebApplication.CreateBuilder(args); 7 | 8 | // 9 | // Use cookie auth for simplicity and randomizing user 10 | builder.Services.AddAuthentication("CookieAuth") 11 | .AddCookie("CookieAuth", options => 12 | { 13 | options.LoginPath = "/RandomizeUser"; 14 | }); 15 | 16 | // 17 | // What a web app using app insights looks like 18 | // 19 | // Add services to the container. 20 | builder.Services.AddRazorPages(); 21 | 22 | builder.Services.AddApplicationInsightsTelemetry(); 23 | 24 | // 25 | // Enter feature management 26 | // 27 | // Enhance a web application with feature management 28 | // Including user targeting capability 29 | // Wire up evaluation event emission 30 | builder.Services.AddFeatureManagement() 31 | .WithTargeting() 32 | .AddApplicationInsightsTelemetry(); 33 | 34 | // 35 | // Default code from .NET template below 36 | // 37 | var app = builder.Build(); 38 | 39 | if (!app.Environment.IsDevelopment()) 40 | { 41 | app.UseExceptionHandler("/Error"); 42 | 43 | app.UseHsts(); 44 | } 45 | 46 | app.UseHttpsRedirection(); 47 | 48 | app.UseStaticFiles(); 49 | 50 | app.UseRouting(); 51 | 52 | app.UseAuthentication(); 53 | 54 | app.UseAuthorization(); 55 | 56 | app.MapRazorPages(); 57 | 58 | // 59 | // Add Targeting Id to HttpContext 60 | app.UseMiddleware(); 61 | 62 | app.Run(); 63 | -------------------------------------------------------------------------------- /examples/BlazorServerApp/Pages/FetchData.razor: -------------------------------------------------------------------------------- 1 | @page "/fetchdata" 2 | 3 | Weather forecast 4 | 5 | @using BlazorServerApp.Data 6 | @inject WeatherForecastService ForecastService 7 | 8 | 9 | 10 |

Weather forecast

11 | 12 |

This component demonstrates fetching data from a service.

13 | 14 | @if (forecasts == null) 15 | { 16 |

Loading...

17 | } 18 | else 19 | { 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | @foreach (var forecast in forecasts) 31 | { 32 | 33 | 34 | 35 | 36 | 37 | 38 | } 39 | 40 |
DateTemp. (C)Temp. (F)Summary
@forecast.Date.ToShortDateString()@forecast.TemperatureC@forecast.TemperatureF@forecast.Summary
41 | } 42 |
43 |
44 | 45 | @code { 46 | private WeatherForecast[] forecasts; 47 | 48 | protected override async Task OnInitializedAsync() 49 | { 50 | forecasts = await ForecastService.GetForecastAsync(DateTime.Now); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/Microsoft.FeatureManagement/Allocation/Allocation.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | // 4 | using System.Collections.Generic; 5 | 6 | namespace Microsoft.FeatureManagement 7 | { 8 | /// 9 | /// The definition of how variants are allocated for a feature. 10 | /// 11 | public class Allocation 12 | { 13 | /// 14 | /// The default variant used if the feature is enabled and no variant is assigned. 15 | /// 16 | public string DefaultWhenEnabled { get; set; } 17 | 18 | /// 19 | /// The default variant used if the feature is disabled. 20 | /// 21 | public string DefaultWhenDisabled { get; set; } 22 | 23 | /// 24 | /// Describes a mapping of user ids to variants. 25 | /// 26 | public IEnumerable User { get; set; } 27 | 28 | /// 29 | /// Describes a mapping of group names to variants. 30 | /// 31 | public IEnumerable Group { get; set; } 32 | 33 | /// 34 | /// Allocates percentiles of user base to variants. 35 | /// 36 | public IEnumerable Percentile { get; set; } 37 | 38 | /// 39 | /// Maps users to the same percentile across multiple feature flags. 40 | /// 41 | public string Seed { get; set; } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/Microsoft.FeatureManagement/FeatureFilterEvaluationContext.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | // 4 | using Microsoft.Extensions.Configuration; 5 | using System.Threading; 6 | 7 | namespace Microsoft.FeatureManagement 8 | { 9 | /// 10 | /// A context used by to gain insight into what feature is being evaluated and the parameters needed to check whether the feature should be enabled. 11 | /// 12 | public class FeatureFilterEvaluationContext 13 | { 14 | /// 15 | /// The name of the feature being evaluated. 16 | /// 17 | public string FeatureName { get; set; } 18 | 19 | /// 20 | /// The settings provided for the feature filter to use when evaluating whether the feature should be enabled. 21 | /// 22 | public IConfiguration Parameters { get; set; } 23 | 24 | /// 25 | /// A settings object, if any, that has been pre-bound from . 26 | /// The settings are made available for s that implement . 27 | /// 28 | public object Settings { get; set; } 29 | 30 | /// 31 | /// A cancellation token that can be used to request cancellation of the feature evaluation operation. 32 | /// 33 | public CancellationToken CancellationToken { get; set; } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/Microsoft.FeatureManagement/IFeatureManager.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | // 4 | using System.Collections.Generic; 5 | using System.Threading.Tasks; 6 | 7 | namespace Microsoft.FeatureManagement 8 | { 9 | /// 10 | /// Used to evaluate whether a feature is enabled or disabled. 11 | /// 12 | public interface IFeatureManager 13 | { 14 | /// 15 | /// Retrieves a list of feature names registered in the feature manager. 16 | /// 17 | /// An enumerator which provides asynchronous iteration over the feature names registered in the feature manager. 18 | IAsyncEnumerable GetFeatureNamesAsync(); 19 | 20 | /// 21 | /// Checks whether a given feature is enabled. 22 | /// 23 | /// The name of the feature to check. 24 | /// True if the feature is enabled, otherwise false. 25 | Task IsEnabledAsync(string feature); 26 | 27 | /// 28 | /// Checks whether a given feature is enabled. 29 | /// 30 | /// The name of the feature to check. 31 | /// A context that provides information to evaluate whether a feature should be on or off. 32 | /// True if the feature is enabled, otherwise false. 33 | Task IsEnabledAsync(string feature, TContext context); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /examples/VariantServiceDemo/Pages/Index.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model IndexModel 3 | @inject IHttpContextAccessor httpContextAccessor 4 | @{ 5 | ViewData["Title"] = "Home page"; 6 | } 7 | 8 |
9 |

Current user @Model.Username

10 |
11 | 12 | 13 | + 14 | = 15 | 0 16 |
17 | 18 |
19 |
20 | 21 | @section Scripts { 22 | 47 | } -------------------------------------------------------------------------------- /tests/Tests.FeatureManagement.AspNetCore/TestController.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | // 4 | using Microsoft.AspNetCore.Mvc; 5 | using Microsoft.FeatureManagement; 6 | using Microsoft.FeatureManagement.Mvc; 7 | 8 | namespace Tests.FeatureManagement.AspNetCore 9 | { 10 | [Route("")] 11 | public class TestController : Controller 12 | { 13 | [HttpGet] 14 | public IActionResult Get() 15 | { 16 | return Ok(); 17 | } 18 | 19 | [Route("/gateAll")] 20 | [HttpGet] 21 | [FeatureGate(Features.ConditionalFeature, Features.ConditionalFeature2)] 22 | public IActionResult GateAll() 23 | { 24 | return Ok(); 25 | } 26 | 27 | [Route("/gateAny")] 28 | [HttpGet] 29 | [FeatureGate(requirementType: RequirementType.Any, Features.ConditionalFeature, Features.ConditionalFeature2)] 30 | public IActionResult GateAny() 31 | { 32 | return Ok(); 33 | } 34 | 35 | [Route("/gateAllNegate")] 36 | [HttpGet] 37 | [FeatureGate(negate: true, Features.ConditionalFeature, Features.ConditionalFeature2)] 38 | public IActionResult GateAllNegate() 39 | { 40 | return Ok(); 41 | } 42 | 43 | [Route("/gateAnyNegate")] 44 | [HttpGet] 45 | [FeatureGate(requirementType: RequirementType.Any, negate: true, Features.ConditionalFeature, Features.ConditionalFeature2)] 46 | public IActionResult GateAnyNegate() 47 | { 48 | return Ok(); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/Microsoft.FeatureManagement/IFeatureManagementBuilder.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | // 4 | using Microsoft.Extensions.DependencyInjection; 5 | 6 | namespace Microsoft.FeatureManagement 7 | { 8 | /// 9 | /// Provides a way to customize feature management functionality. 10 | /// 11 | public interface IFeatureManagementBuilder 12 | { 13 | /// 14 | /// The application services. 15 | /// 16 | IServiceCollection Services { get; } 17 | 18 | /// 19 | /// Adds a given feature filter to the list of feature filters that will be available to enable features during runtime. 20 | /// Possible feature filter metadata types include and 21 | /// Only one feature filter interface can be implemented by a single type. 22 | /// 23 | /// The feature filter type. 24 | /// The feature management builder. 25 | IFeatureManagementBuilder AddFeatureFilter() where T : IFeatureFilterMetadata; 26 | 27 | /// 28 | /// Adds an to be used for storing feature state in a session. 29 | /// 30 | /// An implementation of 31 | /// The feature management builder. 32 | IFeatureManagementBuilder AddSessionManager() where T : ISessionManager; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /examples/TargetingConsoleApp/Program.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | // 4 | using Microsoft.Extensions.Configuration; 5 | using Microsoft.FeatureManagement; 6 | using Microsoft.FeatureManagement.FeatureFilters; 7 | using TargetingConsoleApp.Identity; 8 | 9 | // 10 | // Setup configuration 11 | IConfiguration configuration = new ConfigurationBuilder() 12 | .AddJsonFile("appsettings.json") 13 | .Build(); 14 | 15 | var featureManager = new FeatureManager(new ConfigurationFeatureDefinitionProvider(configuration)) 16 | { 17 | FeatureFilters = new List { new ContextualTargetingFilter() } 18 | }; 19 | 20 | var userRepository = new InMemoryUserRepository(); 21 | 22 | // 23 | // We'll simulate a task to run on behalf of each known user 24 | // To do this we enumerate all the users in our user repository 25 | IEnumerable userIds = InMemoryUserRepository.Users.Select(u => u.Id); 26 | 27 | // 28 | // Mimic work items in a task-driven console application 29 | foreach (string userId in userIds) 30 | { 31 | const string FeatureName = "Beta"; 32 | 33 | // 34 | // Get user 35 | User user = await userRepository.GetUser(userId); 36 | 37 | // 38 | // Check if feature enabled 39 | var targetingContext = new TargetingContext 40 | { 41 | UserId = user.Id, 42 | Groups = user.Groups 43 | }; 44 | 45 | bool enabled = await featureManager.IsEnabledAsync(FeatureName, targetingContext); 46 | 47 | // 48 | // Output results 49 | Console.WriteLine($"The {FeatureName} feature is {(enabled ? "enabled" : "disabled")} for the user '{userId}'."); 50 | } 51 | -------------------------------------------------------------------------------- /examples/BlazorServerApp/Pages/_Host.cshtml: -------------------------------------------------------------------------------- 1 | @page "/" 2 | @namespace BlazorServerApp.Pages 3 | @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers 4 | @using System.Security.Claims; 5 | @using Microsoft.AspNetCore.Authentication; 6 | @using Microsoft.AspNetCore.Authentication.Cookies; 7 | @{ 8 | Layout = "_Layout"; 9 | 10 | int randomNum = Random.Shared.Next(); 11 | 12 | // 13 | // It has 2/3 chance to be logged in. 14 | if (randomNum % 3 != 0) 15 | { 16 | string username = randomNum.ToString(); 17 | 18 | var claims = new List 19 | { 20 | new Claim(ClaimTypes.Name, username) 21 | }; 22 | 23 | var claimsIdentity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme); 24 | 25 | await HttpContext.SignInAsync( 26 | CookieAuthenticationDefaults.AuthenticationScheme, 27 | new ClaimsPrincipal(claimsIdentity), 28 | new AuthenticationProperties()); 29 | } 30 | else 31 | { 32 | await HttpContext.SignOutAsync( 33 | CookieAuthenticationDefaults.AuthenticationScheme); 34 | } 35 | 36 | // 37 | // The user agent context will be set to the parameter UserAgent in App.razor. 38 | // This is the recommended way to work with HttpContext in Blazor server apps. For more details, please refer to: https://learn.microsoft.com/en-us/aspnet/core/blazor/security/server/interactive-server-side-rendering?view=aspnetcore-7.0#ihttpcontextaccessorhttpcontext-in-razor-components 39 | string userAgent = HttpContext.Request.Headers["User-Agent"]; 40 | } 41 | 42 | 43 | -------------------------------------------------------------------------------- /examples/VariantServiceDemo/Program.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | // 4 | using Microsoft.FeatureManagement; 5 | using VariantServiceDemo; 6 | 7 | var builder = WebApplication.CreateBuilder(args); 8 | 9 | // 10 | // What a web app using app insights looks like 11 | // 12 | // Add services to the container. 13 | builder.Services.AddRazorPages(); 14 | 15 | // 16 | // Use cookie auth for simplicity and randomizing user 17 | builder.Services.AddAuthentication("CookieAuth"); 18 | 19 | builder.Services.AddApplicationInsightsTelemetry(); 20 | 21 | // 22 | // Add variant implementations of ICalculator 23 | builder.Services.AddSingleton(); 24 | 25 | builder.Services.AddSingleton(); 26 | 27 | // 28 | // Enter feature management 29 | // 30 | // Enhance a web application with feature management 31 | // Including user targeting capability and the variant service provider of ICalculator which is bounded with the variant feature flag "Calculator" 32 | // Wire up evaluation event emission 33 | builder.Services.AddFeatureManagement() 34 | .WithTargeting() 35 | .WithVariantService("Calculator") 36 | .AddApplicationInsightsTelemetry(); 37 | 38 | var app = builder.Build(); 39 | 40 | if (!app.Environment.IsDevelopment()) 41 | { 42 | app.UseExceptionHandler("/Error"); 43 | 44 | app.UseHsts(); 45 | } 46 | 47 | app.UseHttpsRedirection(); 48 | 49 | app.UseStaticFiles(); 50 | 51 | app.UseRouting(); 52 | 53 | app.UseAuthorization(); 54 | 55 | app.MapRazorPages(); 56 | 57 | // 58 | // Add Targeting Id to HttpContext 59 | app.UseMiddleware(); 60 | 61 | app.Run(); 62 | -------------------------------------------------------------------------------- /examples/BlazorServerApp/Pages/Error.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model BlazorServerApp.Pages.ErrorModel 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | Error 11 | 12 | 13 | 14 | 15 | 16 |
17 |
18 |

Error.

19 |

An error occurred while processing your request.

20 | 21 | @if (Model.ShowRequestId) 22 | { 23 |

24 | Request ID: @Model.RequestId 25 |

26 | } 27 | 28 |

Development Mode

29 |

30 | Swapping to the Development environment displays detailed information about the error that occurred. 31 |

32 |

33 | The Development environment shouldn't be enabled for deployed applications. 34 | It can result in displaying sensitive information from exceptions to end users. 35 | For local debugging, enable the Development environment by setting the ASPNETCORE_ENVIRONMENT environment variable to Development 36 | and restarting the app. 37 |

38 |
39 |
40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /examples/BlazorServerApp/Shared/MainLayout.razor.css: -------------------------------------------------------------------------------- 1 | .page { 2 | position: relative; 3 | display: flex; 4 | flex-direction: column; 5 | } 6 | 7 | main { 8 | flex: 1; 9 | } 10 | 11 | .sidebar { 12 | background-image: linear-gradient(180deg, rgb(5, 39, 103) 0%, #3a0647 70%); 13 | } 14 | 15 | .top-row { 16 | background-color: #f7f7f7; 17 | border-bottom: 1px solid #d6d5d5; 18 | justify-content: flex-end; 19 | height: 3.5rem; 20 | display: flex; 21 | align-items: center; 22 | } 23 | 24 | .enhanced-top-row { 25 | background-color: #04173D; 26 | border-bottom: 1px solid #04173D; 27 | justify-content: flex-end; 28 | height: 3.6rem; 29 | display: flex; 30 | align-items: center; 31 | } 32 | 33 | .top-row ::deep a, .top-row .btn-link { 34 | white-space: nowrap; 35 | margin-left: 1.5rem; 36 | } 37 | 38 | .top-row a:first-child { 39 | overflow: hidden; 40 | text-overflow: ellipsis; 41 | } 42 | 43 | @media (max-width: 640.98px) { 44 | .top-row:not(.auth) { 45 | display: none; 46 | } 47 | 48 | .top-row.auth { 49 | justify-content: space-between; 50 | } 51 | 52 | .top-row a, .top-row .btn-link { 53 | margin-left: 0; 54 | } 55 | } 56 | 57 | @media (min-width: 641px) { 58 | .page { 59 | flex-direction: row; 60 | } 61 | 62 | .sidebar { 63 | width: 250px; 64 | height: 100vh; 65 | position: sticky; 66 | top: 0; 67 | } 68 | 69 | .top-row { 70 | position: sticky; 71 | top: 0; 72 | z-index: 1; 73 | } 74 | 75 | .top-row, article { 76 | padding-left: 2rem !important; 77 | padding-right: 1.5rem !important; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /examples/RazorPages/wwwroot/lib/jquery/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright JS Foundation and other contributors, https://js.foundation/ 2 | 3 | This software consists of voluntary contributions made by many 4 | individuals. For exact contribution history, see the revision history 5 | available at https://github.com/jquery/jquery 6 | 7 | The following license applies to all parts of this software except as 8 | documented below: 9 | 10 | ==== 11 | 12 | Permission is hereby granted, free of charge, to any person obtaining 13 | a copy of this software and associated documentation files (the 14 | "Software"), to deal in the Software without restriction, including 15 | without limitation the rights to use, copy, modify, merge, publish, 16 | distribute, sublicense, and/or sell copies of the Software, and to 17 | permit persons to whom the Software is furnished to do so, subject to 18 | the following conditions: 19 | 20 | The above copyright notice and this permission notice shall be 21 | included in all copies or substantial portions of the Software. 22 | 23 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 24 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 25 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 26 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 27 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 28 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 29 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 30 | 31 | ==== 32 | 33 | All files located in the node_modules and external directories are 34 | externally maintained libraries used by this software which have their 35 | own licenses; we recommend you read them, as their terms may differ from 36 | the terms above. 37 | -------------------------------------------------------------------------------- /examples/FeatureFlagDemo/wwwroot/lib/jquery/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright JS Foundation and other contributors, https://js.foundation/ 2 | 3 | This software consists of voluntary contributions made by many 4 | individuals. For exact contribution history, see the revision history 5 | available at https://github.com/jquery/jquery 6 | 7 | The following license applies to all parts of this software except as 8 | documented below: 9 | 10 | ==== 11 | 12 | Permission is hereby granted, free of charge, to any person obtaining 13 | a copy of this software and associated documentation files (the 14 | "Software"), to deal in the Software without restriction, including 15 | without limitation the rights to use, copy, modify, merge, publish, 16 | distribute, sublicense, and/or sell copies of the Software, and to 17 | permit persons to whom the Software is furnished to do so, subject to 18 | the following conditions: 19 | 20 | The above copyright notice and this permission notice shall be 21 | included in all copies or substantial portions of the Software. 22 | 23 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 24 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 25 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 26 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 27 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 28 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 29 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 30 | 31 | ==== 32 | 33 | All files located in the node_modules and external directories are 34 | externally maintained libraries used by this software which have their 35 | own licenses; we recommend you read them, as their terms may differ from 36 | the terms above. 37 | -------------------------------------------------------------------------------- /examples/VariantServiceDemo/wwwroot/lib/jquery/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright JS Foundation and other contributors, https://js.foundation/ 2 | 3 | This software consists of voluntary contributions made by many 4 | individuals. For exact contribution history, see the revision history 5 | available at https://github.com/jquery/jquery 6 | 7 | The following license applies to all parts of this software except as 8 | documented below: 9 | 10 | ==== 11 | 12 | Permission is hereby granted, free of charge, to any person obtaining 13 | a copy of this software and associated documentation files (the 14 | "Software"), to deal in the Software without restriction, including 15 | without limitation the rights to use, copy, modify, merge, publish, 16 | distribute, sublicense, and/or sell copies of the Software, and to 17 | permit persons to whom the Software is furnished to do so, subject to 18 | the following conditions: 19 | 20 | The above copyright notice and this permission notice shall be 21 | included in all copies or substantial portions of the Software. 22 | 23 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 24 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 25 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 26 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 27 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 28 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 29 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 30 | 31 | ==== 32 | 33 | All files located in the node_modules and external directories are 34 | externally maintained libraries used by this software which have their 35 | own licenses; we recommend you read them, as their terms may differ from 36 | the terms above. 37 | -------------------------------------------------------------------------------- /examples/VariantAndTelemetryDemo/wwwroot/lib/jquery/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright JS Foundation and other contributors, https://js.foundation/ 2 | 3 | This software consists of voluntary contributions made by many 4 | individuals. For exact contribution history, see the revision history 5 | available at https://github.com/jquery/jquery 6 | 7 | The following license applies to all parts of this software except as 8 | documented below: 9 | 10 | ==== 11 | 12 | Permission is hereby granted, free of charge, to any person obtaining 13 | a copy of this software and associated documentation files (the 14 | "Software"), to deal in the Software without restriction, including 15 | without limitation the rights to use, copy, modify, merge, publish, 16 | distribute, sublicense, and/or sell copies of the Software, and to 17 | permit persons to whom the Software is furnished to do so, subject to 18 | the following conditions: 19 | 20 | The above copyright notice and this permission notice shall be 21 | included in all copies or substantial portions of the Software. 22 | 23 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 24 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 25 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 26 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 27 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 28 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 29 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 30 | 31 | ==== 32 | 33 | All files located in the node_modules and external directories are 34 | externally maintained libraries used by this software which have their 35 | own licenses; we recommend you read them, as their terms may differ from 36 | the terms above. 37 | -------------------------------------------------------------------------------- /examples/BlazorServerApp/Program.cs: -------------------------------------------------------------------------------- 1 | using BlazorServerApp.Data; 2 | using Microsoft.AspNetCore.Authentication.Cookies; 3 | using Microsoft.FeatureManagement; 4 | 5 | namespace BlazorServerApp 6 | { 7 | public class Program 8 | { 9 | public static void Main(string[] args) 10 | { 11 | var builder = WebApplication.CreateBuilder(args); 12 | 13 | builder.Services.AddRazorPages(); 14 | builder.Services.AddServerSideBlazor(); 15 | builder.Services.AddSingleton(); 16 | 17 | builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme) 18 | .AddCookie(); 19 | 20 | builder.Services.AddScoped(); 21 | 22 | // 23 | // To consume scoped services, AddScopedFeatureManagement should be used instead of AddFeatureManagement. 24 | // This will ensure that feature management services, including feature filters, targeting context accessor, are added as scoped services. 25 | builder.Services.AddScopedFeatureManagement() 26 | .WithTargeting() 27 | .AddFeatureFilter(); 28 | 29 | var app = builder.Build(); 30 | 31 | if (!app.Environment.IsDevelopment()) 32 | { 33 | app.UseExceptionHandler("/Error"); 34 | app.UseHsts(); 35 | } 36 | 37 | app.UseHttpsRedirection(); 38 | 39 | app.UseStaticFiles(); 40 | 41 | app.UseRouting(); 42 | 43 | app.UseAuthentication(); 44 | 45 | app.MapBlazorHub(); 46 | app.MapFallbackToPage("/_Host"); 47 | 48 | app.Run(); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/Microsoft.FeatureManagement/IContextualFeatureFilter.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | // 4 | using System.Threading.Tasks; 5 | 6 | namespace Microsoft.FeatureManagement 7 | { 8 | /// 9 | /// A filter that can be used to determine whether some criteria is met to enable a feature. A feature filter is free to use any criteria available, such as process state or request content. 10 | /// Feature filters can be registered for a given feature and if any feature filter evaluates to true, that feature will be considered enabled. 11 | /// A contextual feature filter can take advantage of contextual data passed in from callers of the feature management system. 12 | /// A contextual feature filter will only be executed if a context that is assignable from TContext is available. 13 | /// 14 | public interface IContextualFeatureFilter : IFeatureFilterMetadata 15 | { 16 | /// 17 | /// Evaluates the feature filter to see if the filter's criteria for being enabled has been satisfied. 18 | /// 19 | /// A feature filter evaluation context that contains information that may be needed to evaluate the filter. This context includes configuration, if any, for this filter for the feature being evaluated. 20 | /// A context defined by the application that is passed in to the feature management system to provide contextual information for evaluating a feature's state. 21 | /// True if the filter's criteria has been met, false otherwise. 22 | Task EvaluateAsync(FeatureFilterEvaluationContext featureFilterContext, TContext appContext); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /examples/VariantAndTelemetryDemo/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | }, 8 | "AllowedHosts": "*", 9 | "ApplicationInsights": { 10 | "ConnectionString": "" 11 | }, 12 | "feature_management": { 13 | "feature_flags": [ 14 | { 15 | "id": "ImageRating", 16 | "enabled": true, 17 | "telemetry": { 18 | "enabled": true, 19 | "metadata": { 20 | "Label": "A Label", 21 | "Etag": "An etag", 22 | "Tags.A": "Tag A value" 23 | } 24 | }, 25 | "variants": [ 26 | { 27 | "name": "BlackAndWhite", 28 | "configuration_value": "https://cdn.pixabay.com/photo/2017/09/28/09/26/dinosaur-2794840_1280.png" 29 | }, 30 | { 31 | "name": "WithColor", 32 | "configuration_value": "https://cdn.pixabay.com/photo/2012/04/02/15/18/stegosaurus-24752_1280.png" 33 | }, 34 | { 35 | "name": "HighQuality", 36 | "configuration_value": "https://cdn.pixabay.com/photo/2019/02/01/14/24/landscape-3969074_1280.jpg" 37 | } 38 | ], 39 | "allocation": { 40 | "percentile": [ 41 | { 42 | "variant": "BlackAndWhite", 43 | "from": 0, 44 | "to": 33 45 | }, 46 | { 47 | "variant": "WithColor", 48 | "from": 33, 49 | "to": 66 50 | }, 51 | { 52 | "variant": "HighQuality", 53 | "from": 66, 54 | "to": 100 55 | } 56 | ] 57 | } 58 | } 59 | ] 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /examples/VariantServiceDemo/Pages/Index.cshtml.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Authentication; 2 | using Microsoft.AspNetCore.Mvc; 3 | using Microsoft.AspNetCore.Mvc.RazorPages; 4 | using Microsoft.FeatureManagement; 5 | using System.Security.Claims; 6 | 7 | namespace VariantServiceDemo.Pages 8 | { 9 | public class IndexModel : PageModel 10 | { 11 | private readonly IVariantServiceProvider _calculatorProvider; 12 | 13 | public string Username { get; set; } 14 | 15 | public IndexModel(IVariantServiceProvider calculatorProvider) 16 | { 17 | _calculatorProvider = calculatorProvider ?? throw new ArgumentNullException(nameof(calculatorProvider)); 18 | } 19 | 20 | public IActionResult OnGet() 21 | { 22 | // 23 | // generate a new visitor 24 | Username = Random.Shared.Next().ToString(); 25 | 26 | // Clear Application Insights cookies and 27 | Response.Cookies.Delete("ai_user"); 28 | Response.Cookies.Delete("ai_session"); 29 | 30 | // Generate new user claim 31 | var claims = new List 32 | { 33 | new Claim(ClaimTypes.Name, Username) 34 | }; 35 | 36 | var identity = new ClaimsIdentity(claims, "CookieAuth"); 37 | var principal = new ClaimsPrincipal(identity); 38 | 39 | HttpContext.SignInAsync("CookieAuth", principal); 40 | 41 | return Page(); 42 | } 43 | 44 | public async Task OnGetCalculate(double a, double b) 45 | { 46 | ICalculator calculator = await _calculatorProvider.GetServiceAsync(HttpContext.RequestAborted); 47 | 48 | double result = await calculator.AddAsync(a, b); 49 | 50 | return new JsonResult(result); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /examples/FeatureFlagDemo/Program.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | // 4 | using Microsoft.AspNetCore; 5 | using Microsoft.Extensions.Configuration.AzureAppConfiguration; 6 | 7 | namespace FeatureFlagDemo 8 | { 9 | public class Program 10 | { 11 | public static void Main(string[] args) 12 | { 13 | CreateWebHostBuilder(args).Build().Run(); 14 | } 15 | 16 | public static IWebHostBuilder CreateWebHostBuilder(string[] args) 17 | { 18 | return WebHost.CreateDefaultBuilder(args) 19 | .ConfigureAppConfiguration((ctx, builder) => 20 | { 21 | var settings = builder.Build(); 22 | 23 | if (!string.IsNullOrEmpty(settings["AppConfiguration:ConnectionString"])) 24 | { 25 | // 26 | // This section can be used to pull feature flag configuration from Azure App Configuration 27 | builder.AddAzureAppConfiguration(o => 28 | { 29 | o.Connect(settings["AppConfiguration:ConnectionString"]); 30 | 31 | o.Select(KeyFilter.Any); 32 | 33 | o.UseFeatureFlags(); 34 | }); 35 | } 36 | else 37 | { 38 | // 39 | // Disable Azure App Configuration provider when connection string is not configured 40 | // This enables feature flag retrieval solely from appsettings.json 41 | Environment.SetEnvironmentVariable("AZURE_APP_CONFIGURATION_PROVIDER_DISABLED", "true"); 42 | } 43 | }) 44 | .UseStartup(); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /examples/FeatureFlagDemo/BrowserFilter.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | // 4 | using Microsoft.FeatureManagement; 5 | 6 | namespace FeatureFlagDemo.FeatureManagement.FeatureFilters 7 | { 8 | [FilterAlias("Browser")] 9 | public class BrowserFilter : IFeatureFilter 10 | { 11 | private const string Chrome = "Chrome"; 12 | private const string Edge = "Edge"; 13 | 14 | private readonly IHttpContextAccessor _httpContextAccessor; 15 | 16 | public BrowserFilter(IHttpContextAccessor httpContextAccessor) 17 | { 18 | _httpContextAccessor = httpContextAccessor ?? throw new ArgumentNullException(nameof(httpContextAccessor)); 19 | } 20 | 21 | public Task EvaluateAsync(FeatureFilterEvaluationContext context) 22 | { 23 | BrowserFilterSettings settings = context.Parameters.Get() ?? new BrowserFilterSettings(); 24 | 25 | if (settings.AllowedBrowsers.Any(browser => browser.Equals(Chrome, StringComparison.OrdinalIgnoreCase)) && IsChrome()) 26 | { 27 | return Task.FromResult(true); 28 | } 29 | else if (settings.AllowedBrowsers.Any(browser => browser.Equals(Edge, StringComparison.OrdinalIgnoreCase)) && IsEdge()) 30 | { 31 | return Task.FromResult(true); 32 | } 33 | 34 | return Task.FromResult(false); 35 | } 36 | 37 | private bool IsChrome() 38 | { 39 | string userAgent = _httpContextAccessor.HttpContext.Request.Headers["User-Agent"]; 40 | 41 | return userAgent != null && userAgent.Contains("Chrome", StringComparison.OrdinalIgnoreCase) && !userAgent.Contains("edge", StringComparison.OrdinalIgnoreCase); 42 | } 43 | 44 | private bool IsEdge() 45 | { 46 | // Return true if current request is sent from Edge browser 47 | 48 | return false; 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/Microsoft.FeatureManagement.Telemetry.ApplicationInsights/TargetingTelemetryInitializer.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | // 4 | 5 | using Microsoft.ApplicationInsights.Channel; 6 | using Microsoft.ApplicationInsights.DataContracts; 7 | using Microsoft.ApplicationInsights.Extensibility; 8 | using System.Diagnostics; 9 | 10 | namespace Microsoft.FeatureManagement.Telemetry.ApplicationInsights 11 | { 12 | /// 13 | /// Used to add targeting information to outgoing Application Insights telemetry. 14 | /// 15 | public class TargetingTelemetryInitializer : ITelemetryInitializer 16 | { 17 | private const string TargetingIdKey = "TargetingId"; 18 | 19 | /// 20 | /// When telemetry is initialized, adds targeting information to all relevant telemetry. 21 | /// 22 | /// The to be initialized. 23 | /// Thrown if the any param is null. 24 | public void Initialize(ITelemetry telemetry) 25 | { 26 | if (telemetry == null) 27 | { 28 | throw new ArgumentNullException(nameof(telemetry)); 29 | } 30 | 31 | // Extract the targeting id from the current activity's baggage 32 | string targetingId = Activity.Current?.Baggage.FirstOrDefault(t => t.Key == TargetingIdKey).Value; 33 | 34 | // Don't modify telemetry if there's no available targeting id 35 | if (string.IsNullOrEmpty(targetingId)) 36 | { 37 | return; 38 | } 39 | 40 | // Telemetry.Properties is deprecated in favor of ISupportProperties 41 | if (telemetry is ISupportProperties telemetryWithSupportProperties) 42 | { 43 | telemetryWithSupportProperties.Properties["TargetingId"] = targetingId; 44 | } 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /examples/FeatureFlagDemo/Views/Shared/_CookieConsentPartial.cshtml: -------------------------------------------------------------------------------- 1 | @using Microsoft.AspNetCore.Http.Features 2 | 3 | @{ 4 | var consentFeature = Context.Features.Get(); 5 | var showBanner = !consentFeature?.CanTrack ?? false; 6 | var cookieString = consentFeature?.CreateConsentCookie(); 7 | } 8 | 9 | @if (showBanner) 10 | { 11 | 33 | 41 | } -------------------------------------------------------------------------------- /examples/FeatureFlagDemo/Controllers/HomeController.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | // 4 | using FeatureFlagDemo.Models; 5 | using Microsoft.AspNetCore.Mvc; 6 | using Microsoft.FeatureManagement; 7 | using Microsoft.FeatureManagement.Mvc; 8 | using System.Diagnostics; 9 | 10 | namespace FeatureFlagDemo.Controllers 11 | { 12 | public class HomeController : Controller 13 | { 14 | private readonly IFeatureManager _featureManager; 15 | 16 | public HomeController(IFeatureManagerSnapshot featureSnapshot) 17 | { 18 | _featureManager = featureSnapshot; 19 | } 20 | 21 | [FeatureGate(MyFeatureFlags.Home)] 22 | public IActionResult Index() 23 | { 24 | return View(); 25 | } 26 | 27 | public async Task About() 28 | { 29 | ViewData["Message"] = "Your application description page."; 30 | 31 | if (await _featureManager.IsEnabledAsync(MyFeatureFlags.CustomViewData)) 32 | { 33 | ViewData["Message"] = $"This is FANCY CONTENT you can see only if '{MyFeatureFlags.CustomViewData}' is enabled."; 34 | } 35 | 36 | return View(); 37 | } 38 | 39 | public IActionResult Contact() 40 | { 41 | ViewData["Message"] = "Your contact page."; 42 | 43 | return View(); 44 | } 45 | 46 | [FeatureGate(MyFeatureFlags.Beta)] 47 | public IActionResult Beta() 48 | { 49 | ViewData["Message"] = "Your contact page."; 50 | 51 | return View(); 52 | } 53 | 54 | public IActionResult Privacy() 55 | { 56 | return View(); 57 | } 58 | 59 | [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] 60 | public IActionResult Error() 61 | { 62 | return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier }); 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/Microsoft.FeatureManagement.Telemetry.ApplicationInsights/ApplicationInsightsHostedService.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.DependencyInjection; 2 | using Microsoft.Extensions.Hosting; 3 | 4 | namespace Microsoft.FeatureManagement.Telemetry.ApplicationInsights 5 | { 6 | /// 7 | /// A hosted service used to construct and dispose the 8 | /// 9 | internal sealed class ApplicationInsightsHostedService : IHostedService 10 | { 11 | private readonly IServiceProvider _serviceProvider; 12 | 13 | /// 14 | /// Initializes a new instance of the class. 15 | /// 16 | /// The to get the publisher from. 17 | public ApplicationInsightsHostedService(IServiceProvider serviceProvider) 18 | { 19 | _serviceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider)); 20 | } 21 | 22 | /// 23 | /// Uses the service provider to construct a which will start listening for activities. 24 | /// 25 | /// The cancellation token. 26 | /// A representing the asynchronous operation. 27 | public Task StartAsync(CancellationToken cancellationToken) 28 | { 29 | _serviceProvider.GetService(); 30 | 31 | return Task.CompletedTask; 32 | } 33 | 34 | /// 35 | /// Stops this hosted service. 36 | /// 37 | /// The cancellation token. 38 | /// A representing the asynchronous operation. 39 | public Task StopAsync(CancellationToken cancellationToken) 40 | { 41 | return Task.CompletedTask; 42 | } 43 | } 44 | } 45 | --------------------------------------------------------------------------------