diff --git a/sdk/appconfiguration/app-configuration/review/app-configuration-node.api.md b/sdk/appconfiguration/app-configuration/review/app-configuration-node.api.md index 02483bb17fb2..9d8bc9fc64cb 100644 --- a/sdk/appconfiguration/app-configuration/review/app-configuration-node.api.md +++ b/sdk/appconfiguration/app-configuration/review/app-configuration-node.api.md @@ -136,7 +136,7 @@ export const featureFlagPrefix = ".appconfig.featureflag/"; // @public export interface FeatureFlagValue { - conditions: { + conditions?: { clientFilters: { name: string; parameters?: Record; @@ -145,7 +145,7 @@ export interface FeatureFlagValue { }; description?: string; displayName?: string; - enabled: boolean; + enabled?: boolean; id?: string; } diff --git a/sdk/appconfiguration/app-configuration/src/featureFlag.ts b/sdk/appconfiguration/app-configuration/src/featureFlag.ts index 135a957afa82..ec5e10e0c150 100644 --- a/sdk/appconfiguration/app-configuration/src/featureFlag.ts +++ b/sdk/appconfiguration/app-configuration/src/featureFlag.ts @@ -30,7 +30,7 @@ export interface FeatureFlagValue { * * [More Info](https://learn.microsoft.com/azure/azure-app-configuration/howto-feature-filters-aspnet-core) */ - conditions: { + conditions?: { clientFilters: { name: string; parameters?: Record }[]; requirementType?: "All" | "Any"; }; @@ -41,7 +41,7 @@ export interface FeatureFlagValue { /** * Boolean flag to say if the feature flag is enabled. */ - enabled: boolean; + enabled?: boolean; /** * Display name for the feature to use for display rather than the ID. */ @@ -69,14 +69,18 @@ export const FeatureFlagHelper = { } const jsonFeatureFlagValue: JsonFeatureFlagValue = { id: featureFlag.value.id ?? key.replace(featureFlagPrefix, ""), - enabled: featureFlag.value.enabled, + enabled: featureFlag.value.enabled ?? false, description: featureFlag.value.description, - conditions: { + }; + + if (featureFlag.value.conditions) { + jsonFeatureFlagValue.conditions = { client_filters: featureFlag.value.conditions.clientFilters, requirement_type: featureFlag.value.conditions.requirementType ?? "Any", - }, - display_name: featureFlag.value.displayName, - }; + }; + } + + jsonFeatureFlagValue.display_name = featureFlag.value.displayName; const configSetting = { ...featureFlag, @@ -114,14 +118,19 @@ export function parseFeatureFlag( enabled: jsonFeatureFlagValue.enabled, description: jsonFeatureFlagValue.description, displayName: jsonFeatureFlagValue.display_name, - conditions: { clientFilters: jsonFeatureFlagValue.conditions.client_filters }, }, key, contentType: featureFlagContentType, }; - if (jsonFeatureFlagValue.conditions.requirement_type) { - featureflag.value.conditions.requirementType = jsonFeatureFlagValue.conditions.requirement_type; + if (jsonFeatureFlagValue.conditions) { + featureflag.value.conditions = { + clientFilters: jsonFeatureFlagValue.conditions.client_filters, + }; + if (jsonFeatureFlagValue.conditions.requirement_type) { + featureflag.value.conditions.requirementType = + jsonFeatureFlagValue.conditions.requirement_type; + } } return featureflag; } diff --git a/sdk/appconfiguration/app-configuration/src/internal/jsonModels.ts b/sdk/appconfiguration/app-configuration/src/internal/jsonModels.ts index 456e689afd71..fc5a9ca2dc9a 100644 --- a/sdk/appconfiguration/app-configuration/src/internal/jsonModels.ts +++ b/sdk/appconfiguration/app-configuration/src/internal/jsonModels.ts @@ -5,12 +5,12 @@ * @internal */ export type JsonFeatureFlagValue = { - conditions: { + conditions?: { client_filters: { name: string; parameters?: Record }[]; requirement_type?: "All" | "Any"; }; description?: string; - enabled: boolean; + enabled?: boolean; id?: string; display_name?: string; }; diff --git a/sdk/appconfiguration/app-configuration/test/public/featureFlagOptionalFields.spec.ts b/sdk/appconfiguration/app-configuration/test/public/featureFlagOptionalFields.spec.ts new file mode 100644 index 000000000000..70f0a1140efc --- /dev/null +++ b/sdk/appconfiguration/app-configuration/test/public/featureFlagOptionalFields.spec.ts @@ -0,0 +1,91 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import type { ConfigurationSetting } from "../../src/index.js"; +import { featureFlagContentType } from "../../src/index.js"; +import { parseFeatureFlag } from "../../src/featureFlag.js"; +import { describe, it, assert } from "vitest"; + +describe("FeatureFlag - Optional Fields", () => { + it("should parse feature flag with only id field", () => { + const minimalFeatureFlag = { + key: ".appconfig.featureflag/minimal-flag", + value: JSON.stringify({ id: "minimal-flag" }), + contentType: featureFlagContentType, + } as ConfigurationSetting; + + const parsed = parseFeatureFlag(minimalFeatureFlag); + assert.equal(parsed.value.id, "minimal-flag"); + assert.isUndefined(parsed.value.enabled); + assert.isUndefined(parsed.value.conditions); + assert.isUndefined(parsed.value.description); + assert.isUndefined(parsed.value.displayName); + }); + + it("should parse feature flag without conditions", () => { + const featureFlagWithoutConditions = { + key: ".appconfig.featureflag/no-conditions", + value: JSON.stringify({ + id: "no-conditions", + enabled: true, + description: "A feature flag without conditions", + display_name: "No Conditions Flag", + }), + contentType: featureFlagContentType, + } as ConfigurationSetting; + + const parsed = parseFeatureFlag(featureFlagWithoutConditions); + assert.equal(parsed.value.id, "no-conditions"); + assert.equal(parsed.value.enabled, true); + assert.equal(parsed.value.description, "A feature flag without conditions"); + assert.equal(parsed.value.displayName, "No Conditions Flag"); + assert.isUndefined(parsed.value.conditions); + }); + + it("should parse feature flag with empty conditions", () => { + const featureFlagWithEmptyConditions = { + key: ".appconfig.featureflag/empty-conditions", + value: JSON.stringify({ + id: "empty-conditions", + enabled: false, + conditions: { + client_filters: [], + }, + }), + contentType: featureFlagContentType, + } as ConfigurationSetting; + + const parsed = parseFeatureFlag(featureFlagWithEmptyConditions); + assert.equal(parsed.value.id, "empty-conditions"); + assert.equal(parsed.value.enabled, false); + assert.isDefined(parsed.value.conditions); + assert.deepEqual(parsed.value.conditions?.clientFilters, []); + assert.isUndefined(parsed.value.conditions?.requirementType); + }); + + it("should parse feature flag with conditions and requirement_type", () => { + const fullFeatureFlag = { + key: ".appconfig.featureflag/full-flag", + value: JSON.stringify({ + id: "full-flag", + enabled: true, + description: "Full feature flag", + display_name: "Full Flag", + conditions: { + client_filters: [{ name: "Filter1", parameters: { key: "value" } }, { name: "Filter2" }], + requirement_type: "All", + }, + }), + contentType: featureFlagContentType, + } as ConfigurationSetting; + + const parsed = parseFeatureFlag(fullFeatureFlag); + assert.equal(parsed.value.id, "full-flag"); + assert.equal(parsed.value.enabled, true); + assert.equal(parsed.value.description, "Full feature flag"); + assert.equal(parsed.value.displayName, "Full Flag"); + assert.isDefined(parsed.value.conditions); + assert.equal(parsed.value.conditions?.clientFilters.length, 2); + assert.equal(parsed.value.conditions?.requirementType, "All"); + }); +});