diff --git a/.gitignore b/.gitignore index 5fd795e..250bd47 100644 --- a/.gitignore +++ b/.gitignore @@ -409,7 +409,7 @@ types/ *.tgz # examples -examples/package-lock.json +examples/**/**/package-lock.json # playwright test result test-results diff --git a/README.md b/README.md index 54aa97a..7ddcdc6 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ The App Configuration JavaScript provider provides feature flags in as a `Map` o A builtin `ConfigurationMapFeatureFlagProvider` helps to load feature flags in this case. ```js -const appConfig = load(connectionString, {featureFlagOptions}); // load feature flags from Azure App Configuration service +const appConfig = await load(connectionString, {featureFlagOptions}); // load feature flags from Azure App Configuration service const featureProvider = new ConfigurationMapFeatureFlagProvider(appConfig); const featureManager = new FeatureManager(featureProvider); const isAlphaEnabled = await featureManager.isEnabled("Alpha"); @@ -42,7 +42,7 @@ Content of `sample.json`: { "id": "Alpha", "description": "", - "enabled": "true", + "enabled": true, "conditions": { "client_filters": [] } diff --git a/examples/console-app/README.md b/examples/console-app/README.md new file mode 100644 index 0000000..1aa576e --- /dev/null +++ b/examples/console-app/README.md @@ -0,0 +1,23 @@ +# Examples for Microsoft Feature Management for JavaScript + +These examples show how to use the Microsoft Feature Management in some common scenarios. + +## Prerequisites + +The examples are compatible with [LTS versions of Node.js](https://github.com/nodejs/release#release-schedule). + +Some examples use `@azure/app-configuration-provider` to load feature flags from the [Azure App Configuration](https://learn.microsoft.com/azure/azure-app-configuration/overview). Azure App Configuration provides a service to centrally manage application settings and feature flags. + +## Setup & Run + +1. Install the dependencies using `npm`: + + ``` bash + npm install + ``` + +1. Run the examples: + + ``` bash + node featureFlagSample.mjs + ``` diff --git a/examples/console-app/config.json b/examples/console-app/config.json new file mode 100644 index 0000000..702abcd --- /dev/null +++ b/examples/console-app/config.json @@ -0,0 +1,62 @@ +{ + "feature_management": { + "feature_flags": [ + { + "id": "FeatureX", + "enabled": true + }, + { + "id": "FeatureY", + "enabled": false + }, + { + "id": "FeatureFlagWithTimeWindowFilter", + "enabled": true, + "conditions": { + "client_filters": [ + { + "name": "Microsoft.TimeWindow", + "parameters": { + "Start": "Thu, 15 Aug 2024 00:00:00 GMT", + "End": "Mon, 19 Aug 2024 00:00:00 GMT" + } + } + ] + } + }, + { + "id": "FeatureFlagWithTargetingFilter", + "enabled": true, + "conditions": { + "client_filters": [ + { + "name": "Microsoft.Targeting", + "parameters": { + "Audience": { + "Users": [ + "Jeff" + ], + "Groups": [ + { + "Name": "Admin", + "RolloutPercentage": 100 + } + ], + "DefaultRolloutPercentage": 40, + "Exclusion": { + "Users": [ + "Anne" + ], + "Groups": [ + "Guest" + ] + } + } + } + } + ] + } + } + ] + } +} \ No newline at end of file diff --git a/examples/console-app/featureFlagSample.mjs b/examples/console-app/featureFlagSample.mjs new file mode 100644 index 0000000..3de5e9e --- /dev/null +++ b/examples/console-app/featureFlagSample.mjs @@ -0,0 +1,29 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import fs from 'node:fs/promises'; +import { ConfigurationObjectFeatureFlagProvider, FeatureManager } from "@microsoft/feature-management"; + +const config = JSON.parse(await fs.readFile("config.json")); +const featureProvider = new ConfigurationObjectFeatureFlagProvider(config); +const featureManager = new FeatureManager(featureProvider); + +console.log("FeatureX is:", await featureManager.isEnabled("FeatureX")); +console.log("FeatureY is:", await featureManager.isEnabled("FeatureY")); + +// Is true between 2024-8-15 ~ 2024-8-19 +console.log("Feature flag with Time WindoW Filter is:", await featureManager.isEnabled("FeatureFlagWithTimeWindowFilter")); + +// Targeted by Users +console.log("Feature flag with Targeting Filter is:", await featureManager.isEnabled("FeatureFlagWithTargetingFilter", {userId: "Jeff"})) +// Excluded by Users +console.log("Feature flag with Targeting Filter is:", await featureManager.isEnabled("FeatureFlagWithTargetingFilter", {userId: "Anne"})) +// Targeted by Groups Admin +console.log("Feature flag with Targeting Filter is:", await featureManager.isEnabled("FeatureFlagWithTargetingFilter", {userId: "Admin1", groups: ["Admin"]})) +// Excluded by Groups Guest +console.log("Feature flag with Targeting Filter is:", await featureManager.isEnabled("FeatureFlagWithTargetingFilter", {userId: "Guest1", groups: ["Guest"]})) + +// Targeted by default rollout percentage +console.log("Feature flag with Targeting Filter is:", await featureManager.isEnabled("FeatureFlagWithTargetingFilter", {userId: "Alicia"})) +console.log("Feature flag with Targeting Filter is:", await featureManager.isEnabled("FeatureFlagWithTargetingFilter", {userId: "Susan"})) +console.log("Feature flag with Targeting Filter is:", await featureManager.isEnabled("FeatureFlagWithTargetingFilter", {userId: "John"})) diff --git a/examples/console-app/featureFlagSampleWithAppConfig.mjs b/examples/console-app/featureFlagSampleWithAppConfig.mjs new file mode 100644 index 0000000..0255278 --- /dev/null +++ b/examples/console-app/featureFlagSampleWithAppConfig.mjs @@ -0,0 +1,23 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import { load } from "@azure/app-configuration-provider"; +import { ConfigurationMapFeatureFlagProvider, FeatureManager } from "@microsoft/feature-management"; + +const connectionString = ""; +const appConfig = await load(connectionString, { + featureFlagOptions: { + enabled: true, + selectors: [{ + keyFilter: "*" + }], + refresh: { + enabled: true + } + } +}); + +const featureProvider = new ConfigurationMapFeatureFlagProvider(appConfig); +const featureManager = new FeatureManager(featureProvider); + +console.log("Feature Beta is:", await featureManager.isEnabled("Beta")); diff --git a/examples/console-app/package.json b/examples/console-app/package.json new file mode 100644 index 0000000..c43ceb2 --- /dev/null +++ b/examples/console-app/package.json @@ -0,0 +1,6 @@ +{ + "dependencies": { + "@azure/app-configuration-provider": "latest", + "@microsoft/feature-management": "latest" + } +} diff --git a/sdk/feature-management/package-lock.json b/sdk/feature-management/package-lock.json index 0988ed1..0bc8262 100644 --- a/sdk/feature-management/package-lock.json +++ b/sdk/feature-management/package-lock.json @@ -1,12 +1,12 @@ { "name": "@microsoft/feature-management", - "version": "1.0.0-preview", + "version": "1.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@microsoft/feature-management", - "version": "1.0.0-preview", + "version": "1.0.0", "license": "MIT", "devDependencies": { "@playwright/test": "^1.46.1", @@ -530,9 +530,9 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.9.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.9.4.tgz", - "integrity": "sha512-ub/SN3yWqIv5CWiAZPHVS1DloyZsJbtXmX4HxUTIpS0BHm9pW5iYBo2mIZi+hE3AeiTzHz33blwSnhdUo+9NpA==", + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.22.4.tgz", + "integrity": "sha512-Fxamp4aEZnfPOcGA8KSNEohV8hX7zVHOemC8jVBoBUHu5zpJK/Eu3uJwt6BMgy9fkvzxDaurgj96F/NiLukF2w==", "cpu": [ "arm" ], @@ -543,9 +543,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.9.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.9.4.tgz", - "integrity": "sha512-ehcBrOR5XTl0W0t2WxfTyHCR/3Cq2jfb+I4W+Ch8Y9b5G+vbAecVv0Fx/J1QKktOrgUYsIKxWAKgIpvw56IFNA==", + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.22.4.tgz", + "integrity": "sha512-VXoK5UMrgECLYaMuGuVTOx5kcuap1Jm8g/M83RnCHBKOqvPPmROFJGQaZhGccnsFtfXQ3XYa4/jMCJvZnbJBdA==", "cpu": [ "arm64" ], @@ -556,9 +556,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.9.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.9.4.tgz", - "integrity": "sha512-1fzh1lWExwSTWy8vJPnNbNM02WZDS8AW3McEOb7wW+nPChLKf3WG2aG7fhaUmfX5FKw9zhsF5+MBwArGyNM7NA==", + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.22.4.tgz", + "integrity": "sha512-xMM9ORBqu81jyMKCDP+SZDhnX2QEVQzTcC6G18KlTQEzWK8r/oNZtKuZaCcHhnsa6fEeOBionoyl5JsAbE/36Q==", "cpu": [ "arm64" ], @@ -569,9 +569,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.9.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.9.4.tgz", - "integrity": "sha512-Gc6cukkF38RcYQ6uPdiXi70JB0f29CwcQ7+r4QpfNpQFVHXRd0DfWFidoGxjSx1DwOETM97JPz1RXL5ISSB0pA==", + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.22.4.tgz", + "integrity": "sha512-aJJyYKQwbHuhTUrjWjxEvGnNNBCnmpHDvrb8JFDbeSH3m2XdHcxDd3jthAzvmoI8w/kSjd2y0udT+4okADsZIw==", "cpu": [ "x64" ], @@ -582,9 +582,22 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.9.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.9.4.tgz", - "integrity": "sha512-g21RTeFzoTl8GxosHbnQZ0/JkuFIB13C3T7Y0HtKzOXmoHhewLbVTFBQZu+z5m9STH6FZ7L/oPgU4Nm5ErN2fw==", + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.22.4.tgz", + "integrity": "sha512-j63YtCIRAzbO+gC2L9dWXRh5BFetsv0j0va0Wi9epXDgU/XUi5dJKo4USTttVyK7fGw2nPWK0PbAvyliz50SCQ==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.22.4.tgz", + "integrity": "sha512-dJnWUgwWBX1YBRsuKKMOlXCzh2Wu1mlHzv20TpqEsfdZLb3WoJW2kIEsGwLkroYf24IrPAvOT/ZQ2OYMV6vlrg==", "cpu": [ "arm" ], @@ -595,9 +608,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.9.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.9.4.tgz", - "integrity": "sha512-TVYVWD/SYwWzGGnbfTkrNpdE4HON46orgMNHCivlXmlsSGQOx/OHHYiQcMIOx38/GWgwr/po2LBn7wypkWw/Mg==", + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.22.4.tgz", + "integrity": "sha512-AdPRoNi3NKVLolCN/Sp4F4N1d98c4SBnHMKoLuiG6RXgoZ4sllseuGioszumnPGmPM2O7qaAX/IJdeDU8f26Aw==", "cpu": [ "arm64" ], @@ -608,9 +621,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.9.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.9.4.tgz", - "integrity": "sha512-XcKvuendwizYYhFxpvQ3xVpzje2HHImzg33wL9zvxtj77HvPStbSGI9czrdbfrf8DGMcNNReH9pVZv8qejAQ5A==", + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.22.4.tgz", + "integrity": "sha512-Gl0AxBtDg8uoAn5CCqQDMqAx22Wx22pjDOjBdmG0VIWX3qUBHzYmOKh8KXHL4UpogfJ14G4wk16EQogF+v8hmA==", "cpu": [ "arm64" ], @@ -620,10 +633,23 @@ "linux" ] }, + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.22.4.tgz", + "integrity": "sha512-3aVCK9xfWW1oGQpTsYJJPF6bfpWfhbRnhdlyhak2ZiyFLDaayz0EP5j9V1RVLAAxlmWKTDfS9wyRyY3hvhPoOg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.9.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.9.4.tgz", - "integrity": "sha512-LFHS/8Q+I9YA0yVETyjonMJ3UA+DczeBd/MqNEzsGSTdNvSJa1OJZcSH8GiXLvcizgp9AlHs2walqRcqzjOi3A==", + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.22.4.tgz", + "integrity": "sha512-ePYIir6VYnhgv2C5Xe9u+ico4t8sZWXschR6fMgoPUK31yQu7hTEJb7bCqivHECwIClJfKgE7zYsh1qTP3WHUA==", "cpu": [ "riscv64" ], @@ -633,10 +659,23 @@ "linux" ] }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.22.4.tgz", + "integrity": "sha512-GqFJ9wLlbB9daxhVlrTe61vJtEY99/xB3C8e4ULVsVfflcpmR6c8UZXjtkMA6FhNONhj2eA5Tk9uAVw5orEs4Q==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.9.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.9.4.tgz", - "integrity": "sha512-dIYgo+j1+yfy81i0YVU5KnQrIJZE8ERomx17ReU4GREjGtDW4X+nvkBak2xAUpyqLs4eleDSj3RrV72fQos7zw==", + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.22.4.tgz", + "integrity": "sha512-87v0ol2sH9GE3cLQLNEy0K/R0pz1nvg76o8M5nhMR0+Q+BBGLnb35P0fVz4CQxHYXaAOhE8HhlkaZfsdUOlHwg==", "cpu": [ "x64" ], @@ -647,9 +686,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.9.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.9.4.tgz", - "integrity": "sha512-RoaYxjdHQ5TPjaPrLsfKqR3pakMr3JGqZ+jZM0zP2IkDtsGa4CqYaWSfQmZVgFUCgLrTnzX+cnHS3nfl+kB6ZQ==", + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.22.4.tgz", + "integrity": "sha512-UV6FZMUgePDZrFjrNGIWzDo/vABebuXBhJEqrHxrGiU6HikPy0Z3LfdtciIttEUQfuDdCn8fqh7wiFJjCNwO+g==", "cpu": [ "x64" ], @@ -660,9 +699,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.9.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.9.4.tgz", - "integrity": "sha512-T8Q3XHV+Jjf5e49B4EAaLKV74BbX7/qYBRQ8Wop/+TyyU0k+vSjiLVSHNWdVd1goMjZcbhDmYZUYW5RFqkBNHQ==", + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.22.4.tgz", + "integrity": "sha512-BjI+NVVEGAXjGWYHz/vv0pBqfGoUH0IGZ0cICTn7kB9PyjrATSkX+8WkguNjWoj2qSr1im/+tTGRaY+4/PdcQw==", "cpu": [ "arm64" ], @@ -673,9 +712,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.9.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.9.4.tgz", - "integrity": "sha512-z+JQ7JirDUHAsMecVydnBPWLwJjbppU+7LZjffGf+Jvrxq+dVjIE7By163Sc9DKc3ADSU50qPVw0KonBS+a+HQ==", + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.22.4.tgz", + "integrity": "sha512-SiWG/1TuUdPvYmzmYnmd3IEifzR61Tragkbx9D3+R8mzQqDBz8v+BvZNDlkiTtI9T15KYZhP0ehn3Dld4n9J5g==", "cpu": [ "ia32" ], @@ -686,9 +725,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.9.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.9.4.tgz", - "integrity": "sha512-LfdGXCV9rdEify1oxlN9eamvDSjv9md9ZVMAbNHA87xqIfFCxImxan9qZ8+Un54iK2nnqPlbnSi4R54ONtbWBw==", + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.22.4.tgz", + "integrity": "sha512-j8pPKp53/lq9lMXN57S8cFz0MynJk8OWNuUnXct/9KCpKU7DgU3bYMJhwWmcqC0UU29p8Lr0/7KEVcaM6bf47Q==", "cpu": [ "x64" ], @@ -2689,9 +2728,9 @@ } }, "node_modules/rollup": { - "version": "4.9.4", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.9.4.tgz", - "integrity": "sha512-2ztU7pY/lrQyXSCnnoU4ICjT/tCG9cdH3/G25ERqE3Lst6vl2BCM5hL2Nw+sslAvAf+ccKsAq1SkKQALyqhR7g==", + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.22.4.tgz", + "integrity": "sha512-vD8HJ5raRcWOyymsR6Z3o6+RzfEPCnVLMFJ6vRslO1jt4LO6dUo5Qnpg7y4RkZFM2DMe3WUirkI5c16onjrc6A==", "dev": true, "dependencies": { "@types/estree": "1.0.5" @@ -2704,19 +2743,22 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.9.4", - "@rollup/rollup-android-arm64": "4.9.4", - "@rollup/rollup-darwin-arm64": "4.9.4", - "@rollup/rollup-darwin-x64": "4.9.4", - "@rollup/rollup-linux-arm-gnueabihf": "4.9.4", - "@rollup/rollup-linux-arm64-gnu": "4.9.4", - "@rollup/rollup-linux-arm64-musl": "4.9.4", - "@rollup/rollup-linux-riscv64-gnu": "4.9.4", - "@rollup/rollup-linux-x64-gnu": "4.9.4", - "@rollup/rollup-linux-x64-musl": "4.9.4", - "@rollup/rollup-win32-arm64-msvc": "4.9.4", - "@rollup/rollup-win32-ia32-msvc": "4.9.4", - "@rollup/rollup-win32-x64-msvc": "4.9.4", + "@rollup/rollup-android-arm-eabi": "4.22.4", + "@rollup/rollup-android-arm64": "4.22.4", + "@rollup/rollup-darwin-arm64": "4.22.4", + "@rollup/rollup-darwin-x64": "4.22.4", + "@rollup/rollup-linux-arm-gnueabihf": "4.22.4", + "@rollup/rollup-linux-arm-musleabihf": "4.22.4", + "@rollup/rollup-linux-arm64-gnu": "4.22.4", + "@rollup/rollup-linux-arm64-musl": "4.22.4", + "@rollup/rollup-linux-powerpc64le-gnu": "4.22.4", + "@rollup/rollup-linux-riscv64-gnu": "4.22.4", + "@rollup/rollup-linux-s390x-gnu": "4.22.4", + "@rollup/rollup-linux-x64-gnu": "4.22.4", + "@rollup/rollup-linux-x64-musl": "4.22.4", + "@rollup/rollup-win32-arm64-msvc": "4.22.4", + "@rollup/rollup-win32-ia32-msvc": "4.22.4", + "@rollup/rollup-win32-x64-msvc": "4.22.4", "fsevents": "~2.3.2" } }, diff --git a/sdk/feature-management/package.json b/sdk/feature-management/package.json index 7aac766..f7e5e01 100644 --- a/sdk/feature-management/package.json +++ b/sdk/feature-management/package.json @@ -1,6 +1,6 @@ { "name": "@microsoft/feature-management", - "version": "1.0.0-preview.1", + "version": "1.0.0", "description": "Feature Management is a library for enabling/disabling features at runtime. Developers can use feature flags in simple use cases like conditional statement to more advanced scenarios like conditionally adding routes.", "main": "./dist/commonjs/index.js", "module": "./dist/esm/index.js", @@ -42,7 +42,7 @@ "eslint": "^8.56.0", "mocha": "^10.2.0", "rimraf": "^5.0.5", - "rollup": "^4.9.4", + "rollup": "^4.22.4", "rollup-plugin-dts": "^6.1.0", "tslib": "^2.6.2", "typescript": "^5.3.3" diff --git a/sdk/feature-management/src/index.ts b/sdk/feature-management/src/index.ts index 81ba59a..d616a5b 100644 --- a/sdk/feature-management/src/index.ts +++ b/sdk/feature-management/src/index.ts @@ -4,3 +4,4 @@ export { FeatureManager, FeatureManagerOptions, EvaluationResult, VariantAssignmentReason } from "./featureManager.js"; export { ConfigurationMapFeatureFlagProvider, ConfigurationObjectFeatureFlagProvider, IFeatureFlagProvider } from "./featureProvider.js"; export { IFeatureFilter } from "./filter/FeatureFilter.js"; +export { VERSION } from "./version.js"; diff --git a/sdk/feature-management/src/version.ts b/sdk/feature-management/src/version.ts new file mode 100644 index 0000000..dcc0b29 --- /dev/null +++ b/sdk/feature-management/src/version.ts @@ -0,0 +1,4 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +export const VERSION = "1.0.0"; diff --git a/sdk/feature-management/test/browser/browser.test.ts b/sdk/feature-management/test/browser/browser.test.ts index e6b137c..b7e71be 100644 --- a/sdk/feature-management/test/browser/browser.test.ts +++ b/sdk/feature-management/test/browser/browser.test.ts @@ -7,16 +7,15 @@ test("Testcase can pass in browser environment", async ({ page }) => { const filePath = path.join(__dirname, "index.html"); let hasPageError = false; - page.on("pageerror", (err) => { hasPageError = true; console.log(`Page Error: ${err.message}`); }); await page.goto(`file:${filePath}`); + await page.waitForTimeout(10000); const failures = await page.evaluate(() => (window as any).mochaFailures); - chai.expect(failures).to.equal(0); chai.expect(hasPageError).to.be.false; }); diff --git a/sdk/feature-management/test/browser/index.html b/sdk/feature-management/test/browser/index.html index 57df7fd..5c07b7e 100644 --- a/sdk/feature-management/test/browser/index.html +++ b/sdk/feature-management/test/browser/index.html @@ -19,7 +19,7 @@ diff --git a/sdk/feature-management/test/browser/testcases.js b/sdk/feature-management/test/browser/testcases.js index ba865ed..a31cd04 100644 --- a/sdk/feature-management/test/browser/testcases.js +++ b/sdk/feature-management/test/browser/testcases.js @@ -1,63 +1,104 @@ const ConfigurationObjectFeatureFlagProvider = FeatureManagement.ConfigurationObjectFeatureFlagProvider; const FeatureManager = FeatureManagement.FeatureManager; -describe("feature manager", () => { - it("should load from json string", - async () => { - const jsonObject = { - "feature_management": { - "feature_flags": [ +const jsonObject = { + "feature_management": { + "feature_flags": [ + { + "id": "ComplexTargeting", + "description": "A feature flag using a targeting filter, that will return true for Alice, Stage1, and 50% of Stage2. Dave and Stage3 are excluded. The default rollout percentage is 25%.", + "enabled": true, + "conditions": { + "client_filters": [ { - "id": "ComplexTargeting", - "description": "A feature flag using a targeting filter, that will return true for Alice, Stage1, and 50% of Stage2. Dave and Stage3 are excluded. The default rollout percentage is 25%.", - "enabled": true, - "conditions": { - "client_filters": [ - { - "name": "Microsoft.Targeting", - "parameters": { - "Audience": { - "Users": [ - "Alice" - ], - "Groups": [ - { - "Name": "Stage1", - "RolloutPercentage": 100 - }, - { - "Name": "Stage2", - "RolloutPercentage": 50 - } - ], - "DefaultRolloutPercentage": 25, - "Exclusion": { - "Users": ["Dave"], - "Groups": ["Stage3"] - } - } + "name": "Microsoft.Targeting", + "parameters": { + "Audience": { + "Users": [ + "Alice" + ], + "Groups": [ + { + "Name": "Stage1", + "RolloutPercentage": 100 + }, + { + "Name": "Stage2", + "RolloutPercentage": 50 } + ], + "DefaultRolloutPercentage": 25, + "Exclusion": { + "Users": ["Dave"], + "Groups": ["Stage3"] } - ] + } } } ] } - }; - - const provider = new ConfigurationObjectFeatureFlagProvider(jsonObject); - const featureManager = new FeatureManager(provider); + } + ] + } +}; + +const provider = new ConfigurationObjectFeatureFlagProvider(jsonObject); +const featureManager = new FeatureManager(provider); + +describe("feature manager", () => { + it("should not target user Aiden in default rollout", + async () => { chai.expect(await featureManager.isEnabled("ComplexTargeting", { userId: "Aiden" })).to.eq(false); + } + ).timeout(1000);; + + it("should target user Bloosom in default rollout", + async () => { chai.expect(await featureManager.isEnabled("ComplexTargeting", { userId: "Blossom" })).to.eq(true); + } + ).timeout(1000);; + + it("should target user Alice", + async () => { chai.expect(await featureManager.isEnabled("ComplexTargeting", { userId: "Alice" })).to.eq(true); + } + ).timeout(1000);; + + it("should target user Aiden in group Stage1", + async () => { chai.expect(await featureManager.isEnabled("ComplexTargeting", { userId: "Aiden", groups: ["Stage1"] })).to.eq(true); + } + ).timeout(1000);; + + it("should not target user Dave in group Stage1", + async () => { + chai.expect(await featureManager.isEnabled("ComplexTargeting", { userId: "Dave", groups: ["Stage1"] })).to.eq(false); + } + ).timeout(1000);; + + it("should not target empty user in group Stage2", + async () => { chai.expect(await featureManager.isEnabled("ComplexTargeting", { groups: ["Stage2"] })).to.eq(false); + } + ).timeout(1000);; + + it("should target user Aiden in group Stage2", + async () => { chai.expect(await featureManager.isEnabled("ComplexTargeting", { userId: "Aiden", groups: ["Stage2"] })).to.eq(true); + } + ).timeout(1000);; + + it("should not target user Chris in group Stage2", + async () => { chai.expect(await featureManager.isEnabled("ComplexTargeting", { userId: "Chris", groups: ["Stage2"] })).to.eq(false); - chai.expect(await featureManager.isEnabled("ComplexTargeting", { groups: ["Stage3"] })).to.eq(false), + } + ).timeout(1000);; + + it("should not target group Stage3", + async () => { + chai.expect(await featureManager.isEnabled("ComplexTargeting", { groups: ["Stage3"] })).to.eq(false); chai.expect(await featureManager.isEnabled("ComplexTargeting", { userId: "Alice", groups: ["Stage3"] })).to.eq(false); chai.expect(await featureManager.isEnabled("ComplexTargeting", { userId: "Blossom", groups: ["Stage3"] })).to.eq(false); - chai.expect(await featureManager.isEnabled("ComplexTargeting", { userId: "Dave", groups: ["Stage1"] })).to.eq(false); } - ); + ).timeout(1000); });