Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/AzureAppConfigurationImpl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -898,8 +898,8 @@ function getValidKeyValueSelectors(selectors?: SettingSelector[]): SettingSelect

function getValidFeatureFlagSelectors(selectors?: SettingSelector[]): SettingSelector[] {
if (selectors === undefined || selectors.length === 0) {
// selectors must be explicitly provided.
throw new Error("Feature flag selectors must be provided.");
// Default selector: key: *, label: \0
return [{ keyFilter: `${featureFlagPrefix}${KeyFilter.Any}`, labelFilter: LabelFilter.Null }];
} else {
selectors.forEach(selector => {
selector.keyFilter = `${featureFlagPrefix}${selector.keyFilter}`;
Expand Down
5 changes: 2 additions & 3 deletions src/featureManagement/FeatureFlagOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,15 @@ import { SettingSelector } from "../types.js";
export interface FeatureFlagOptions {
/**
* Specifies whether feature flags will be loaded from Azure App Configuration.

*/
enabled: boolean;

/**
* Specifies the selectors used to filter feature flags.
* Specifies what feature flags to include in the configuration provider.
*
* @remarks
* keyFilter of selector will be prefixed with "appconfig.featureflag/" when request is sent.
* If no selectors are specified then no feature flags will be retrieved.
* If no selectors are specified then all feature flags with no label will be included.
*/
selectors?: SettingSelector[];

Expand Down
29 changes: 14 additions & 15 deletions test/featureFlag.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

import * as chai from "chai";
import * as chaiAsPromised from "chai-as-promised";
import { featureFlagContentType } from "@azure/app-configuration";
import { load } from "./exportedApi.js";
import { MAX_TIME_OUT, createMockedConnectionString, createMockedEndpoint, createMockedFeatureFlag, createMockedKeyValue, mockAppConfigurationClientListConfigurationSettings, restoreMocks } from "./utils/testHelper.js";
chai.use(chaiAsPromised);
Expand Down Expand Up @@ -49,9 +50,9 @@ const mockedKVs = [{
}, {
key: ".appconfig.featureflag/variant",
value: sampleVariantValue,
contentType: "application/vnd.microsoft.appconfig.ff+json;charset=utf-8",
contentType: featureFlagContentType,
}].map(createMockedKeyValue).concat([
createMockedFeatureFlag("Beta", { enabled: true }),
createMockedFeatureFlag("FlagWithTestLabel", { enabled: true }, {label: "Test"}),
createMockedFeatureFlag("Alpha_1", { enabled: true }),
createMockedFeatureFlag("Alpha_2", { enabled: false }),
createMockedFeatureFlag("Telemetry_1", { enabled: true, telemetry: { enabled: true } }, { etag: "ETag"}),
Expand Down Expand Up @@ -213,15 +214,22 @@ describe("feature flags", function () {
const connectionString = createMockedConnectionString();
const settings = await load(connectionString, {
featureFlagOptions: {
enabled: true,
selectors: [{
keyFilter: "*"
}]
enabled: true
}
});
expect(settings).not.undefined;
expect(settings.get("feature_management")).not.undefined;
expect(settings.get<any>("feature_management").feature_flags).not.undefined;
// it should only load feature flags with no label by default
expect((settings.get<any>("feature_management").feature_flags as any[]).find(ff => ff.id === "FlagWithTestLabel")).to.be.undefined;

const settings2 = await load(connectionString, {
featureFlagOptions: {
enabled: true,
selectors: [ { keyFilter: "*", labelFilter: "Test" } ]
}
});
expect((settings2.get<any>("feature_management").feature_flags as any[]).find(ff => ff.id === "FlagWithTestLabel")).not.undefined;
});

it("should not load feature flags if disabled", async () => {
Expand All @@ -242,15 +250,6 @@ describe("feature flags", function () {
expect(settings.get("feature_management")).undefined;
});

it("should throw error if selectors not specified", async () => {
const connectionString = createMockedConnectionString();
return expect(load(connectionString, {
featureFlagOptions: {
enabled: true
}
})).eventually.rejectedWith("Feature flag selectors must be provided.");
});

it("should load feature flags with custom selector", async () => {
const connectionString = createMockedConnectionString();
const settings = await load(connectionString, {
Expand Down
4 changes: 2 additions & 2 deletions test/utils/testHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// Licensed under the MIT license.

import * as sinon from "sinon";
import { AppConfigurationClient, ConfigurationSetting } from "@azure/app-configuration";
import { AppConfigurationClient, ConfigurationSetting, featureFlagContentType } from "@azure/app-configuration";
import { ClientSecretCredential } from "@azure/identity";
import { KeyVaultSecret, SecretClient } from "@azure/keyvault-secrets";
import * as uuid from "uuid";
Expand Down Expand Up @@ -240,7 +240,7 @@ const createMockedFeatureFlag = (name: string, flagProps?: any, props?: any) =>
"client_filters": []
}
}, flagProps)),
contentType: "application/vnd.microsoft.appconfig.ff+json;charset=utf-8",
contentType: featureFlagContentType,
lastModified: new Date(),
tags: {},
etag: uuid.v4(),
Expand Down