Skip to content

Commit 3251fa8

Browse files
authored
Fb/scopemap complex entries (#16)
* new todo * fail validation for scopeMaps with complex entries
1 parent edc167e commit 3251fa8

File tree

3 files changed

+74
-17
lines changed

3 files changed

+74
-17
lines changed

src/featureToggles.js

Lines changed: 27 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -279,24 +279,35 @@ class FeatureToggles {
279279
return [{ featureKey, errorMessage: "feature key is not valid" }];
280280
}
281281

282-
if (!FeatureToggles._isValidScopeKey(scopeKey)) {
283-
return [{ featureKey, scopeKey, errorMessage: "scopeKey is not valid" }];
282+
if (scopeMap) {
283+
const allowedScopesCheckMap = this.__config[featureKey][CONFIG_KEY.ALLOWED_SCOPES_CHECK_MAP];
284+
for (const [scope, value] of Object.entries(scopeMap)) {
285+
if (allowedScopesCheckMap && !allowedScopesCheckMap[scope]) {
286+
return [
287+
{
288+
featureKey,
289+
scopeKey,
290+
errorMessage: 'scope "{0}" is not allowed',
291+
errorMessageValues: [scope],
292+
},
293+
];
294+
}
295+
const scopeType = typeof value;
296+
if (scopeType !== "string") {
297+
return [
298+
{
299+
featureKey,
300+
scopeKey,
301+
errorMessage: 'scope "{0}" has invalid type {1}, must be string',
302+
errorMessageValues: [scope, scopeType],
303+
},
304+
];
305+
}
306+
}
284307
}
285308

286-
const allowedScopesCheckMap = this.__config[featureKey][CONFIG_KEY.ALLOWED_SCOPES_CHECK_MAP];
287-
if (allowedScopesCheckMap && scopeKey !== undefined && scopeKey !== SCOPE_ROOT_KEY) {
288-
const actualScopes = Object.keys(scopeMap);
289-
const mismatch = actualScopes.find((scope) => !allowedScopesCheckMap[scope]);
290-
if (mismatch !== undefined) {
291-
return [
292-
{
293-
featureKey,
294-
scopeKey,
295-
errorMessage: 'scope "{0}" is not allowed',
296-
errorMessageValues: [mismatch],
297-
},
298-
];
299-
}
309+
if (!FeatureToggles._isValidScopeKey(scopeKey)) {
310+
return [{ featureKey, scopeKey, errorMessage: "scopeKey is not valid" }];
300311
}
301312

302313
// NOTE: value === null is our way of encoding featureKey resetting changes, so it is always allowed

test/featureToggles.test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -352,7 +352,7 @@ describe("feature toggles test", () => {
352352
await featureToggles.initializeFeatures({ config: mockConfig });
353353

354354
const inputArgsList = [
355-
[FEATURE.A, 1, { a: 1 }],
355+
[FEATURE.A, 1, { a: "1" }],
356356
[FEATURE.A, 1, "a::1"],
357357
[FEATURE.AA, true],
358358
[FEATURE.AA, true, { tenant: "t1", user: "u1" }],

test/integration-local/featureToggles.test.js

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,52 @@ describe("local integration test", () => {
357357
expect(redisWrapperLoggerSpy.error).toHaveBeenCalledTimes(0);
358358
});
359359

360+
it("validateFeatureValue with invalid scopes", async () => {
361+
// valid
362+
expect(await validateFeatureValue(FEATURE.C, "", { tenant: "t1" })).toMatchInlineSnapshot(`[]`);
363+
364+
// invalid
365+
expect(await validateFeatureValue(FEATURE.C, "", { tenant: { subTenant: "bla" } })).toMatchInlineSnapshot(`
366+
[
367+
{
368+
"errorMessage": "scope "{0}" has invalid type {1}, must be string",
369+
"errorMessageValues": [
370+
"tenant",
371+
"object",
372+
],
373+
"featureKey": "test/feature_c",
374+
"scopeKey": "tenant::[object Object]",
375+
},
376+
]
377+
`);
378+
expect(await validateFeatureValue(FEATURE.C, "", { tenant: ["a", "b", "c"] })).toMatchInlineSnapshot(`
379+
[
380+
{
381+
"errorMessage": "scope "{0}" has invalid type {1}, must be string",
382+
"errorMessageValues": [
383+
"tenant",
384+
"object",
385+
],
386+
"featureKey": "test/feature_c",
387+
"scopeKey": "tenant::a,b,c",
388+
},
389+
]
390+
`);
391+
expect(await validateFeatureValue(FEATURE.C, "", { tenant: () => "1" })).toMatchInlineSnapshot(`
392+
[
393+
{
394+
"errorMessage": "scope "{0}" has invalid type {1}, must be string",
395+
"errorMessageValues": [
396+
"tenant",
397+
"function",
398+
],
399+
"featureKey": "test/feature_c",
400+
"scopeKey": "tenant::() => "1"",
401+
},
402+
]
403+
`);
404+
});
405+
360406
it("registerFeatureValueValidation, validateFeatureValue", async () => {
361407
const successfulValidator = () => undefined;
362408
const failingValidator1 = () => {

0 commit comments

Comments
 (0)