Skip to content

Commit fe509f5

Browse files
Merge pull request #422 from microsoft/preview
Merge preview to release/v4
2 parents 5c2af2c + e219d06 commit fe509f5

28 files changed

+1342
-158
lines changed

README.md

+161-22
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ Here are some of the benefits of using this library:
3636
* [Targeting](#targeting)
3737
* [Targeting Exclusion](#targeting-exclusion)
3838
* [Variants](#variants)
39+
* [Variants in Dependency Injection](#variants-in-dependency-injection)
3940
* [Telemetry](#telemetry)
4041
* [Enabling Telemetry](#enabling-telemetry)
4142
* [Custom Telemetry Publishers](#custom-telemetry-publishers)
@@ -96,7 +97,7 @@ The feature management library supports appsettings.json as a feature flag sourc
9697

9798
The `FeatureManagement` section of the json document is used by convention to load feature flag settings. In the section above, we see that we have provided three different features. Features define their feature filters using the `EnabledFor` property. In the feature filters for `FeatureT` we see `AlwaysOn`. This feature filter is built-in and if specified will always enable the feature. The `AlwaysOn` feature filter does not require any configuration, so it only has the `Name` property. `FeatureU` has no filters in its `EnabledFor` property and thus will never be enabled. Any functionality that relies on this feature being enabled will not be accessible as long as the feature filters remain empty. However, as soon as a feature filter is added that enables the feature it can begin working. `FeatureV` specifies a feature filter named `TimeWindow`. This is an example of a configurable feature filter. We can see in the example that the filter has a `Parameters` property. This is used to configure the filter. In this case, the start and end times for the feature to be active are configured.
9899

99-
The detailed schema of the `FeatureManagement` section can be found [here](./schemas/FeatureManagement.Dotnet.v1.0.0.schema.json).
100+
The detailed schema of the `FeatureManagement` section can be found [here](./schemas/FeatureManagement.Dotnet.v2.0.0.schema.json).
100101

101102
**Advanced:** The usage of colon ':' in feature flag names is forbidden.
102103

@@ -199,6 +200,36 @@ The feature management library also supports the usage of the [`Microsoft Featur
199200

200201
**Note:** If the `feature_management` section can be found in the configuration, the `FeatureManagement` section will be ignored.
201202

203+
#### Microsoft Feature Management Schema
204+
205+
The feature management library also supports the usage of the [`Microsoft Feature Management schema`](https://github.com/Azure/AppConfiguration/blob/main/docs/FeatureManagement/FeatureManagement.v1.0.0.schema.json) to declare feature flags. This schema is language agnostic in origin and is supported by all Microsoft feature management libraries.
206+
207+
``` JavaScript
208+
{
209+
"feature_management": {
210+
"feature_flags": [
211+
{
212+
"id": "FeatureT",
213+
"enabled": true,
214+
"conditions": {
215+
"client_filters": [
216+
{
217+
"name": "Microsoft.TimeWindow",
218+
"parameters": {
219+
"Start": "Mon, 01 May 2023 13:59:59 GMT",
220+
"End": "Sat, 01 July 2023 00:00:00 GMT"
221+
}
222+
}
223+
]
224+
}
225+
}
226+
]
227+
}
228+
}
229+
```
230+
231+
**Note:** If the `feature_management` section can be found in the configuration, the `FeatureManagement` section will be ignored.
232+
202233
## Consumption
203234

204235
The basic form of feature management is checking if a feature flag is enabled and then performing actions based on the result. This is done through the `IFeatureManager`'s `IsEnabledAsync` method.
@@ -784,17 +815,60 @@ variantConfiguration.Bind(settings);
784815

785816
The variant returned is dependent on the user currently being evaluated, and that information is obtained from an instance of `TargetingContext`. This context can either be passed in when calling `GetVariantAsync` or it can be automatically retrieved from an implementation of [`ITargetingContextAccessor`](#itargetingcontextaccessor) if one is registered.
786817

787-
### Defining Variants
818+
### Variant Feature Flag Declaration
819+
820+
Compared to normal feature flags, variant feature flags have two additional properties: `Variants` and `Allocation`. The `Variants` property is an array that contains the variants defined for this feature. The `Allocation` property defines how these variants should be allocated for the feature. Just like declaring normal feature flags, you can set up variant feature flags in a json file. Here is an example of a variant feature flag.
821+
822+
``` javascript
823+
824+
{
825+
"FeatureManagement":
826+
{
827+
"MyVariantFeatureFlag":
828+
{
829+
"Allocation": {
830+
"DefaultWhenEnabled": "Small",
831+
"Group": [
832+
{
833+
"Variant": "Big",
834+
"Groups": [
835+
"Ring1"
836+
]
837+
}
838+
]
839+
},
840+
"Variants": [
841+
{
842+
"Name": "Big"
843+
},
844+
{
845+
"Name": "Small"
846+
}
847+
],
848+
"EnabledFor": [
849+
{
850+
"Name": "AlwaysOn"
851+
}
852+
]
853+
}
854+
}
855+
}
856+
857+
```
858+
859+
For more details about how to configure variant feature flags, please see [here](./schemas/FeatureManagement.Dotnet.v2.0.0.schema.json).
860+
861+
#### Defining Variants
788862

789863
Each variant has two properties: a name and a configuration. The name is used to refer to a specific variant, and the configuration is the value of that variant. The configuration can be set using either the `ConfigurationReference` or `ConfigurationValue` properties. `ConfigurationReference` is a string path that references a section of the current configuration that contains the feature flag declaration. `ConfigurationValue` is an inline configuration that can be a string, number, boolean, or configuration object. If both are specified, `ConfigurationValue` is used. If neither are specified, the returned variant's `Configuration` property will be null.
790864

791865
A list of all possible variants is defined for each feature under the `Variants` property.
792866

793-
```
867+
``` javascript
794868
{
795869
"FeatureManagement":
796870
{
797-
"MyFlag":
871+
"MyVariantFeatureFlag":
798872
{
799873
"Variants": [
800874
{
@@ -815,14 +889,25 @@ A list of all possible variants is defined for each feature under the `Variants`
815889
]
816890
}
817891
}
892+
893+
"ShoppingCart": {
894+
"Big": {
895+
"Size": 600,
896+
"Color": "green"
897+
},
898+
"Small": {
899+
"Size": 300,
900+
"Color": "gray"
901+
}
902+
}
818903
}
819904
```
820905

821-
### Allocating Variants
906+
#### Allocating Variants
822907

823908
The process of allocating a feature's variants is determined by the `Allocation` property of the feature.
824909

825-
```
910+
``` javascript
826911
"Allocation": {
827912
"DefaultWhenEnabled": "Small",
828913
"DefaultWhenDisabled": "Small",
@@ -870,7 +955,7 @@ The `Allocation` setting of a feature flag has the following properties:
870955
| `DefaultWhenDisabled` | Specifies which variant should be used when a variant is requested while the feature is considered disabled. |
871956
| `DefaultWhenEnabled` | Specifies which variant should be used when a variant is requested while the feature is considered enabled and no other variant was assigned to the user. |
872957
| `User` | Specifies a variant and a list of users to whom that variant should be assigned. |
873-
| `Group` | Specifies a variant and a list of groups the current user has to be in for that variant to be assigned. |
958+
| `Group` | Specifies a variant and a list of groups. The variant will be assigned if the user is in at least one of the groups. |
874959
| `Percentile` | Specifies a variant and a percentage range the user's calculated percentage has to fit into for that variant to be assigned. |
875960
| `Seed` | The value which percentage calculations for `Percentile` are based on. The percentage calculation for a specific user will be the same across all features if the same `Seed` value is used. If no `Seed` is specified, then a default seed is created based on the feature name. |
876961

@@ -880,40 +965,94 @@ If the feature is enabled, the feature manager will check the `User`, `Group`, a
880965

881966
Allocation logic is similar to the [Microsoft.Targeting](./README.md#MicrosoftTargeting) feature filter, but there are some parameters that are present in targeting that aren't in allocation, and vice versa. The outcomes of targeting and allocation are not related.
882967

968+
**Note:** To allow allocating feature variants, you need to register `ITargetingContextAccessor`. This can be done by calling the `WithTargeting<T>` method.
969+
883970
### Overriding Enabled State with a Variant
884971

885972
You can use variants to override the enabled state of a feature flag. This gives variants an opportunity to extend the evaluation of a feature flag. If a caller is checking whether a flag that has variants is enabled, the feature manager will check if the variant assigned to the current user is set up to override the result. This is done using the optional variant property `StatusOverride`. By default, this property is set to `None`, which means the variant doesn't affect whether the flag is considered enabled or disabled. Setting `StatusOverride` to `Enabled` allows the variant, when chosen, to override a flag to be enabled. Setting `StatusOverride` to `Disabled` provides the opposite functionality, therefore disabling the flag when the variant is chosen. A feature with a `Status` of `Disabled` cannot be overridden.
886973

887974
If you are using a feature flag with binary variants, the `StatusOverride` property can be very helpful. It allows you to continue using APIs like `IsEnabledAsync` and `FeatureGateAttribute` in your application, all while benefiting from the new features that come with variants, such as percentile allocation and seed.
888975

889-
```
976+
``` javascript
890977
"Allocation": {
891-
"Percentile": [{
892-
"Variant": "On",
893-
"From": 10,
894-
"To": 20
895-
}],
978+
"Percentile": [
979+
{
980+
"Variant": "On",
981+
"From": 10,
982+
"To": 20
983+
}
984+
],
896985
"DefaultWhenEnabled": "Off",
897986
"Seed": "Enhanced-Feature-Group"
898987
},
899988
"Variants": [
900-
{
989+
{
901990
"Name": "On"
902991
},
903-
{
992+
{
904993
"Name": "Off",
905994
"StatusOverride": "Disabled"
906-
}
995+
}
907996
],
908-
"EnabledFor": [
909-
{
910-
"Name": "AlwaysOn"
911-
}
912-
]
997+
"EnabledFor": [
998+
{
999+
"Name": "AlwaysOn"
1000+
}
1001+
]
9131002
```
9141003

9151004
In the above example, the feature is enabled by the `AlwaysOn` filter. If the current user is in the calculated percentile range of 10 to 20, then the `On` variant is returned. Otherwise, the `Off` variant is returned and because `StatusOverride` is equal to `Disabled`, the feature will now be considered disabled.
9161005

1006+
### Variants in Dependency Injection
1007+
1008+
Variant feature flags can be used in conjunction with dependency injection to surface different implementations of a service for different users. This is accomplished through the use of the `IVariantServiceProvider<TService>` interface.
1009+
1010+
``` C#
1011+
IVariantServiceProvider<IAlgorithm> algorithmServiceProvider;
1012+
...
1013+
1014+
IAlgorithm forecastAlgorithm = await algorithmServiceProvider.GetServiceAsync(cancellationToken);
1015+
```
1016+
1017+
In the snippet above, the `IVariantServiceProvider<IAlgorithm>` will retrieve an implementation of `IAlgorithm` from the dependency injection container. The chosen implementation is dependent upon:
1018+
* The feature flag that the `IAlgorithm` service was registered with.
1019+
* The allocated variant for that feature.
1020+
1021+
The `IVariantServiceProvider<T>` is made available to the application by calling `IFeatureManagementBuilder.WithVariantService<T>(string featureName)`. See below for an example.
1022+
1023+
``` C#
1024+
services.AddFeatureManagement()
1025+
.WithVariantService<IAlgorithm>("ForecastAlgorithm");
1026+
```
1027+
1028+
The call above makes `IVariantServiceProvider<IAlgorithm>` available in the service collection. Implementation(s) of `IAlgorithm` must be added separately via an add method such as `services.AddSingleton<IAlgorithm, SomeImplementation>()`. The implementation of `IAlgorithm` that the `IVariantServiceProvider` uses depends on the `ForecastAlgorithm` variant feature flag. If no implementation of `IAlgorithm` is added to the service collection, then the `IVariantServiceProvider<IAlgorithm>.GetServiceAsync()` will return a task with a *null* result.
1029+
1030+
``` javascript
1031+
{
1032+
// The example variant feature flag
1033+
"ForecastAlgorithm": {
1034+
"Variants": [
1035+
{
1036+
"Name": "AlgorithmBeta"
1037+
},
1038+
...
1039+
]
1040+
}
1041+
}
1042+
```
1043+
1044+
#### Variant Service Alias Attribute
1045+
1046+
``` C#
1047+
[VariantServiceAlias("Beta")]
1048+
public class AlgorithmBeta : IAlgorithm
1049+
{
1050+
...
1051+
}
1052+
```
1053+
1054+
The variant service provider will use the type names of implementations to match the allocated variant. If a variant service is decorated with the `VariantServiceAliasAttribute`, the name declared in this attribute should be used in configuration to reference this variant service.
1055+
9171056
## Telemetry
9181057

9191058
When a feature flag change is deployed, it is often important to analyze its effect on an application. For example, here are a few questions that may arise:
@@ -931,7 +1070,7 @@ By default, feature flags will not have telemetry emitted. To publish telemetry
9311070

9321071
For flags defined in `appsettings.json`, that is done by using the `Telemetry` property on feature flags.
9331072

934-
```
1073+
``` javascript
9351074
{
9361075
"FeatureManagement":
9371076
{

examples/EvaluationDataToApplicationInsights/Program.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// Copyright (c) Microsoft Corporation.
22
// Licensed under the MIT license.
33
//
4-
using Microsoft.FeatureManagement.Telemetry.ApplicationInsights;
4+
using Microsoft.FeatureManagement.Telemetry;
55
using Microsoft.FeatureManagement;
66
using EvaluationDataToApplicationInsights;
77
using Microsoft.ApplicationInsights.Extensibility;

pack.ps1

+2-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ $targetProjects = @(
2222

2323
"Microsoft.FeatureManagement",
2424
"Microsoft.FeatureManagement.AspNetCore",
25-
"Microsoft.FeatureManagement.Telemetry.ApplicationInsights"
25+
"Microsoft.FeatureManagement.Telemetry.ApplicationInsights",
26+
"Microsoft.FeatureManagement.Telemetry.ApplicationInsights.AspNetCore"
2627
)
2728

2829
# Create the log directory.

0 commit comments

Comments
 (0)