1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-16 08:13:42 +00:00

[PM-2282] Make feature flags type safe (#8612)

Refactors the feature flags in ConfigService to be type safe. It also moves the default value to a centralized location rather than the caller defining it. This ensures consistency across the various places they are used.
This commit is contained in:
Oscar Hinton
2024-04-26 12:57:26 +02:00
committed by GitHub
parent c7fa376be3
commit 14b2eb99a2
27 changed files with 67 additions and 58 deletions

View File

@@ -1,7 +1,7 @@
import { Observable } from "rxjs";
import { SemVer } from "semver";
import { FeatureFlag } from "../../../enums/feature-flag.enum";
import { FeatureFlag, FeatureFlagValueType } from "../../../enums/feature-flag.enum";
import { Region } from "../environment.service";
import { ServerConfig } from "./server-config";
@@ -14,23 +14,15 @@ export abstract class ConfigService {
/**
* Retrieves the value of a feature flag for the currently active user
* @param key The feature flag to retrieve
* @param defaultValue The default value to return if the feature flag is not set or the server's config is irretrievable
* @returns An observable that emits the value of the feature flag, updates as the server config changes
*/
getFeatureFlag$: <T extends boolean | number | string>(
key: FeatureFlag,
defaultValue?: T,
) => Observable<T>;
getFeatureFlag$: <Flag extends FeatureFlag>(key: Flag) => Observable<FeatureFlagValueType<Flag>>;
/**
* Retrieves the value of a feature flag for the currently active user
* @param key The feature flag to retrieve
* @param defaultValue The default value to return if the feature flag is not set or the server's config is irretrievable
* @returns The value of the feature flag
*/
getFeatureFlag: <T extends boolean | number | string>(
key: FeatureFlag,
defaultValue?: T,
) => Promise<T>;
getFeatureFlag: <Flag extends FeatureFlag>(key: Flag) => Promise<FeatureFlagValueType<Flag>>;
/**
* Verifies whether the server version meets the minimum required version
* @param minimumRequiredServerVersion The minimum version required

View File

@@ -1,5 +1,6 @@
import { Jsonify } from "type-fest";
import { AllowedFeatureFlagTypes } from "../../../enums/feature-flag.enum";
import {
ServerConfigData,
ThirdPartyServerConfigData,
@@ -14,7 +15,7 @@ export class ServerConfig {
server?: ThirdPartyServerConfigData;
environment?: EnvironmentServerConfigData;
utcDate: Date;
featureStates: { [key: string]: string } = {};
featureStates: { [key: string]: AllowedFeatureFlagTypes } = {};
constructor(serverConfigData: ServerConfigData) {
this.version = serverConfigData.version;

View File

@@ -1,5 +1,6 @@
import { Jsonify } from "type-fest";
import { AllowedFeatureFlagTypes } from "../../../enums/feature-flag.enum";
import { Region } from "../../abstractions/environment.service";
import {
ServerConfigResponse,
@@ -13,7 +14,7 @@ export class ServerConfigData {
server?: ThirdPartyServerConfigData;
environment?: EnvironmentServerConfigData;
utcDate: string;
featureStates: { [key: string]: string } = {};
featureStates: { [key: string]: AllowedFeatureFlagTypes } = {};
constructor(serverConfigResponse: Partial<ServerConfigResponse>) {
this.version = serverConfigResponse?.version;

View File

@@ -13,7 +13,11 @@ import {
} from "rxjs";
import { SemVer } from "semver";
import { FeatureFlag, FeatureFlagValue } from "../../../enums/feature-flag.enum";
import {
DefaultFeatureFlagValue,
FeatureFlag,
FeatureFlagValueType,
} from "../../../enums/feature-flag.enum";
import { UserId } from "../../../types/guid";
import { ConfigApiServiceAbstraction } from "../../abstractions/config/config-api.service.abstraction";
import { ConfigService } from "../../abstractions/config/config.service";
@@ -89,20 +93,21 @@ export class DefaultConfigService implements ConfigService {
map((config) => config?.environment?.cloudRegion ?? Region.US),
);
}
getFeatureFlag$<T extends FeatureFlagValue>(key: FeatureFlag, defaultValue?: T) {
getFeatureFlag$<Flag extends FeatureFlag>(key: Flag) {
return this.serverConfig$.pipe(
map((serverConfig) => {
if (serverConfig?.featureStates == null || serverConfig.featureStates[key] == null) {
return defaultValue;
return DefaultFeatureFlagValue[key];
}
return serverConfig.featureStates[key] as T;
return serverConfig.featureStates[key] as FeatureFlagValueType<Flag>;
}),
);
}
async getFeatureFlag<T extends FeatureFlagValue>(key: FeatureFlag, defaultValue?: T) {
return await firstValueFrom(this.getFeatureFlag$(key, defaultValue));
async getFeatureFlag<Flag extends FeatureFlag>(key: Flag) {
return await firstValueFrom(this.getFeatureFlag$(key));
}
checkServerMeetsVersionRequirement$(minimumRequiredServerVersion: SemVer) {