diff --git a/src/AzureAppConfigurationImpl.ts b/src/AzureAppConfigurationImpl.ts index 916ece49..6d7a4a98 100644 --- a/src/AzureAppConfigurationImpl.ts +++ b/src/AzureAppConfigurationImpl.ts @@ -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}`; diff --git a/src/featureManagement/FeatureFlagOptions.ts b/src/featureManagement/FeatureFlagOptions.ts index eedf9ec7..55ceda4d 100644 --- a/src/featureManagement/FeatureFlagOptions.ts +++ b/src/featureManagement/FeatureFlagOptions.ts @@ -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[]; diff --git a/test/featureFlag.test.ts b/test/featureFlag.test.ts index 2544e617..2d6a7e02 100644 --- a/test/featureFlag.test.ts +++ b/test/featureFlag.test.ts @@ -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); @@ -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"}), @@ -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("feature_management").feature_flags).not.undefined; + // it should only load feature flags with no label by default + expect((settings.get("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("feature_management").feature_flags as any[]).find(ff => ff.id === "FlagWithTestLabel")).not.undefined; }); it("should not load feature flags if disabled", async () => { @@ -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, { diff --git a/test/utils/testHelper.ts b/test/utils/testHelper.ts index a5812694..85f7ac80 100644 --- a/test/utils/testHelper.ts +++ b/test/utils/testHelper.ts @@ -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"; @@ -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(),