mirror of
https://github.com/bitwarden/browser
synced 2025-12-10 13:23:34 +00:00
[PM-11661]Add New Reseed - Fill Buffer Behind Feature Flag (#10905)
* Add New Reseed - Fill Buffer Behind Feature Flag * Add Tests * Lint
This commit is contained in:
@@ -34,6 +34,7 @@ export enum FeatureFlag {
|
||||
AccountDeprovisioning = "pm-10308-account-deprovisioning",
|
||||
NotificationBarAddLoginImprovements = "notification-bar-add-login-improvements",
|
||||
AC2476_DeprecateStripeSourcesAPI = "AC-2476-deprecate-stripe-sources-api",
|
||||
StorageReseedRefactor = "storage-reseed-refactor",
|
||||
CipherKeyEncryption = "cipher-key-encryption",
|
||||
}
|
||||
|
||||
@@ -76,6 +77,7 @@ export const DefaultFeatureFlagValue = {
|
||||
[FeatureFlag.EnableUpgradePasswordManagerSub]: FALSE,
|
||||
[FeatureFlag.GenerateIdentityFillScriptRefactor]: FALSE,
|
||||
[FeatureFlag.DelayFido2PageScriptInitWithinMv2]: FALSE,
|
||||
[FeatureFlag.StorageReseedRefactor]: FALSE,
|
||||
[FeatureFlag.AccountDeprovisioning]: FALSE,
|
||||
[FeatureFlag.NotificationBarAddLoginImprovements]: FALSE,
|
||||
[FeatureFlag.AC2476_DeprecateStripeSourcesAPI]: FALSE,
|
||||
|
||||
@@ -2,6 +2,7 @@ import { Observable } from "rxjs";
|
||||
import { SemVer } from "semver";
|
||||
|
||||
import { FeatureFlag, FeatureFlagValueType } from "../../../enums/feature-flag.enum";
|
||||
import { UserId } from "../../../types/guid";
|
||||
import { Region } from "../environment.service";
|
||||
|
||||
import { ServerConfig } from "./server-config";
|
||||
@@ -17,6 +18,18 @@ export abstract class ConfigService {
|
||||
* @returns An observable that emits the value of the feature flag, updates as the server config changes
|
||||
*/
|
||||
getFeatureFlag$: <Flag extends FeatureFlag>(key: Flag) => Observable<FeatureFlagValueType<Flag>>;
|
||||
|
||||
/**
|
||||
* Retrieves the cached feature flag value for a give user. This will NOT call to the server to get
|
||||
* the most up to date feature flag.
|
||||
* @param key The feature flag key to get the value for.
|
||||
* @param userId The user id of the user to get the feature flag value for.
|
||||
*/
|
||||
abstract userCachedFeatureFlag$<Flag extends FeatureFlag>(
|
||||
key: Flag,
|
||||
userId: UserId,
|
||||
): Observable<FeatureFlagValueType<Flag>>;
|
||||
|
||||
/**
|
||||
* Retrieves the value of a feature flag for the currently active user
|
||||
* @param key The feature flag to retrieve
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { AllowedFeatureFlagTypes } from "../../../enums/feature-flag.enum";
|
||||
import { BaseResponse } from "../../../models/response/base.response";
|
||||
import { Region } from "../../abstractions/environment.service";
|
||||
|
||||
@@ -6,7 +7,7 @@ export class ServerConfigResponse extends BaseResponse {
|
||||
gitHash: string;
|
||||
server: ThirdPartyServerConfigResponse;
|
||||
environment: EnvironmentServerConfigResponse;
|
||||
featureStates: { [key: string]: string } = {};
|
||||
featureStates: { [key: string]: AllowedFeatureFlagTypes } = {};
|
||||
|
||||
constructor(response: any) {
|
||||
super(response);
|
||||
|
||||
@@ -16,6 +16,7 @@ import {
|
||||
import { subscribeTo } from "../../../../spec/observable-tracker";
|
||||
import { AuthService } from "../../../auth/abstractions/auth.service";
|
||||
import { AuthenticationStatus } from "../../../auth/enums/authentication-status";
|
||||
import { FeatureFlag } from "../../../enums/feature-flag.enum";
|
||||
import { UserId } from "../../../types/guid";
|
||||
import { ConfigApiServiceAbstraction } from "../../abstractions/config/config-api.service.abstraction";
|
||||
import { ServerConfig } from "../../abstractions/config/server-config";
|
||||
@@ -277,6 +278,48 @@ describe("ConfigService", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("userCachedFeatureFlag$", () => {
|
||||
it("maps saved user config to a feature flag", async () => {
|
||||
const updateFeature = (value: boolean) => {
|
||||
return new ServerConfig(
|
||||
new ServerConfigData({
|
||||
featureStates: {
|
||||
"test-feature": value,
|
||||
},
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
const configService = new DefaultConfigService(
|
||||
configApiService,
|
||||
environmentService,
|
||||
logService,
|
||||
stateProvider,
|
||||
authService,
|
||||
);
|
||||
|
||||
userState.nextState(null);
|
||||
|
||||
const promise = firstValueFrom(
|
||||
configService
|
||||
.userCachedFeatureFlag$("test-feature" as FeatureFlag, userId)
|
||||
.pipe(bufferCount(3)),
|
||||
);
|
||||
|
||||
userState.nextState(updateFeature(true));
|
||||
userState.nextState(updateFeature(false));
|
||||
|
||||
const values = await promise;
|
||||
|
||||
// We wouldn't normally expect this to be undefined, the logic
|
||||
// should normally return the feature flags default value but since
|
||||
// we are faking a feature flag key, undefined is expected
|
||||
expect(values[0]).toBe(undefined);
|
||||
expect(values[1]).toBe(true);
|
||||
expect(values[2]).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("slow configuration", () => {
|
||||
const environmentSubject = new BehaviorSubject<Environment>(null);
|
||||
|
||||
|
||||
@@ -115,16 +115,27 @@ export class DefaultConfigService implements ConfigService {
|
||||
|
||||
getFeatureFlag$<Flag extends FeatureFlag>(key: Flag) {
|
||||
return this.serverConfig$.pipe(
|
||||
map((serverConfig) => {
|
||||
if (serverConfig?.featureStates == null || serverConfig.featureStates[key] == null) {
|
||||
return DefaultFeatureFlagValue[key];
|
||||
}
|
||||
|
||||
return serverConfig.featureStates[key] as FeatureFlagValueType<Flag>;
|
||||
}),
|
||||
map((serverConfig) => this.getFeatureFlagValue(serverConfig, key)),
|
||||
);
|
||||
}
|
||||
|
||||
private getFeatureFlagValue<Flag extends FeatureFlag>(
|
||||
serverConfig: ServerConfig | null,
|
||||
flag: Flag,
|
||||
) {
|
||||
if (serverConfig?.featureStates == null || serverConfig.featureStates[flag] == null) {
|
||||
return DefaultFeatureFlagValue[flag];
|
||||
}
|
||||
|
||||
return serverConfig.featureStates[flag] as FeatureFlagValueType<Flag>;
|
||||
}
|
||||
|
||||
userCachedFeatureFlag$<Flag extends FeatureFlag>(key: Flag, userId: UserId) {
|
||||
return this.stateProvider
|
||||
.getUser(userId, USER_SERVER_CONFIG)
|
||||
.state$.pipe(map((config) => this.getFeatureFlagValue(config, key)));
|
||||
}
|
||||
|
||||
async getFeatureFlag<Flag extends FeatureFlag>(key: Flag) {
|
||||
return await firstValueFrom(this.getFeatureFlag$(key));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user