mirror of
https://github.com/bitwarden/browser.git
synced 2024-11-25 12:15:18 +01:00
* Revert "[PM-5277] Migrate Sync Service to State Provider (#7680)"
This reverts commit 78008a9e1e
.
Includes a noop migration builder that allows us to bridge over the deleted migration
* Prefer revert migrations to noop
this revert avoids the need to change behavior between released vs unreleased migrations and keeps some dangerous code out of the repo :success:
* Update ordering of badge settings migrator to be consistent with `rc`, which was cut with only up to version 25
* Fix missing type import
This commit is contained in:
parent
2d0a3e27f2
commit
b691b6b1d6
@ -693,7 +693,6 @@ export default class MainBackground {
|
|||||||
this.folderApiService,
|
this.folderApiService,
|
||||||
this.organizationService,
|
this.organizationService,
|
||||||
this.sendApiService,
|
this.sendApiService,
|
||||||
this.stateProvider,
|
|
||||||
logoutCallback,
|
logoutCallback,
|
||||||
);
|
);
|
||||||
this.eventUploadService = new EventUploadService(
|
this.eventUploadService = new EventUploadService(
|
||||||
@ -1077,7 +1076,7 @@ export default class MainBackground {
|
|||||||
await this.eventUploadService.uploadEvents(userId);
|
await this.eventUploadService.uploadEvents(userId);
|
||||||
|
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
this.syncService.setLastSync(new Date(0), userId as UserId),
|
this.syncService.setLastSync(new Date(0), userId),
|
||||||
this.cryptoService.clearKeys(userId),
|
this.cryptoService.clearKeys(userId),
|
||||||
this.settingsService.clear(userId),
|
this.settingsService.clear(userId),
|
||||||
this.cipherService.clear(userId),
|
this.cipherService.clear(userId),
|
||||||
|
@ -551,7 +551,6 @@ export class Main {
|
|||||||
this.folderApiService,
|
this.folderApiService,
|
||||||
this.organizationService,
|
this.organizationService,
|
||||||
this.sendApiService,
|
this.sendApiService,
|
||||||
this.stateProvider,
|
|
||||||
async (expired: boolean) => await this.logout(),
|
async (expired: boolean) => await this.logout(),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -571,7 +571,7 @@ export class AppComponent implements OnInit, OnDestroy {
|
|||||||
let preLogoutActiveUserId;
|
let preLogoutActiveUserId;
|
||||||
try {
|
try {
|
||||||
await this.eventUploadService.uploadEvents(userBeingLoggedOut);
|
await this.eventUploadService.uploadEvents(userBeingLoggedOut);
|
||||||
await this.syncService.setLastSync(new Date(0), userBeingLoggedOut as UserId);
|
await this.syncService.setLastSync(new Date(0), userBeingLoggedOut);
|
||||||
await this.cryptoService.clearKeys(userBeingLoggedOut);
|
await this.cryptoService.clearKeys(userBeingLoggedOut);
|
||||||
await this.settingsService.clear(userBeingLoggedOut);
|
await this.settingsService.clear(userBeingLoggedOut);
|
||||||
await this.cipherService.clear(userBeingLoggedOut);
|
await this.cipherService.clear(userBeingLoggedOut);
|
||||||
|
@ -80,4 +80,14 @@ export class StateService extends BaseStateService<GlobalState, Account> {
|
|||||||
options = this.reconcileOptions(options, await this.defaultInMemoryOptions());
|
options = this.reconcileOptions(options, await this.defaultInMemoryOptions());
|
||||||
return await super.setEncryptedSends(value, options);
|
return await super.setEncryptedSends(value, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override async getLastSync(options?: StorageOptions): Promise<string> {
|
||||||
|
options = this.reconcileOptions(options, await this.defaultInMemoryOptions());
|
||||||
|
return await super.getLastSync(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
override async setLastSync(value: string, options?: StorageOptions): Promise<void> {
|
||||||
|
options = this.reconcileOptions(options, await this.defaultInMemoryOptions());
|
||||||
|
return await super.setLastSync(value, options);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -139,10 +139,10 @@ import { ValidationService } from "@bitwarden/common/platform/services/validatio
|
|||||||
import { WebCryptoFunctionService } from "@bitwarden/common/platform/services/web-crypto-function.service";
|
import { WebCryptoFunctionService } from "@bitwarden/common/platform/services/web-crypto-function.service";
|
||||||
import {
|
import {
|
||||||
ActiveUserStateProvider,
|
ActiveUserStateProvider,
|
||||||
DerivedStateProvider,
|
|
||||||
GlobalStateProvider,
|
GlobalStateProvider,
|
||||||
SingleUserStateProvider,
|
SingleUserStateProvider,
|
||||||
StateProvider,
|
StateProvider,
|
||||||
|
DerivedStateProvider,
|
||||||
} from "@bitwarden/common/platform/state";
|
} from "@bitwarden/common/platform/state";
|
||||||
/* eslint-disable import/no-restricted-paths -- We need the implementations to inject, but generally these should not be accessed */
|
/* eslint-disable import/no-restricted-paths -- We need the implementations to inject, but generally these should not be accessed */
|
||||||
import { DefaultActiveUserStateProvider } from "@bitwarden/common/platform/state/implementations/default-active-user-state.provider";
|
import { DefaultActiveUserStateProvider } from "@bitwarden/common/platform/state/implementations/default-active-user-state.provider";
|
||||||
@ -517,7 +517,6 @@ import { ModalService } from "./modal.service";
|
|||||||
FolderApiServiceAbstraction,
|
FolderApiServiceAbstraction,
|
||||||
OrganizationServiceAbstraction,
|
OrganizationServiceAbstraction,
|
||||||
SendApiServiceAbstraction,
|
SendApiServiceAbstraction,
|
||||||
StateProvider,
|
|
||||||
LOGOUT_CALLBACK,
|
LOGOUT_CALLBACK,
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
@ -328,6 +328,8 @@ export abstract class StateService<T extends Account = Account> {
|
|||||||
setKeyHash: (value: string, options?: StorageOptions) => Promise<void>;
|
setKeyHash: (value: string, options?: StorageOptions) => Promise<void>;
|
||||||
getLastActive: (options?: StorageOptions) => Promise<number>;
|
getLastActive: (options?: StorageOptions) => Promise<number>;
|
||||||
setLastActive: (value: number, options?: StorageOptions) => Promise<void>;
|
setLastActive: (value: number, options?: StorageOptions) => Promise<void>;
|
||||||
|
getLastSync: (options?: StorageOptions) => Promise<string>;
|
||||||
|
setLastSync: (value: string, options?: StorageOptions) => Promise<void>;
|
||||||
getLocalData: (options?: StorageOptions) => Promise<{ [cipherId: string]: LocalData }>;
|
getLocalData: (options?: StorageOptions) => Promise<{ [cipherId: string]: LocalData }>;
|
||||||
setLocalData: (
|
setLocalData: (
|
||||||
value: { [cipherId: string]: LocalData },
|
value: { [cipherId: string]: LocalData },
|
||||||
|
@ -181,6 +181,7 @@ export class AccountProfile {
|
|||||||
forceSetPasswordReason?: ForceSetPasswordReason;
|
forceSetPasswordReason?: ForceSetPasswordReason;
|
||||||
hasPremiumPersonally?: boolean;
|
hasPremiumPersonally?: boolean;
|
||||||
hasPremiumFromOrganization?: boolean;
|
hasPremiumFromOrganization?: boolean;
|
||||||
|
lastSync?: string;
|
||||||
userId?: string;
|
userId?: string;
|
||||||
usesKeyConnector?: boolean;
|
usesKeyConnector?: boolean;
|
||||||
keyHash?: string;
|
keyHash?: string;
|
||||||
|
@ -1619,6 +1619,23 @@ export class StateService<
|
|||||||
await this.storageService.save(keys.accountActivity, accountActivity, options);
|
await this.storageService.save(keys.accountActivity, accountActivity, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getLastSync(options?: StorageOptions): Promise<string> {
|
||||||
|
return (
|
||||||
|
await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskMemoryOptions()))
|
||||||
|
)?.profile?.lastSync;
|
||||||
|
}
|
||||||
|
|
||||||
|
async setLastSync(value: string, options?: StorageOptions): Promise<void> {
|
||||||
|
const account = await this.getAccount(
|
||||||
|
this.reconcileOptions(options, await this.defaultOnDiskMemoryOptions()),
|
||||||
|
);
|
||||||
|
account.profile.lastSync = value;
|
||||||
|
await this.saveAccount(
|
||||||
|
account,
|
||||||
|
this.reconcileOptions(options, await this.defaultOnDiskMemoryOptions()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
async getLocalData(options?: StorageOptions): Promise<{ [cipherId: string]: LocalData }> {
|
async getLocalData(options?: StorageOptions): Promise<{ [cipherId: string]: LocalData }> {
|
||||||
return (
|
return (
|
||||||
await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskLocalOptions()))
|
await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskLocalOptions()))
|
||||||
|
@ -44,8 +44,6 @@ export const BILLING_DISK = new StateDefinition("billing", "disk");
|
|||||||
|
|
||||||
export const FOLDER_DISK = new StateDefinition("folder", "disk", { web: "memory" });
|
export const FOLDER_DISK = new StateDefinition("folder", "disk", { web: "memory" });
|
||||||
|
|
||||||
export const SYNC_STATE = new StateDefinition("sync", "disk", { web: "memory" });
|
|
||||||
|
|
||||||
export const VAULT_SETTINGS_DISK = new StateDefinition("vaultSettings", "disk", {
|
export const VAULT_SETTINGS_DISK = new StateDefinition("vaultSettings", "disk", {
|
||||||
web: "disk-local",
|
web: "disk-local",
|
||||||
});
|
});
|
||||||
|
@ -20,8 +20,9 @@ import { CollapsedGroupingsMigrator } from "./migrations/22-move-collapsed-group
|
|||||||
import { MoveBiometricPromptsToStateProviders } from "./migrations/23-move-biometric-prompts-to-state-providers";
|
import { MoveBiometricPromptsToStateProviders } from "./migrations/23-move-biometric-prompts-to-state-providers";
|
||||||
import { SmOnboardingTasksMigrator } from "./migrations/24-move-sm-onboarding-key-to-state-providers";
|
import { SmOnboardingTasksMigrator } from "./migrations/24-move-sm-onboarding-key-to-state-providers";
|
||||||
import { ClearClipboardDelayMigrator } from "./migrations/25-move-clear-clipboard-to-autofill-settings-state-provider";
|
import { ClearClipboardDelayMigrator } from "./migrations/25-move-clear-clipboard-to-autofill-settings-state-provider";
|
||||||
import { BadgeSettingsMigrator } from "./migrations/26-move-badge-settings-to-state-providers";
|
import { RevertLastSyncMigrator } from "./migrations/26-revert-move-last-sync-to-state-provider";
|
||||||
import { MoveBiometricUnlockToStateProviders } from "./migrations/27-move-biometric-unlock-to-state-providers";
|
import { BadgeSettingsMigrator } from "./migrations/27-move-badge-settings-to-state-providers";
|
||||||
|
import { MoveBiometricUnlockToStateProviders } from "./migrations/28-move-biometric-unlock-to-state-providers";
|
||||||
import { FixPremiumMigrator } from "./migrations/3-fix-premium";
|
import { FixPremiumMigrator } from "./migrations/3-fix-premium";
|
||||||
import { RemoveEverBeenUnlockedMigrator } from "./migrations/4-remove-ever-been-unlocked";
|
import { RemoveEverBeenUnlockedMigrator } from "./migrations/4-remove-ever-been-unlocked";
|
||||||
import { AddKeyTypeToOrgKeysMigrator } from "./migrations/5-add-key-type-to-org-keys";
|
import { AddKeyTypeToOrgKeysMigrator } from "./migrations/5-add-key-type-to-org-keys";
|
||||||
@ -32,7 +33,7 @@ import { MoveBrowserSettingsToGlobal } from "./migrations/9-move-browser-setting
|
|||||||
import { MinVersionMigrator } from "./migrations/min-version";
|
import { MinVersionMigrator } from "./migrations/min-version";
|
||||||
|
|
||||||
export const MIN_VERSION = 2;
|
export const MIN_VERSION = 2;
|
||||||
export const CURRENT_VERSION = 27;
|
export const CURRENT_VERSION = 28;
|
||||||
export type MinVersion = typeof MIN_VERSION;
|
export type MinVersion = typeof MIN_VERSION;
|
||||||
|
|
||||||
export function createMigrationBuilder() {
|
export function createMigrationBuilder() {
|
||||||
@ -61,8 +62,9 @@ export function createMigrationBuilder() {
|
|||||||
.with(MoveBiometricPromptsToStateProviders, 22, 23)
|
.with(MoveBiometricPromptsToStateProviders, 22, 23)
|
||||||
.with(SmOnboardingTasksMigrator, 23, 24)
|
.with(SmOnboardingTasksMigrator, 23, 24)
|
||||||
.with(ClearClipboardDelayMigrator, 24, 25)
|
.with(ClearClipboardDelayMigrator, 24, 25)
|
||||||
.with(BadgeSettingsMigrator, 25, 26)
|
.with(RevertLastSyncMigrator, 25, 26)
|
||||||
.with(MoveBiometricUnlockToStateProviders, 26, CURRENT_VERSION);
|
.with(BadgeSettingsMigrator, 26, 27)
|
||||||
|
.with(MoveBiometricUnlockToStateProviders, 27, CURRENT_VERSION);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function currentVersion(
|
export async function currentVersion(
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { any, MockProxy } from "jest-mock-extended";
|
import { MockProxy, any } from "jest-mock-extended";
|
||||||
|
|
||||||
import { MigrationHelper } from "../migration-helper";
|
import { MigrationHelper } from "../migration-helper";
|
||||||
import { mockMigrationHelper } from "../migration-helper.spec";
|
import { mockMigrationHelper } from "../migration-helper.spec";
|
||||||
|
@ -0,0 +1,112 @@
|
|||||||
|
import { any, MockProxy } from "jest-mock-extended";
|
||||||
|
|
||||||
|
import { MigrationHelper } from "../migration-helper";
|
||||||
|
import { mockMigrationHelper } from "../migration-helper.spec";
|
||||||
|
|
||||||
|
import { RevertLastSyncMigrator } from "./26-revert-move-last-sync-to-state-provider";
|
||||||
|
|
||||||
|
function rollbackJSON() {
|
||||||
|
return {
|
||||||
|
global: {
|
||||||
|
otherStuff: "otherStuff1",
|
||||||
|
},
|
||||||
|
authenticatedAccounts: ["user-1", "user-2"],
|
||||||
|
"user-1": {
|
||||||
|
profile: {
|
||||||
|
lastSync: "2024-01-24T00:00:00.000Z",
|
||||||
|
otherStuff: "otherStuff4",
|
||||||
|
},
|
||||||
|
otherStuff: "otherStuff5",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function exampleJSON() {
|
||||||
|
return {
|
||||||
|
"user_user-1_sync_lastSync": "2024-01-24T00:00:00.000Z",
|
||||||
|
"user_user-2_sync_lastSync": null as any,
|
||||||
|
global: {
|
||||||
|
otherStuff: "otherStuff1",
|
||||||
|
},
|
||||||
|
authenticatedAccounts: ["user-1", "user-2"],
|
||||||
|
"user-1": {
|
||||||
|
profile: {
|
||||||
|
lastSync: "2024-01-24T00:00:00.000Z",
|
||||||
|
otherStuff: "otherStuff4",
|
||||||
|
},
|
||||||
|
otherStuff: "otherStuff5",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("LastSyncMigrator", () => {
|
||||||
|
let helper: MockProxy<MigrationHelper>;
|
||||||
|
let sut: RevertLastSyncMigrator;
|
||||||
|
|
||||||
|
const keyDefinitionLike = {
|
||||||
|
key: "lastSync",
|
||||||
|
stateDefinition: {
|
||||||
|
name: "sync",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
describe("rollback", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
helper = mockMigrationHelper(rollbackJSON(), 26);
|
||||||
|
sut = new RevertLastSyncMigrator(25, 26);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should remove lastSync from all accounts", async () => {
|
||||||
|
await sut.rollback(helper);
|
||||||
|
expect(helper.set).toHaveBeenCalledWith("user-1", {
|
||||||
|
profile: {
|
||||||
|
otherStuff: "otherStuff4",
|
||||||
|
},
|
||||||
|
otherStuff: "otherStuff5",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should set lastSync provider value for each account", async () => {
|
||||||
|
await sut.rollback(helper);
|
||||||
|
|
||||||
|
expect(helper.setToUser).toHaveBeenCalledWith(
|
||||||
|
"user-1",
|
||||||
|
keyDefinitionLike,
|
||||||
|
"2024-01-24T00:00:00.000Z",
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(helper.setToUser).toHaveBeenCalledWith("user-2", keyDefinitionLike, null);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("migrate", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
helper = mockMigrationHelper(exampleJSON(), 25);
|
||||||
|
sut = new RevertLastSyncMigrator(25, 26);
|
||||||
|
});
|
||||||
|
|
||||||
|
it.each(["user-1", "user-2"])("should null out new values", async (userId) => {
|
||||||
|
await sut.migrate(helper);
|
||||||
|
|
||||||
|
expect(helper.setToUser).toHaveBeenCalledWith(userId, keyDefinitionLike, null);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should add lastSync back to accounts", async () => {
|
||||||
|
await sut.migrate(helper);
|
||||||
|
|
||||||
|
expect(helper.set).toHaveBeenCalledWith("user-1", {
|
||||||
|
profile: {
|
||||||
|
lastSync: "2024-01-24T00:00:00.000Z",
|
||||||
|
otherStuff: "otherStuff4",
|
||||||
|
},
|
||||||
|
otherStuff: "otherStuff5",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should not try to restore values to missing accounts", async () => {
|
||||||
|
await sut.rollback(helper);
|
||||||
|
|
||||||
|
expect(helper.set).not.toHaveBeenCalledWith("user-2", any());
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,47 @@
|
|||||||
|
import { KeyDefinitionLike, MigrationHelper } from "../migration-helper";
|
||||||
|
import { Migrator } from "../migrator";
|
||||||
|
|
||||||
|
type ExpectedAccountType = {
|
||||||
|
profile?: {
|
||||||
|
lastSync?: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const LAST_SYNC_KEY: KeyDefinitionLike = {
|
||||||
|
key: "lastSync",
|
||||||
|
stateDefinition: {
|
||||||
|
name: "sync",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export class RevertLastSyncMigrator extends Migrator<25, 26> {
|
||||||
|
async rollback(helper: MigrationHelper): Promise<void> {
|
||||||
|
const accounts = await helper.getAccounts<ExpectedAccountType>();
|
||||||
|
async function rollbackAccount(userId: string, account: ExpectedAccountType): Promise<void> {
|
||||||
|
const value = account?.profile?.lastSync;
|
||||||
|
await helper.setToUser(userId, LAST_SYNC_KEY, value ?? null);
|
||||||
|
if (value != null) {
|
||||||
|
delete account.profile.lastSync;
|
||||||
|
await helper.set(userId, account);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await Promise.all([...accounts.map(({ userId, account }) => rollbackAccount(userId, account))]);
|
||||||
|
}
|
||||||
|
async migrate(helper: MigrationHelper): Promise<void> {
|
||||||
|
const accounts = await helper.getAccounts<ExpectedAccountType>();
|
||||||
|
|
||||||
|
async function migrateAccount(userId: string, account: ExpectedAccountType): Promise<void> {
|
||||||
|
const value = await helper.getFromUser(userId, LAST_SYNC_KEY);
|
||||||
|
if (account) {
|
||||||
|
account.profile = Object.assign(account.profile ?? {}, {
|
||||||
|
lastSync: value,
|
||||||
|
});
|
||||||
|
await helper.set(userId, account);
|
||||||
|
}
|
||||||
|
await helper.setToUser(userId, LAST_SYNC_KEY, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
await Promise.all([...accounts.map(({ userId, account }) => migrateAccount(userId, account))]);
|
||||||
|
}
|
||||||
|
}
|
@ -3,7 +3,7 @@ import { any, MockProxy } from "jest-mock-extended";
|
|||||||
import { StateDefinitionLike, MigrationHelper } from "../migration-helper";
|
import { StateDefinitionLike, MigrationHelper } from "../migration-helper";
|
||||||
import { mockMigrationHelper } from "../migration-helper.spec";
|
import { mockMigrationHelper } from "../migration-helper.spec";
|
||||||
|
|
||||||
import { BadgeSettingsMigrator } from "./26-move-badge-settings-to-state-providers";
|
import { BadgeSettingsMigrator } from "./27-move-badge-settings-to-state-providers";
|
||||||
|
|
||||||
function exampleJSON() {
|
function exampleJSON() {
|
||||||
return {
|
return {
|
||||||
@ -77,8 +77,8 @@ describe("BadgeSettingsMigrator", () => {
|
|||||||
|
|
||||||
describe("migrate", () => {
|
describe("migrate", () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
helper = mockMigrationHelper(exampleJSON(), 25);
|
helper = mockMigrationHelper(exampleJSON(), 26);
|
||||||
sut = new BadgeSettingsMigrator(25, 26);
|
sut = new BadgeSettingsMigrator(26, 27);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should remove disableBadgeCounter setting from all accounts", async () => {
|
it("should remove disableBadgeCounter setting from all accounts", async () => {
|
||||||
@ -117,8 +117,8 @@ describe("BadgeSettingsMigrator", () => {
|
|||||||
|
|
||||||
describe("rollback", () => {
|
describe("rollback", () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
helper = mockMigrationHelper(rollbackJSON(), 26);
|
helper = mockMigrationHelper(rollbackJSON(), 27);
|
||||||
sut = new BadgeSettingsMigrator(25, 26);
|
sut = new BadgeSettingsMigrator(26, 27);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should null out new values for each account", async () => {
|
it("should null out new values for each account", async () => {
|
@ -14,7 +14,7 @@ const enableBadgeCounterKeyDefinition: KeyDefinitionLike = {
|
|||||||
key: "enableBadgeCounter",
|
key: "enableBadgeCounter",
|
||||||
};
|
};
|
||||||
|
|
||||||
export class BadgeSettingsMigrator extends Migrator<25, 26> {
|
export class BadgeSettingsMigrator extends Migrator<26, 27> {
|
||||||
async migrate(helper: MigrationHelper): Promise<void> {
|
async migrate(helper: MigrationHelper): Promise<void> {
|
||||||
// account state (e.g. account settings -> state provider framework keys)
|
// account state (e.g. account settings -> state provider framework keys)
|
||||||
const accounts = await helper.getAccounts<ExpectedAccountState>();
|
const accounts = await helper.getAccounts<ExpectedAccountState>();
|
@ -6,7 +6,7 @@ import { mockMigrationHelper } from "../migration-helper.spec";
|
|||||||
import {
|
import {
|
||||||
BIOMETRIC_UNLOCK_ENABLED,
|
BIOMETRIC_UNLOCK_ENABLED,
|
||||||
MoveBiometricUnlockToStateProviders,
|
MoveBiometricUnlockToStateProviders,
|
||||||
} from "./27-move-biometric-unlock-to-state-providers";
|
} from "./28-move-biometric-unlock-to-state-providers";
|
||||||
|
|
||||||
function exampleJSON() {
|
function exampleJSON() {
|
||||||
return {
|
return {
|
||||||
@ -52,8 +52,8 @@ describe("MoveBiometricPromptsToStateProviders migrator", () => {
|
|||||||
|
|
||||||
describe("migrate", () => {
|
describe("migrate", () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
helper = mockMigrationHelper(exampleJSON(), 26);
|
helper = mockMigrationHelper(exampleJSON(), 27);
|
||||||
sut = new MoveBiometricUnlockToStateProviders(26, 27);
|
sut = new MoveBiometricUnlockToStateProviders(27, 28);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("removes biometricUnlock from all accounts", async () => {
|
it("removes biometricUnlock from all accounts", async () => {
|
||||||
@ -85,8 +85,8 @@ describe("MoveBiometricPromptsToStateProviders migrator", () => {
|
|||||||
|
|
||||||
describe("rollback", () => {
|
describe("rollback", () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
helper = mockMigrationHelper(rollbackJSON(), 27);
|
helper = mockMigrationHelper(rollbackJSON(), 28);
|
||||||
sut = new MoveBiometricUnlockToStateProviders(26, 27);
|
sut = new MoveBiometricUnlockToStateProviders(27, 28);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("nulls out new values", async () => {
|
it("nulls out new values", async () => {
|
@ -12,7 +12,7 @@ export const BIOMETRIC_UNLOCK_ENABLED: KeyDefinitionLike = {
|
|||||||
stateDefinition: { name: "biometricSettings" },
|
stateDefinition: { name: "biometricSettings" },
|
||||||
};
|
};
|
||||||
|
|
||||||
export class MoveBiometricUnlockToStateProviders extends Migrator<26, 27> {
|
export class MoveBiometricUnlockToStateProviders extends Migrator<27, 28> {
|
||||||
async migrate(helper: MigrationHelper): Promise<void> {
|
async migrate(helper: MigrationHelper): Promise<void> {
|
||||||
const legacyAccounts = await helper.getAccounts<ExpectedAccountType>();
|
const legacyAccounts = await helper.getAccounts<ExpectedAccountType>();
|
||||||
|
|
@ -1,18 +1,14 @@
|
|||||||
import { Observable } from "rxjs";
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
SyncCipherNotification,
|
SyncCipherNotification,
|
||||||
SyncFolderNotification,
|
SyncFolderNotification,
|
||||||
SyncSendNotification,
|
SyncSendNotification,
|
||||||
} from "../../../models/response/notification.response";
|
} from "../../../models/response/notification.response";
|
||||||
import { UserId } from "../../../types/guid";
|
|
||||||
|
|
||||||
export abstract class SyncService {
|
export abstract class SyncService {
|
||||||
syncInProgress: boolean;
|
syncInProgress: boolean;
|
||||||
lastSync$: Observable<Date | null>;
|
|
||||||
|
|
||||||
getLastSync: () => Promise<Date>;
|
getLastSync: () => Promise<Date>;
|
||||||
setLastSync: (date: Date, userId?: UserId) => Promise<any>;
|
setLastSync: (date: Date, userId?: string) => Promise<any>;
|
||||||
fullSync: (forceSync: boolean, allowThrowOnError?: boolean) => Promise<boolean>;
|
fullSync: (forceSync: boolean, allowThrowOnError?: boolean) => Promise<boolean>;
|
||||||
syncUpsertFolder: (notification: SyncFolderNotification, isEdit: boolean) => Promise<boolean>;
|
syncUpsertFolder: (notification: SyncFolderNotification, isEdit: boolean) => Promise<boolean>;
|
||||||
syncDeleteFolder: (notification: SyncFolderNotification) => Promise<boolean>;
|
syncDeleteFolder: (notification: SyncFolderNotification) => Promise<boolean>;
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
import { firstValueFrom, map } from "rxjs";
|
|
||||||
|
|
||||||
import { ApiService } from "../../../abstractions/api.service";
|
import { ApiService } from "../../../abstractions/api.service";
|
||||||
import { SettingsService } from "../../../abstractions/settings.service";
|
import { SettingsService } from "../../../abstractions/settings.service";
|
||||||
import { InternalOrganizationServiceAbstraction } from "../../../admin-console/abstractions/organization/organization.service.abstraction";
|
import { InternalOrganizationServiceAbstraction } from "../../../admin-console/abstractions/organization/organization.service.abstraction";
|
||||||
@ -25,12 +23,10 @@ import { MessagingService } from "../../../platform/abstractions/messaging.servi
|
|||||||
import { StateService } from "../../../platform/abstractions/state.service";
|
import { StateService } from "../../../platform/abstractions/state.service";
|
||||||
import { sequentialize } from "../../../platform/misc/sequentialize";
|
import { sequentialize } from "../../../platform/misc/sequentialize";
|
||||||
import { AccountDecryptionOptions } from "../../../platform/models/domain/account";
|
import { AccountDecryptionOptions } from "../../../platform/models/domain/account";
|
||||||
import { KeyDefinition, StateProvider, SYNC_STATE } from "../../../platform/state";
|
|
||||||
import { SendData } from "../../../tools/send/models/data/send.data";
|
import { SendData } from "../../../tools/send/models/data/send.data";
|
||||||
import { SendResponse } from "../../../tools/send/models/response/send.response";
|
import { SendResponse } from "../../../tools/send/models/response/send.response";
|
||||||
import { SendApiService } from "../../../tools/send/services/send-api.service.abstraction";
|
import { SendApiService } from "../../../tools/send/services/send-api.service.abstraction";
|
||||||
import { InternalSendService } from "../../../tools/send/services/send.service.abstraction";
|
import { InternalSendService } from "../../../tools/send/services/send.service.abstraction";
|
||||||
import { UserId } from "../../../types/guid";
|
|
||||||
import { CipherService } from "../../../vault/abstractions/cipher.service";
|
import { CipherService } from "../../../vault/abstractions/cipher.service";
|
||||||
import { FolderApiServiceAbstraction } from "../../../vault/abstractions/folder/folder-api.service.abstraction";
|
import { FolderApiServiceAbstraction } from "../../../vault/abstractions/folder/folder-api.service.abstraction";
|
||||||
import { InternalFolderService } from "../../../vault/abstractions/folder/folder.service.abstraction";
|
import { InternalFolderService } from "../../../vault/abstractions/folder/folder.service.abstraction";
|
||||||
@ -43,22 +39,8 @@ import { CollectionService } from "../../abstractions/collection.service";
|
|||||||
import { CollectionData } from "../../models/data/collection.data";
|
import { CollectionData } from "../../models/data/collection.data";
|
||||||
import { CollectionDetailsResponse } from "../../models/response/collection.response";
|
import { CollectionDetailsResponse } from "../../models/response/collection.response";
|
||||||
|
|
||||||
const LAST_SYNC_KEY = new KeyDefinition<string | null>(SYNC_STATE, "lastSync", {
|
|
||||||
deserializer: (value) => value,
|
|
||||||
});
|
|
||||||
|
|
||||||
export class SyncService implements SyncServiceAbstraction {
|
export class SyncService implements SyncServiceAbstraction {
|
||||||
private lastSyncState = this.stateProvider.getActive(LAST_SYNC_KEY);
|
|
||||||
|
|
||||||
syncInProgress = false;
|
syncInProgress = false;
|
||||||
lastSync$ = this.lastSyncState.state$.pipe(
|
|
||||||
map((value) => {
|
|
||||||
if (value == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return new Date(value);
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private apiService: ApiService,
|
private apiService: ApiService,
|
||||||
@ -77,20 +59,24 @@ export class SyncService implements SyncServiceAbstraction {
|
|||||||
private folderApiService: FolderApiServiceAbstraction,
|
private folderApiService: FolderApiServiceAbstraction,
|
||||||
private organizationService: InternalOrganizationServiceAbstraction,
|
private organizationService: InternalOrganizationServiceAbstraction,
|
||||||
private sendApiService: SendApiService,
|
private sendApiService: SendApiService,
|
||||||
private stateProvider: StateProvider,
|
|
||||||
private logoutCallback: (expired: boolean) => Promise<void>,
|
private logoutCallback: (expired: boolean) => Promise<void>,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async getLastSync(): Promise<Date | null> {
|
async getLastSync(): Promise<Date> {
|
||||||
return await firstValueFrom(this.lastSync$);
|
if ((await this.stateService.getUserId()) == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const lastSync = await this.stateService.getLastSync();
|
||||||
|
if (lastSync) {
|
||||||
|
return new Date(lastSync);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
async setLastSync(date: Date, userId?: UserId): Promise<any> {
|
async setLastSync(date: Date, userId?: string): Promise<any> {
|
||||||
if (userId !== undefined) {
|
await this.stateService.setLastSync(date.toJSON(), { userId: userId });
|
||||||
await this.stateProvider.getUser(userId, LAST_SYNC_KEY).update(() => date.toJSON());
|
|
||||||
} else {
|
|
||||||
await this.lastSyncState.update(() => date.toJSON());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@sequentialize(() => "fullSync")
|
@sequentialize(() => "fullSync")
|
||||||
|
Loading…
Reference in New Issue
Block a user