mirror of
https://github.com/bitwarden/browser
synced 2025-12-18 09:13:33 +00:00
[EC-473] Add feature flags to common code (#3324)
This commit is contained in:
99
libs/common/src/misc/flags.spec.ts
Normal file
99
libs/common/src/misc/flags.spec.ts
Normal file
@@ -0,0 +1,99 @@
|
||||
import { flagEnabled, devFlagEnabled, devFlagValue } from "./flags";
|
||||
|
||||
describe("flagEnabled", () => {
|
||||
beforeEach(() => {
|
||||
process.env.FLAGS = JSON.stringify({});
|
||||
});
|
||||
|
||||
it("returns true by default", () => {
|
||||
expect(flagEnabled<any>("nonExistentFlag")).toBe(true);
|
||||
});
|
||||
|
||||
it("returns true if enabled", () => {
|
||||
process.env.FLAGS = JSON.stringify({
|
||||
newFeature: true,
|
||||
});
|
||||
|
||||
expect(flagEnabled<any>("newFeature")).toBe(true);
|
||||
});
|
||||
|
||||
it("returns false if disabled", () => {
|
||||
process.env.FLAGS = JSON.stringify({
|
||||
newFeature: false,
|
||||
});
|
||||
|
||||
expect(flagEnabled<any>("newFeature")).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("devFlagEnabled", () => {
|
||||
beforeEach(() => {
|
||||
process.env.DEV_FLAGS = JSON.stringify({});
|
||||
});
|
||||
|
||||
describe("in a development environment", () => {
|
||||
beforeEach(() => {
|
||||
process.env.ENV = "development";
|
||||
});
|
||||
|
||||
it("returns true by default", () => {
|
||||
expect(devFlagEnabled<any>("nonExistentFlag")).toBe(true);
|
||||
});
|
||||
|
||||
it("returns true if enabled", () => {
|
||||
process.env.DEV_FLAGS = JSON.stringify({
|
||||
devHack: true,
|
||||
});
|
||||
|
||||
expect(devFlagEnabled<any>("devHack")).toBe(true);
|
||||
});
|
||||
|
||||
it("returns true if truthy", () => {
|
||||
process.env.DEV_FLAGS = JSON.stringify({
|
||||
devHack: { key: 3 },
|
||||
});
|
||||
|
||||
expect(devFlagEnabled<any>("devHack")).toBe(true);
|
||||
});
|
||||
|
||||
it("returns false if disabled", () => {
|
||||
process.env.DEV_FLAGS = JSON.stringify({
|
||||
devHack: false,
|
||||
});
|
||||
|
||||
expect(devFlagEnabled<any>("devHack")).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
it("always returns false in prod", () => {
|
||||
process.env.ENV = "production";
|
||||
process.env.DEV_FLAGS = JSON.stringify({
|
||||
devHack: true,
|
||||
});
|
||||
|
||||
expect(devFlagEnabled<any>("devHack")).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("devFlagValue", () => {
|
||||
beforeEach(() => {
|
||||
process.env.DEV_FLAGS = JSON.stringify({});
|
||||
process.env.ENV = "development";
|
||||
});
|
||||
|
||||
it("throws if dev flag is disabled", () => {
|
||||
process.env.DEV_FLAGS = JSON.stringify({
|
||||
devHack: false,
|
||||
});
|
||||
|
||||
expect(() => devFlagValue<any>("devHack")).toThrow("it is protected by a disabled dev flag");
|
||||
});
|
||||
|
||||
it("returns the dev flag value", () => {
|
||||
process.env.DEV_FLAGS = JSON.stringify({
|
||||
devHack: "Hello world",
|
||||
});
|
||||
|
||||
expect(devFlagValue<any>("devHack")).toBe("Hello world");
|
||||
});
|
||||
});
|
||||
61
libs/common/src/misc/flags.ts
Normal file
61
libs/common/src/misc/flags.ts
Normal file
@@ -0,0 +1,61 @@
|
||||
// required to avoid linting errors when there are no flags
|
||||
/* eslint-disable @typescript-eslint/ban-types */
|
||||
export type SharedFlags = {};
|
||||
|
||||
// required to avoid linting errors when there are no flags
|
||||
/* eslint-disable @typescript-eslint/ban-types */
|
||||
export type SharedDevFlags = {};
|
||||
|
||||
function getFlags<T>(envFlags: string | T): T {
|
||||
if (typeof envFlags === "string") {
|
||||
return JSON.parse(envFlags) as T;
|
||||
} else {
|
||||
return envFlags as T;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of a feature flag from environment.
|
||||
* All flags default to "on" (true).
|
||||
* Only use for shared code in `libs`, otherwise use the client-specific function.
|
||||
* @param flag The name of the feature flag to check
|
||||
* @returns The value of the flag
|
||||
*/
|
||||
export function flagEnabled<Flags extends SharedFlags>(flag: keyof Flags): boolean {
|
||||
const flags = getFlags<Flags>(process.env.FLAGS);
|
||||
return flags[flag] == null || !!flags[flag];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of a dev flag from environment.
|
||||
* Will always return false unless in development.
|
||||
* Only use for shared code in `libs`, otherwise use the client-specific function.
|
||||
* @param flag The name of the dev flag to check
|
||||
* @returns The value of the flag
|
||||
*/
|
||||
export function devFlagEnabled<DevFlags extends SharedDevFlags>(flag: keyof DevFlags): boolean {
|
||||
if (process.env.ENV !== "development") {
|
||||
return false;
|
||||
}
|
||||
|
||||
const devFlags = getFlags<DevFlags>(process.env.DEV_FLAGS);
|
||||
return devFlags[flag] == null || !!devFlags[flag];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of a dev flag from environment.
|
||||
* Will always return false unless in development.
|
||||
* @param flag The name of the dev flag to check
|
||||
* @returns The value of the flag
|
||||
* @throws Error if the flag is not enabled
|
||||
*/
|
||||
export function devFlagValue<DevFlags extends SharedDevFlags>(
|
||||
flag: keyof DevFlags
|
||||
): DevFlags[keyof DevFlags] {
|
||||
if (!devFlagEnabled(flag)) {
|
||||
throw new Error(`This method should not be called, it is protected by a disabled dev flag.`);
|
||||
}
|
||||
|
||||
const devFlags = getFlags<DevFlags>(process.env.DEV_FLAGS);
|
||||
return devFlags[flag];
|
||||
}
|
||||
Reference in New Issue
Block a user