Skip to content

Commit cbc60e1

Browse files
committed
validate feature flag
1 parent 5733f56 commit cbc60e1

File tree

7 files changed

+424
-208
lines changed

7 files changed

+424
-208
lines changed

package-lock.json

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/featureManager.ts

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
import { TimeWindowFilter } from "./filter/TimeWindowFilter.js";
55
import { IFeatureFilter } from "./filter/FeatureFilter.js";
6-
import { RequirementType } from "./model.js";
6+
import { RequirementType } from "./schema/model.js";
77
import { IFeatureFlagProvider } from "./featureProvider.js";
88
import { TargetingFilter } from "./filter/TargetingFilter.js";
99

@@ -30,15 +30,12 @@ export class FeatureManager {
3030

3131
// If multiple feature flags are found, the first one takes precedence.
3232
async isEnabled(featureName: string, context?: unknown): Promise<boolean> {
33-
const featureFlag = await this.#provider.getFeatureFlag(featureName);
33+
const featureFlag = await this.#getFeatureFlag(featureName);
3434
if (featureFlag === undefined) {
3535
// If the feature is not found, then it is disabled.
3636
return false;
3737
}
3838

39-
// Ensure that the feature flag is in the correct format. Feature providers should validate the feature flags, but we do it here as a safeguard.
40-
validateFeatureFlagFormat(featureFlag);
41-
4239
if (featureFlag.enabled !== true) {
4340
// If the feature is not explicitly enabled, then it is disabled by default.
4441
return false;
@@ -75,14 +72,13 @@ export class FeatureManager {
7572
return !shortCircuitEvaluationResult;
7673
}
7774

75+
async #getFeatureFlag(featureName: string): Promise<any> {
76+
const featureFlag = await this.#provider.getFeatureFlag(featureName);
77+
return featureFlag;
78+
}
79+
7880
}
7981

8082
interface FeatureManagerOptions {
8183
customFilters?: IFeatureFilter[];
8284
}
83-
84-
function validateFeatureFlagFormat(featureFlag: any): void {
85-
if (featureFlag.enabled !== undefined && typeof featureFlag.enabled !== "boolean") {
86-
throw new Error(`Feature flag ${featureFlag.id} has an invalid 'enabled' value.`);
87-
}
88-
}

src/featureProvider.ts

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22
// Licensed under the MIT license.
33

44
import { IGettable } from "./gettable.js";
5-
import { FeatureFlag, FeatureManagementConfiguration, FEATURE_MANAGEMENT_KEY, FEATURE_FLAGS_KEY } from "./model.js";
5+
import { FeatureFlag, FeatureManagementConfiguration, FEATURE_MANAGEMENT_KEY, FEATURE_FLAGS_KEY } from "./schema/model.js";
6+
import { validateFeatureFlag } from "./schema/validator.js";
67

78
export interface IFeatureFlagProvider {
89
/**
@@ -28,12 +29,16 @@ export class ConfigurationMapFeatureFlagProvider implements IFeatureFlagProvider
2829
}
2930
async getFeatureFlag(featureName: string): Promise<FeatureFlag | undefined> {
3031
const featureConfig = this.#configuration.get<FeatureManagementConfiguration>(FEATURE_MANAGEMENT_KEY);
31-
return featureConfig?.[FEATURE_FLAGS_KEY]?.findLast((feature) => feature.id === featureName);
32+
const featureFlag = featureConfig?.[FEATURE_FLAGS_KEY]?.findLast((feature) => feature.id === featureName);
33+
validateFeatureFlag(featureFlag);
34+
return featureFlag;
3235
}
3336

3437
async getFeatureFlags(): Promise<FeatureFlag[]> {
3538
const featureConfig = this.#configuration.get<FeatureManagementConfiguration>(FEATURE_MANAGEMENT_KEY);
36-
return featureConfig?.[FEATURE_FLAGS_KEY] ?? [];
39+
const featureFlag = featureConfig?.[FEATURE_FLAGS_KEY] ?? [];
40+
validateFeatureFlag(featureFlag);
41+
return featureFlag;
3742
}
3843
}
3944

@@ -49,10 +54,14 @@ export class ConfigurationObjectFeatureFlagProvider implements IFeatureFlagProvi
4954

5055
async getFeatureFlag(featureName: string): Promise<FeatureFlag | undefined> {
5156
const featureFlags = this.#configuration[FEATURE_MANAGEMENT_KEY]?.[FEATURE_FLAGS_KEY];
52-
return featureFlags?.findLast((feature: FeatureFlag) => feature.id === featureName);
57+
const featureFlag = featureFlags?.findLast((feature: FeatureFlag) => feature.id === featureName);
58+
validateFeatureFlag(featureFlag);
59+
return featureFlag;
5360
}
5461

5562
async getFeatureFlags(): Promise<FeatureFlag[]> {
56-
return this.#configuration[FEATURE_MANAGEMENT_KEY]?.[FEATURE_FLAGS_KEY] ?? [];
63+
const featureFlag = this.#configuration[FEATURE_MANAGEMENT_KEY]?.[FEATURE_FLAGS_KEY] ?? [];
64+
validateFeatureFlag(featureFlag);
65+
return featureFlag;
5766
}
58-
}
67+
}

0 commit comments

Comments
 (0)