Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 7 additions & 11 deletions src/featureManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

import { TimeWindowFilter } from "./filter/TimeWindowFilter.js";
import { IFeatureFilter } from "./filter/FeatureFilter.js";
import { RequirementType } from "./model.js";
import { RequirementType } from "./schema/model.js";
import { IFeatureFlagProvider } from "./featureProvider.js";
import { TargetingFilter } from "./filter/TargetingFilter.js";

Expand All @@ -30,15 +30,12 @@ export class FeatureManager {

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

// 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.
validateFeatureFlagFormat(featureFlag);

if (featureFlag.enabled !== true) {
// If the feature is not explicitly enabled, then it is disabled by default.
return false;
Expand Down Expand Up @@ -75,14 +72,13 @@ export class FeatureManager {
return !shortCircuitEvaluationResult;
}

async #getFeatureFlag(featureName: string): Promise<any> {
const featureFlag = await this.#provider.getFeatureFlag(featureName);
return featureFlag;
}

}

interface FeatureManagerOptions {
customFilters?: IFeatureFilter[];
}

function validateFeatureFlagFormat(featureFlag: any): void {
if (featureFlag.enabled !== undefined && typeof featureFlag.enabled !== "boolean") {
throw new Error(`Feature flag ${featureFlag.id} has an invalid 'enabled' value.`);
}
}
19 changes: 14 additions & 5 deletions src/featureProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
// Licensed under the MIT license.

import { IGettable } from "./gettable.js";
import { FeatureFlag, FeatureManagementConfiguration, FEATURE_MANAGEMENT_KEY, FEATURE_FLAGS_KEY } from "./model.js";
import { FeatureFlag, FeatureManagementConfiguration, FEATURE_MANAGEMENT_KEY, FEATURE_FLAGS_KEY } from "./schema/model.js";
import { validateFeatureFlag } from "./schema/validator.js";

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

async getFeatureFlags(): Promise<FeatureFlag[]> {
const featureConfig = this.#configuration.get<FeatureManagementConfiguration>(FEATURE_MANAGEMENT_KEY);
return featureConfig?.[FEATURE_FLAGS_KEY] ?? [];
const featureFlag = featureConfig?.[FEATURE_FLAGS_KEY] ?? [];
validateFeatureFlag(featureFlag);
return featureFlag;
}
}

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

async getFeatureFlag(featureName: string): Promise<FeatureFlag | undefined> {
const featureFlags = this.#configuration[FEATURE_MANAGEMENT_KEY]?.[FEATURE_FLAGS_KEY];
return featureFlags?.findLast((feature: FeatureFlag) => feature.id === featureName);
const featureFlag = featureFlags?.findLast((feature: FeatureFlag) => feature.id === featureName);
validateFeatureFlag(featureFlag);
return featureFlag;
}

async getFeatureFlags(): Promise<FeatureFlag[]> {
return this.#configuration[FEATURE_MANAGEMENT_KEY]?.[FEATURE_FLAGS_KEY] ?? [];
const featureFlag = this.#configuration[FEATURE_MANAGEMENT_KEY]?.[FEATURE_FLAGS_KEY] ?? [];
validateFeatureFlag(featureFlag);
return featureFlag;
}
}
Loading
Loading