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

[PM-5979] Refactor EnvironmentService (#8040)

Refactor environment service to emit a single observable. This required significant changes to how the environment service behaves and tackles much of the tech debt planned for it.
This commit is contained in:
Oscar Hinton
2024-03-21 17:09:44 +01:00
committed by GitHub
parent 7a42b4ebc6
commit e767295c86
88 changed files with 1710 additions and 1379 deletions

View File

@@ -40,6 +40,7 @@ import { EventCollectionMigrator } from "./migrations/41-move-event-collection-t
import { EnableFaviconMigrator } from "./migrations/42-move-enable-favicon-to-domain-settings-state-provider";
import { AutoConfirmFingerPrintsMigrator } from "./migrations/43-move-auto-confirm-finger-prints-to-state-provider";
import { UserDecryptionOptionsMigrator } from "./migrations/44-move-user-decryption-options-to-state-provider";
import { MergeEnvironmentState } from "./migrations/45-merge-environment-state";
import { AddKeyTypeToOrgKeysMigrator } from "./migrations/5-add-key-type-to-org-keys";
import { RemoveLegacyEtmKeyMigrator } from "./migrations/6-remove-legacy-etm-key";
import { MoveBiometricAutoPromptToAccount } from "./migrations/7-move-biometric-auto-prompt-to-account";
@@ -48,7 +49,8 @@ import { MoveBrowserSettingsToGlobal } from "./migrations/9-move-browser-setting
import { MinVersionMigrator } from "./migrations/min-version";
export const MIN_VERSION = 3;
export const CURRENT_VERSION = 44;
export const CURRENT_VERSION = 45;
export type MinVersion = typeof MIN_VERSION;
export function createMigrationBuilder() {
@@ -94,7 +96,8 @@ export function createMigrationBuilder() {
.with(EventCollectionMigrator, 40, 41)
.with(EnableFaviconMigrator, 41, 42)
.with(AutoConfirmFingerPrintsMigrator, 42, 43)
.with(UserDecryptionOptionsMigrator, 43, CURRENT_VERSION);
.with(UserDecryptionOptionsMigrator, 43, 44)
.with(MergeEnvironmentState, 44, CURRENT_VERSION);
}
export async function currentVersion(

View File

@@ -0,0 +1,164 @@
import { runMigrator } from "../migration-helper.spec";
import { MergeEnvironmentState } from "./45-merge-environment-state";
describe("MergeEnvironmentState", () => {
const migrator = new MergeEnvironmentState(44, 45);
it("can migrate all data", async () => {
const output = await runMigrator(migrator, {
authenticatedAccounts: ["user1", "user2"],
global: {
extra: "data",
},
global_environment_region: "US",
global_environment_urls: {
base: "example.com",
},
user1: {
extra: "data",
settings: {
extra: "data",
},
},
user2: {
extra: "data",
settings: {
extra: "data",
},
},
extra: "data",
user_user1_environment_region: "US",
user_user2_environment_region: "EU",
user_user1_environment_urls: {
base: "example.com",
},
user_user2_environment_urls: {
base: "other.example.com",
},
});
expect(output).toEqual({
authenticatedAccounts: ["user1", "user2"],
global: {
extra: "data",
},
global_environment_environment: {
region: "US",
urls: {
base: "example.com",
},
},
user1: {
extra: "data",
settings: {
extra: "data",
},
},
user2: {
extra: "data",
settings: {
extra: "data",
},
},
extra: "data",
user_user1_environment_environment: {
region: "US",
urls: {
base: "example.com",
},
},
user_user2_environment_environment: {
region: "EU",
urls: {
base: "other.example.com",
},
},
});
});
it("handles missing parts", async () => {
const output = await runMigrator(migrator, {
authenticatedAccounts: ["user1", "user2"],
global: {
extra: "data",
},
user1: {
extra: "data",
settings: {
extra: "data",
},
},
user2: null,
});
expect(output).toEqual({
authenticatedAccounts: ["user1", "user2"],
global: {
extra: "data",
},
user1: {
extra: "data",
settings: {
extra: "data",
},
},
user2: null,
});
});
it("can migrate only global data", async () => {
const output = await runMigrator(migrator, {
authenticatedAccounts: [],
global_environment_region: "Self-Hosted",
global: {},
});
expect(output).toEqual({
authenticatedAccounts: [],
global_environment_environment: {
region: "Self-Hosted",
urls: undefined,
},
global: {},
});
});
it("can migrate only user state", async () => {
const output = await runMigrator(migrator, {
authenticatedAccounts: ["user1"] as const,
global: null,
user1: { settings: {} },
user_user1_environment_region: "Self-Hosted",
user_user1_environment_urls: {
base: "some-base-url",
api: "some-api-url",
identity: "some-identity-url",
icons: "some-icons-url",
notifications: "some-notifications-url",
events: "some-events-url",
webVault: "some-webVault-url",
keyConnector: "some-keyConnector-url",
},
});
expect(output).toEqual({
authenticatedAccounts: ["user1"] as const,
global: null,
user1: { settings: {} },
user_user1_environment_environment: {
region: "Self-Hosted",
urls: {
base: "some-base-url",
api: "some-api-url",
identity: "some-identity-url",
icons: "some-icons-url",
notifications: "some-notifications-url",
events: "some-events-url",
webVault: "some-webVault-url",
keyConnector: "some-keyConnector-url",
},
},
});
});
});

View File

@@ -0,0 +1,83 @@
import { KeyDefinitionLike, MigrationHelper, StateDefinitionLike } from "../migration-helper";
import { Migrator } from "../migrator";
const ENVIRONMENT_STATE: StateDefinitionLike = { name: "environment" };
const ENVIRONMENT_REGION: KeyDefinitionLike = {
key: "region",
stateDefinition: ENVIRONMENT_STATE,
};
const ENVIRONMENT_URLS: KeyDefinitionLike = {
key: "urls",
stateDefinition: ENVIRONMENT_STATE,
};
const ENVIRONMENT_ENVIRONMENT: KeyDefinitionLike = {
key: "environment",
stateDefinition: ENVIRONMENT_STATE,
};
export class MergeEnvironmentState extends Migrator<44, 45> {
async migrate(helper: MigrationHelper): Promise<void> {
const accounts = await helper.getAccounts<unknown>();
async function migrateAccount(userId: string, account: unknown): Promise<void> {
const region = await helper.getFromUser(userId, ENVIRONMENT_REGION);
const urls = await helper.getFromUser(userId, ENVIRONMENT_URLS);
if (region == null && urls == null) {
return;
}
await helper.setToUser(userId, ENVIRONMENT_ENVIRONMENT, {
region,
urls,
});
await helper.removeFromUser(userId, ENVIRONMENT_REGION);
await helper.removeFromUser(userId, ENVIRONMENT_URLS);
}
await Promise.all([...accounts.map(({ userId, account }) => migrateAccount(userId, account))]);
const region = await helper.getFromGlobal(ENVIRONMENT_REGION);
const urls = await helper.getFromGlobal(ENVIRONMENT_URLS);
if (region == null && urls == null) {
return;
}
await helper.setToGlobal(ENVIRONMENT_ENVIRONMENT, {
region,
urls,
});
await helper.removeFromGlobal(ENVIRONMENT_REGION);
await helper.removeFromGlobal(ENVIRONMENT_URLS);
}
async rollback(helper: MigrationHelper): Promise<void> {
const accounts = await helper.getAccounts<unknown>();
async function rollbackAccount(userId: string, account: unknown): Promise<void> {
const state = (await helper.getFromUser(userId, ENVIRONMENT_ENVIRONMENT)) as {
region: string;
urls: string;
} | null;
await helper.setToUser(userId, ENVIRONMENT_REGION, state?.region);
await helper.setToUser(userId, ENVIRONMENT_URLS, state?.urls);
await helper.removeFromUser(userId, ENVIRONMENT_ENVIRONMENT);
}
await Promise.all([...accounts.map(({ userId, account }) => rollbackAccount(userId, account))]);
const state = (await helper.getFromGlobal(ENVIRONMENT_ENVIRONMENT)) as {
region: string;
urls: string;
} | null;
await helper.setToGlobal(ENVIRONMENT_REGION, state?.region);
await helper.setToGlobal(ENVIRONMENT_URLS, state?.urls);
await helper.removeFromGlobal(ENVIRONMENT_ENVIRONMENT);
}
}