Skip to content

Commit f6a2e45

Browse files
update
1 parent 0aa3749 commit f6a2e45

File tree

30 files changed

+5896
-747
lines changed

30 files changed

+5896
-747
lines changed

README.md

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,26 +2,29 @@
22

33
[![feature-management](https://img.shields.io/npm/v/@microsoft/feature-management?label=@microsoft/feature-management)](https://www.npmjs.com/package/@microsoft/feature-management)
44

5-
Feature Management is a library for enabling/disabling features at runtime.
6-
Developers can use feature flags in simple use cases like conditional statement to more advanced scenarios like conditionally adding routes.
5+
Feature management provides a way to develop and expose application functionality based on features. Many applications have special requirements when a new feature is developed such as when the feature should be enabled and under what conditions. This library provides a way to define these relationships, and also integrates into common JavaScript code patterns to make exposing these features possible.
76

87
## Getting Started
98

10-
### Prerequisites
9+
[Azure App Configuration Quickstart](https://learn.microsoft.com/azure/azure-app-configuration/quickstart-feature-flag-javascript): A quickstart guide about how to integrate feature flags from Azure App Configuration into your JavaScript applications.
1110

12-
- Node.js LTS version
11+
[Feature Overview](https://learn.microsoft.com/azure/azure-app-configuration/feature-management-overview#feature-development-status): This document provides a feature status overview.
12+
13+
[Feature Reference](https://learn.microsoft.com/azure/azure-app-configuration/feature-management-javascript-reference): This document provides a full feature rundown.
1314

1415
### Usage
1516

16-
You can use feature flags from the Azure App Configuration service, local files or any other sources.
17+
You can use feature flags from the Azure App Configuration service, local files or any other sources. For more information, please go to [Feature flag configuration](https://learn.microsoft.com/azure/azure-app-configuration/feature-management-javascript-reference#feature-flag-configuration).
1718

1819
#### Use feature flags from Azure App Configuration
1920

2021
The App Configuration JavaScript provider provides feature flags in as a `Map` object.
2122
A builtin `ConfigurationMapFeatureFlagProvider` helps to load feature flags in this case.
2223

2324
```js
24-
const appConfig = await load(connectionString, {featureFlagOptions}); // load feature flags from Azure App Configuration service
25+
import { load } from "@azure/app-configuration-provider";
26+
import { FeatureManager, ConfigurationMapFeatureFlagProvider } from "@microsoft/feature-management";
27+
const appConfig = await load("<CONNECTION-STRING>", {featureFlagOptions}); // load feature flags from Azure App Configuration service
2528
const featureProvider = new ConfigurationMapFeatureFlagProvider(appConfig);
2629
const featureManager = new FeatureManager(featureProvider);
2730
const isAlphaEnabled = await featureManager.isEnabled("Alpha");
@@ -54,13 +57,18 @@ Content of `sample.json`:
5457

5558
Load feature flags from `sample.json` file.
5659
```js
60+
import { FeatureManager, ConfigurationObjectFeatureFlagProvider } from "@microsoft/feature-management";
5761
const config = JSON.parse(await fs.readFile("path/to/sample.json"));
5862
const featureProvider = new ConfigurationObjectFeatureFlagProvider(config);
5963
const featureManager = new FeatureManager(featureProvider);
6064
const isAlphaEnabled = await featureManager.isEnabled("Alpha");
6165
console.log("Feature Alpha is:", isAlphaEnabled);
6266
```
6367

68+
## Examples
69+
70+
See code snippets under [examples/](./examples/) folder.
71+
6472
## Contributing
6573

6674
This project welcomes contributions and suggestions. Most contributions require you to agree to a

examples/express-app/README.md

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
# Examples for Microsoft Feature Management for JavaScript
2+
3+
These examples show how to use the Microsoft Feature Management in an express application.
4+
5+
## Prerequisites
6+
7+
The examples are compatible with [LTS versions of Node.js](https://github.com/nodejs/release#release-schedule).
8+
9+
## Setup & Run
10+
11+
1. Go to `src/feature-management` under the root folder and run:
12+
13+
```bash
14+
npm run install
15+
npm run build
16+
```
17+
18+
1. Go back to `examples/express-app` and install the dependencies using `npm`:
19+
20+
```bash
21+
npm install
22+
```
23+
24+
1. Run the examples:
25+
26+
```bash
27+
node server.mjs
28+
```
29+
30+
1. Visit `http://localhost:3000/Beta` and use `userId` and `groups` query to specify the targeting context (e.g. /Beta?userId=Jeff or /Beta?groups=Admin).
31+
32+
- If you are not targeted, you will get the message "Page not found".
33+
34+
- If you are targeted, you will get the message "Welcome to the Beta page!".
35+
36+
## Targeting
37+
38+
The targeting mechanism uses the `exampleTargetingContextAccessor` to extract the targeting context from the request. This function retrieves the userId and groups from the query parameters of the request.
39+
40+
```javascript
41+
const exampleTargetingContextAccessor = {
42+
getTargetingContext: () => {
43+
const req = requestAccessor.getStore();
44+
// read user and groups from request query data
45+
const { userId, groups } = req.query;
46+
// return aa ITargetingContext with the appropriate user info
47+
return { userId: userId, groups: groups ? groups.split(",") : [] };
48+
}
49+
};
50+
```
51+
52+
The `FeatureManager` is configured with this targeting context accessor:
53+
54+
```javascript
55+
const featureManager = new FeatureManager(
56+
featureProvider,
57+
{
58+
targetingContextAccessor: exampleTargetingContextAccessor
59+
}
60+
);
61+
```
62+
63+
This allows you to get ambient targeting context while doing feature flag evaluation.
64+
65+
### Request Accessor
66+
67+
The `requestAccessor` is an instance of `AsyncLocalStorage` from the `async_hooks` module. It is used to store the request object in asynchronous local storage, allowing it to be accessed throughout the lifetime of the request. This is particularly useful for accessing request-specific data in asynchronous operations. For more information, please go to https://nodejs.org/api/async_context.html
68+
69+
```javascript
70+
import { AsyncLocalStorage } from "async_hooks";
71+
const requestAccessor = new AsyncLocalStorage();
72+
```
73+
74+
Middleware is used to store the request object in the AsyncLocalStorage:
75+
76+
```javascript
77+
server.use((req, res, next) => {
78+
requestAccessor.run(req, next);
79+
});
80+
```

examples/express-app/config.json

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
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+
],
16+
"Groups": [
17+
{
18+
"Name": "Admin",
19+
"RolloutPercentage": 100
20+
}
21+
],
22+
"DefaultRolloutPercentage": 40
23+
}
24+
}
25+
}
26+
]
27+
}
28+
}
29+
]
30+
}
31+
}

examples/express-app/package.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"scripts": {
3+
"start": "node server.mjs"
4+
},
5+
"dependencies": {
6+
"@microsoft/feature-management": "../../src/feature-management",
7+
"express": "^4.21.2"
8+
}
9+
}

examples/express-app/server.mjs

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT license.
3+
4+
import fs from "fs/promises";
5+
import { ConfigurationObjectFeatureFlagProvider, FeatureManager } from "@microsoft/feature-management";
6+
// You can also use Azure App Configuration as the source of feature flags.
7+
// For more information, please go to quickstart: https://learn.microsoft.com/azure/azure-app-configuration/quickstart-feature-flag-javascript
8+
9+
const config = JSON.parse(await fs.readFile("config.json"));
10+
const featureProvider = new ConfigurationObjectFeatureFlagProvider(config);
11+
12+
// https://nodejs.org/api/async_context.html
13+
import { AsyncLocalStorage } from "async_hooks";
14+
const requestAccessor = new AsyncLocalStorage();
15+
const exampleTargetingContextAccessor = {
16+
getTargetingContext: () => {
17+
const req = requestAccessor.getStore();
18+
if (req === undefined) {
19+
return { userId: undefined, groups: [] };
20+
}
21+
// read user and groups from request query data
22+
const { userId, groups } = req.query;
23+
// return an ITargetingContext with the appropriate user info
24+
return { userId: userId, groups: groups ? groups.split(",") : [] };
25+
}
26+
};
27+
28+
const featureManager = new FeatureManager(
29+
featureProvider,
30+
{
31+
targetingContextAccessor: exampleTargetingContextAccessor
32+
}
33+
);
34+
35+
import express from "express";
36+
const server = express();
37+
const PORT = 3000;
38+
39+
// Use a middleware to store the request object in async local storage.
40+
// The async local storage allows the targeting context accessor to access the current request throughout its lifetime.
41+
// Middleware 1 (request object is stored in async local storage here and it will be available across the following chained async operations)
42+
// Middleware 2
43+
// Request Handler (feature flag evaluation happens here)
44+
server.use((req, res, next) => {
45+
requestAccessor.run(req, next);
46+
});
47+
48+
server.get("/", (req, res) => {
49+
res.send("Hello World!");
50+
});
51+
52+
server.get("/Beta", async (req, res) => {
53+
if (await featureManager.isEnabled("Beta")) {
54+
res.send("Welcome to the Beta page!");
55+
} else {
56+
res.status(404).send("Page not found");
57+
}
58+
});
59+
60+
// Start the server
61+
server.listen(PORT, () => {
62+
console.log(`Server is running at http://localhost:${PORT}`);
63+
});

src/feature-management-applicationinsights-browser/README.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
# Microsoft Feature Management Application Insights Plugin for Browser
22

3-
Feature Management Application Insights Plugin for Browser provides a solution for sending feature flag evaluation events produced by the Feature Management library.
3+
Feature Management Application Insights Plugin for Browser provides a solution for sending feature flag evaluation telemetry produced by the [`@microsoft/feature-management`](https://www.npmjs.com/package/@microsoft/feature-management) library.
44

55
## Getting Started
66

7+
For more information, please go to [Feature reference](https://learn.microsoft.com/azure/azure-app-configuration/feature-management-javascript-reference#application-insights-integration).
8+
79
### Usage
810

911
``` javascript
@@ -12,7 +14,7 @@ import { FeatureManager, ConfigurationObjectFeatureFlagProvider } from "@microso
1214
import { createTelemetryPublisher, trackEvent } from "@microsoft/feature-management-applicationinsights-browser";
1315

1416
const appInsights = new ApplicationInsights({ config: {
15-
connectionString: CONNECTION_STRING
17+
connectionString: "<APPINSIGHTS_CONNECTION_STRING>"
1618
}});
1719
appInsights.loadAppInsights();
1820

src/feature-management-applicationinsights-browser/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@microsoft/feature-management-applicationinsights-browser",
3-
"version": "2.0.0",
3+
"version": "2.0.2",
44
"description": "Feature Management Application Insights Plugin for Browser provides a solution for sending feature flag evaluation events produced by the Feature Management library.",
55
"main": "./dist/esm/index.js",
66
"module": "./dist/esm/index.js",
@@ -46,7 +46,7 @@
4646
},
4747
"dependencies": {
4848
"@microsoft/applicationinsights-web": "^3.3.2",
49-
"@microsoft/feature-management": "2.0.0"
49+
"@microsoft/feature-management": "2.0.2"
5050
}
5151
}
5252

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// Copyright (c) Microsoft Corporation.
22
// Licensed under the MIT license.
33

4-
export { createTelemetryPublisher, trackEvent, createTargetingTelemetryInitializer } from "./telemetry.js";
4+
export { createTargetingTelemetryInitializer, createTelemetryPublisher, trackEvent } from "./telemetry.js";
55
export { VERSION } from "./version.js";

src/feature-management-applicationinsights-browser/src/telemetry.ts

Lines changed: 3 additions & 3 deletions
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-
import { EvaluationResult, createFeatureEvaluationEventProperties, TargetingContextAccessor } from "@microsoft/feature-management";
4+
import { EvaluationResult, createFeatureEvaluationEventProperties, ITargetingContextAccessor } from "@microsoft/feature-management";
55
import { ApplicationInsights, IEventTelemetry, ITelemetryItem } from "@microsoft/applicationinsights-web";
66

77
const TARGETING_ID = "TargetingId";
@@ -45,9 +45,9 @@ export function trackEvent(client: ApplicationInsights, targetingId: string, eve
4545
* @param targetingContextAccessor The accessor function to get the targeting context.
4646
* @returns A telemetry initializer that attaches targeting id to telemetry items.
4747
*/
48-
export function createTargetingTelemetryInitializer(targetingContextAccessor: TargetingContextAccessor): (item: ITelemetryItem) => void {
48+
export function createTargetingTelemetryInitializer(targetingContextAccessor: ITargetingContextAccessor): (item: ITelemetryItem) => void {
4949
return (item: ITelemetryItem) => {
50-
const targetingContext = targetingContextAccessor();
50+
const targetingContext = targetingContextAccessor.getTargetingContext();
5151
if (targetingContext?.userId === undefined) {
5252
console.warn("Targeting id is undefined.");
5353
}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
// Copyright (c) Microsoft Corporation.
22
// Licensed under the MIT license.
33

4-
export const VERSION = "2.0.0";
4+
export const VERSION = "2.0.2";

0 commit comments

Comments
 (0)