Skip to content

Commit 8afb542

Browse files
ChristineWanjaucwanjau
andauthored
Update feature flag equality check (#17)
* Update feature flag equality check * code improvements * fix linting issue * Add tests * address comments * avoid defining function --------- Co-authored-by: cwanjau <cwanjau@microsoft.com>
1 parent 334b430 commit 8afb542

File tree

2 files changed

+196
-13
lines changed

2 files changed

+196
-13
lines changed

libraries/azure-app-configuration-importer/src/internal/utils.ts

Lines changed: 30 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import { SourceOptions } from "../importOptions";
1313
import { ConfigurationFormat, ConfigurationProfile } from "../enums";
1414
import { ArgumentError, ArgumentNullError } from "../errors";
1515
import { Constants } from "../internal/constants";
16-
import { MsFeatureFlagValue } from "../featureFlag";
16+
import { MsFeatureFlagValue, Variant } from "../featureFlag";
1717

1818
/** @internal*/
1919
export function isJsonContentType(contentType?: string): boolean {
@@ -125,17 +125,17 @@ export function validateOptions(options: SourceOptions): void {
125125
}
126126
}
127127

128-
function isFeatureFlagValueEqual(valueA: string | FeatureFlagValue, valueB: string): boolean {
129-
let featureFlagAValue: FeatureFlagValue;
128+
function isFeatureFlagValueEqual(valueA: string | MsFeatureFlagValue, valueB: string): boolean {
129+
let featureFlagAValue: MsFeatureFlagValue;
130130

131131
if (typeof valueA == "string") {
132-
featureFlagAValue = toFeatureFlagValue(valueA);
132+
featureFlagAValue = toMsFeatureFlagValue(valueA);
133133
}
134134
else {
135-
featureFlagAValue = valueA;
135+
featureFlagAValue = valueA as MsFeatureFlagValue;
136136
}
137137

138-
const featureFlagBValue: FeatureFlagValue = toFeatureFlagValue(valueB);
138+
const featureFlagBValue: MsFeatureFlagValue = toMsFeatureFlagValue(valueB);
139139

140140
if (Object.keys(featureFlagAValue).length !== Object.keys(featureFlagBValue).length) {
141141
return false;
@@ -144,31 +144,48 @@ function isFeatureFlagValueEqual(valueA: string | FeatureFlagValue, valueB: stri
144144
return featureFlagAValue.id == featureFlagBValue.id &&
145145
featureFlagAValue.enabled == featureFlagBValue.enabled &&
146146
featureFlagAValue.description == featureFlagBValue.description &&
147-
areFeatureFlagFiltersEqual(featureFlagAValue.conditions.clientFilters, featureFlagBValue.conditions.clientFilters);
147+
areArrayEqual<FeatureFlagClientFilters>(featureFlagAValue.conditions.clientFilters, featureFlagBValue.conditions.clientFilters) &&
148+
isEqual(featureFlagAValue.allocation, featureFlagBValue.allocation) &&
149+
areArrayEqual<Variant>(featureFlagAValue.variants ?? [], featureFlagBValue.variants ?? []) &&
150+
isEqual(featureFlagAValue.telemetry, featureFlagBValue.telemetry);
148151
}
149152

150-
function areFeatureFlagFiltersEqual(filterA: FeatureFlagClientFilters[], filterB: FeatureFlagClientFilters[]): boolean {
151-
if (filterA.length !== filterB.length) {
153+
function areArrayEqual<T>(arrayA: T[], arrayB: T[]): boolean {
154+
if (arrayA.length !== arrayB.length) {
152155
return false;
153156
}
154157

155-
for (let i = 0; i < filterA.length; i++) {
156-
if (!isEqual(filterA[i], filterB[i])) {
158+
for (let i = 0; i < arrayA.length; i++) {
159+
if (!isEqual(arrayA[i], arrayB[i])) {
157160
return false;
158161
}
159162
}
160163
return true;
161164
}
162165

163-
function toFeatureFlagValue(value: string): FeatureFlagValue {
166+
function toMsFeatureFlagValue(value: string): MsFeatureFlagValue {
164167
const parsedJson: any = JSON.parse(value);
165168

166-
return {
169+
const msFeatureFlagValue: MsFeatureFlagValue = {
167170
id: parsedJson.id,
168171
enabled: parsedJson.enabled,
169172
description:parsedJson.description,
170173
conditions: isEmpty(parsedJson.conditions) ? {clientFilters: []} : {clientFilters: parsedJson.conditions.client_filters}
171174
};
175+
176+
if (parsedJson.allocation) {
177+
msFeatureFlagValue.allocation = parsedJson.allocation;
178+
}
179+
180+
if (parsedJson.variants) {
181+
msFeatureFlagValue.variants = parsedJson.variants;
182+
}
183+
184+
if (parsedJson.telemetry) {
185+
msFeatureFlagValue.telemetry = parsedJson.telemetry;
186+
}
187+
188+
return msFeatureFlagValue;
172189
}
173190

174191
export function serializeFeatureFlagValue(featureFlagValue: MsFeatureFlagValue): string {

libraries/azure-app-configuration-importer/tests/util.spec.ts

Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import { assert } from "chai";
55
import { areTagsEqual, isJsonContentType, isConfigSettingEqual } from "../src/internal/utils";
66
import { ConfigurationSetting, FeatureFlagValue, SetConfigurationSettingParam } from "@azure/app-configuration";
7+
import { MsFeatureFlagValue } from "../src/featureFlag";
78

89
describe("Test the utility methods", () => {
910
it("Determine the content type is json contentType", async () => {
@@ -244,9 +245,92 @@ describe("Test the utility methods", () => {
244245
isReadOnly: true
245246
};
246247

248+
const testKeyValue7: SetConfigurationSettingParam<MsFeatureFlagValue> = {
249+
key: "FeatureX",
250+
label: "test",
251+
value: {
252+
id: "time001",
253+
enabled: true,
254+
description: "",
255+
conditions: {
256+
clientFilters: []
257+
},
258+
allocation: {
259+
percentile: [
260+
{
261+
variant: "Off",
262+
from: 0,
263+
to: 23
264+
},
265+
{
266+
variant: "On",
267+
from: 23,
268+
to: 100
269+
}
270+
],
271+
group: [
272+
{
273+
variant: "On",
274+
groups: [
275+
"m1"
276+
]
277+
},
278+
{
279+
variant: "Off",
280+
groups: [
281+
"m2",
282+
"m3"
283+
]
284+
}
285+
],
286+
user: [
287+
{
288+
variant: "Off",
289+
users: [
290+
"user1",
291+
"user3"
292+
]
293+
},
294+
{
295+
variant: "On",
296+
users: [
297+
"user2"
298+
]
299+
}
300+
],
301+
seed: "bcngrfgnfgn",
302+
default_when_enabled: "On",
303+
default_when_disabled: "On"
304+
},
305+
variants: [
306+
{
307+
name: "Off",
308+
configuration_value: false
309+
},
310+
{
311+
name: "On",
312+
configuration_value: true
313+
}
314+
]
315+
},
316+
contentType: "application/vnd.microsoft.appconfig.ff+json;charset=utf-8",
317+
tags: {tag1: "tag1"}
318+
};
319+
320+
const testKeyValue8: ConfigurationSetting = {
321+
key: "FeatureX",
322+
label: "test",
323+
value: "{\"id\":\"time001\",\"enabled\":true,\"description\":\"\",\"conditions\":{\"client_filters\":[]},\"allocation\":{\"percentile\":[{\"variant\":\"Off\",\"from\":0,\"to\":23},{\"variant\":\"On\",\"from\":23,\"to\":100}],\"group\":[{\"variant\":\"On\",\"groups\":[\"m1\"]},{\"variant\":\"Off\",\"groups\":[\"m2\",\"m3\"]}],\"user\":[{\"variant\":\"Off\",\"users\":[\"user1\",\"user3\"]},{\"variant\":\"On\",\"users\":[\"user2\"]}],\"seed\":\"bcngrfgnfgn\",\"default_when_enabled\":\"On\",\"default_when_disabled\":\"On\"},\"variants\":[{\"name\":\"Off\",\"configuration_value\":false},{\"name\":\"On\",\"configuration_value\":true}]}",
324+
contentType: "application/vnd.microsoft.appconfig.ff+json;charset=utf-8",
325+
tags: {tag1: "tag1"},
326+
isReadOnly: true
327+
};
328+
329+
247330
assert.isTrue(isConfigSettingEqual(testKeyValue1, testKeyValue2));
248331
assert.isTrue(isConfigSettingEqual(testKeyValue3, testKeyValue4));
249332
assert.isTrue(isConfigSettingEqual(testKeyValue5, testKeyValue6));
333+
assert.isTrue(isConfigSettingEqual(testKeyValue7, testKeyValue8));
250334
});
251335

252336
it("Determine if feature flag values with different values are not equal", async()=> {
@@ -339,9 +423,91 @@ describe("Test the utility methods", () => {
339423
isReadOnly: true
340424
};
341425

426+
const testKeyValue9: SetConfigurationSettingParam<MsFeatureFlagValue> = {
427+
key: "FeatureX",
428+
label: "test",
429+
value: {
430+
id: "time001",
431+
enabled: true,
432+
description: "",
433+
conditions: {
434+
clientFilters: []
435+
},
436+
allocation: {
437+
percentile: [
438+
{
439+
variant: "Off",
440+
from: 0,
441+
to: 23
442+
},
443+
{
444+
variant: "On",
445+
from: 23,
446+
to: 100
447+
}
448+
],
449+
group: [
450+
{
451+
variant: "On",
452+
groups: [
453+
"m1"
454+
]
455+
},
456+
{
457+
variant: "Off",
458+
groups: [
459+
"m2",
460+
"m3"
461+
]
462+
}
463+
],
464+
user: [
465+
{
466+
variant: "Off",
467+
users: [
468+
"user1",
469+
"user3"
470+
]
471+
},
472+
{
473+
variant: "On",
474+
users: [
475+
"user2"
476+
]
477+
}
478+
],
479+
seed: "bcngrfgnfgn",
480+
default_when_enabled: "On",
481+
default_when_disabled: "On"
482+
},
483+
variants: [
484+
{
485+
name: "Off",
486+
configuration_value: false
487+
},
488+
{
489+
name: "On",
490+
configuration_value: true
491+
}
492+
]
493+
},
494+
contentType: "application/vnd.microsoft.appconfig.ff+json;charset=utf-8",
495+
tags: { tag1: "tag1" }
496+
};
497+
498+
const testKeyValue10: ConfigurationSetting = {
499+
key: "FeatureX",
500+
label: "test",
501+
value: "{\"id\":\"time001\",\"enabled\":true,\"description\":\"\",\"conditions\":{\"client_filters\":[]},\"allocation\":{\"percentile\":[{\"variant\":\"Off\",\"from\":0,\"to\":23},{\"variant\":\"On\",\"from\":23,\"to\":100}],\"group\":[{\"variant\":\"On\",\"groups\":[\"m1\"]},{\"variant\":\"Off\",\"groups\":[\"m2\",\"m3\"]}],\"user\":[{\"variant\":\"Off\",\"users\":[\"user1\",\"user3\"]},{\"variant\":\"On\",\"users\":[\"user2\"]}],\"seed\":\"bcngrfgnfgn\",\"default_when_enabled\":\"On\",\"default_when_disabled\":\"On\"},\"variants\":[{\"name\":\"Off\",\"configuration_value\":false},{\"name\":\"On\",\"configuration_value\":true,\"status_override\":\"None\"}]}",
502+
contentType: "application/vnd.microsoft.appconfig.ff+json;charset=utf-8",
503+
tags: {},
504+
isReadOnly: true
505+
};
506+
342507
assert.isFalse(isConfigSettingEqual(testKeyValue1, testKeyValue2));
343508
assert.isFalse(isConfigSettingEqual(testKeyValue3, testKeyValue4));
344509
assert.isFalse(isConfigSettingEqual(testKeyValue5, testKeyValue6));
345510
assert.isFalse(isConfigSettingEqual(testKeyValue7, testKeyValue8));
511+
assert.isFalse(isConfigSettingEqual(testKeyValue9, testKeyValue10));
346512
});
347513
});

0 commit comments

Comments
 (0)