From b9c11f2e9f933bfcefc8e4c9848b630cd9cfba66 Mon Sep 17 00:00:00 2001 From: Siolto Date: Mon, 18 Nov 2024 15:00:01 +0100 Subject: [PATCH 01/42] WIP: Enhance LocalAccountDTO with deletion Info --- packages/app-runtime/src/AppConfig.ts | 6 ++ packages/app-runtime/src/AppRuntime.ts | 4 +- ...entityDeletionProcessStatusChangedEvent.ts | 10 +++ ...ntityDeletionProcessStatusChangedModule.ts | 38 +++++++++ .../src/modules/runtimeEvents/index.ts | 1 + .../multiAccount/MultiAccountController.ts | 18 +++++ .../src/multiAccount/data/LocalAccount.ts | 5 ++ .../src/multiAccount/data/LocalAccountDTO.ts | 1 + .../multiAccount/data/LocalAccountMapper.ts | 3 +- ...DeletionProcessStatusChangedModule.test.ts | 78 +++++++++++++++++++ .../test/transport/relationships.test.ts | 32 ++++++++ 11 files changed, 194 insertions(+), 2 deletions(-) create mode 100644 packages/app-runtime/src/events/IdentityDeletionProcessStatusChangedEvent.ts create mode 100644 packages/app-runtime/src/modules/runtimeEvents/IdentityDeletionProcessStatusChangedModule.ts create mode 100644 packages/app-runtime/test/modules/IdentityDeletionProcessStatusChangedModule.test.ts diff --git a/packages/app-runtime/src/AppConfig.ts b/packages/app-runtime/src/AppConfig.ts index 8f5144bc1..c6ea7679d 100644 --- a/packages/app-runtime/src/AppConfig.ts +++ b/packages/app-runtime/src/AppConfig.ts @@ -70,6 +70,12 @@ export function createAppConfig(...configs: AppConfigOverwrite[]): AppConfig { location: "relationshipTemplateProcessed", enabled: true }, + identityDeletionProcessStatusChanged: { + name: "identityDeletionProcessStatusChanged", + displayName: "Identity Deletion Process Status Changed Module", + location: "identityDeletionProcessStatusChanged", + enabled: true + }, decider: { displayName: "Decider Module", name: "DeciderModule", diff --git a/packages/app-runtime/src/AppRuntime.ts b/packages/app-runtime/src/AppRuntime.ts index 8538ecca9..0e9743986 100644 --- a/packages/app-runtime/src/AppRuntime.ts +++ b/packages/app-runtime/src/AppRuntime.ts @@ -16,6 +16,7 @@ import { AppRuntimeModuleConfiguration, AppSyncModule, IAppRuntimeModuleConstructor, + IdentityDeletionProcessStatusChangedModule, MailReceivedModule, MessageReceivedModule, OnboardingChangeReceivedModule, @@ -300,7 +301,8 @@ export class AppRuntime extends Runtime { onboardingChangeReceived: OnboardingChangeReceivedModule, messageReceived: MessageReceivedModule, relationshipChanged: RelationshipChangedModule, - relationshipTemplateProcessed: RelationshipTemplateProcessedModule + relationshipTemplateProcessed: RelationshipTemplateProcessedModule, + identityDeletionProcessStatusChanged: IdentityDeletionProcessStatusChangedModule }; public static registerModule(moduleName: string, ctor: IAppRuntimeModuleConstructor): void { diff --git a/packages/app-runtime/src/events/IdentityDeletionProcessStatusChangedEvent.ts b/packages/app-runtime/src/events/IdentityDeletionProcessStatusChangedEvent.ts new file mode 100644 index 000000000..5ce4a21f5 --- /dev/null +++ b/packages/app-runtime/src/events/IdentityDeletionProcessStatusChangedEvent.ts @@ -0,0 +1,10 @@ +import { DataEvent } from "@nmshd/runtime"; +import { IdentityDeletionProcess } from "@nmshd/transport"; + +export class IdentityDeletionProcessStatusChangedEvent extends DataEvent { + public static readonly namespace: string = "app.identityDeletionProcessStatusChanged"; + + public constructor(address: string, data: IdentityDeletionProcess) { + super(IdentityDeletionProcessStatusChangedEvent.namespace, address, data); + } +} diff --git a/packages/app-runtime/src/modules/runtimeEvents/IdentityDeletionProcessStatusChangedModule.ts b/packages/app-runtime/src/modules/runtimeEvents/IdentityDeletionProcessStatusChangedModule.ts new file mode 100644 index 000000000..adb4c63d7 --- /dev/null +++ b/packages/app-runtime/src/modules/runtimeEvents/IdentityDeletionProcessStatusChangedModule.ts @@ -0,0 +1,38 @@ +import { IdentityDeletionProcessStatus, IdentityDeletionProcessStatusChangedEvent } from "@nmshd/runtime"; +import { AppRuntimeError } from "../../AppRuntimeError"; +import { AppRuntimeModule, AppRuntimeModuleConfiguration } from "../AppRuntimeModule"; + +export interface IdentityDeletionProcessStatusChangedModuleConfig extends AppRuntimeModuleConfiguration {} + +export class IdentityDeletionProcessChangedModuleError extends AppRuntimeError {} + +export class IdentityDeletionProcessStatusChangedModule extends AppRuntimeModule { + public async init(): Promise { + // Nothing to do here + } + + public start(): Promise | void { + this.subscribeToEvent(IdentityDeletionProcessStatusChangedEvent, this.handleIdentityDeletionProcessStatusChanged.bind(this)); + } + + private async handleIdentityDeletionProcessStatusChanged(event: IdentityDeletionProcessStatusChangedEvent) { + if (event.data.status === IdentityDeletionProcessStatus.Approved) { + this.runtime.currentSession.account.deletionDate = event.data.gracePeriodEndsAt; + await this.runtime.multiAccountController.updateLocalAccountDeletionDate(event.eventTargetAddress, event.data.gracePeriodEndsAt); + } else { + delete this.runtime.currentSession.account.deletionDate; + await this.runtime.multiAccountController.updateLocalAccountDeletionDate(event.eventTargetAddress); + } + + // await this.runtime.currentSession.transportServices.account.syncEverything(); + // await this.runtime.currentSession.transportServices.account.syncDatawallet(); + + // fire LocalAccountChangedEvent + + // listen + } + + public override stop(): Promise | void { + this.unsubscribeFromAllEvents(); + } +} diff --git a/packages/app-runtime/src/modules/runtimeEvents/index.ts b/packages/app-runtime/src/modules/runtimeEvents/index.ts index 69825c2de..7a3710bda 100644 --- a/packages/app-runtime/src/modules/runtimeEvents/index.ts +++ b/packages/app-runtime/src/modules/runtimeEvents/index.ts @@ -1,2 +1,3 @@ +export * from "./IdentityDeletionProcessStatusChangedModule"; export * from "./MessageReceivedModule"; export * from "./RelationshipChangedModule"; diff --git a/packages/app-runtime/src/multiAccount/MultiAccountController.ts b/packages/app-runtime/src/multiAccount/MultiAccountController.ts index 55728c7ec..5503961fc 100644 --- a/packages/app-runtime/src/multiAccount/MultiAccountController.ts +++ b/packages/app-runtime/src/multiAccount/MultiAccountController.ts @@ -42,6 +42,7 @@ export class MultiAccountController { this._dbClosed = false; this._localAccounts = await this._db.getCollection("LocalAccounts"); + return this; } @@ -238,6 +239,23 @@ export class MultiAccountController { await this._localAccounts.update(oldAccount, renamedAccount); } + public async updateLocalAccountDeletionDate(address: string, deletionDate?: string): Promise { + const oldAccount = await this._localAccounts.findOne({ address }); + + if (!oldAccount) { + throw TransportCoreErrors.general.recordNotFound(LocalAccount, address).logWith(this._log); + } + + const account = LocalAccount.from(oldAccount); + if (deletionDate === undefined) { + delete account.deletionDate; + } else { + account.deletionDate = deletionDate; + } + + await this._localAccounts.update(oldAccount, account); + } + public async updateLastAccessedAt(accountId: string): Promise { const document = await this._localAccounts.read(accountId); if (!document) { diff --git a/packages/app-runtime/src/multiAccount/data/LocalAccount.ts b/packages/app-runtime/src/multiAccount/data/LocalAccount.ts index 4bcab0860..942fa28d4 100644 --- a/packages/app-runtime/src/multiAccount/data/LocalAccount.ts +++ b/packages/app-runtime/src/multiAccount/data/LocalAccount.ts @@ -9,6 +9,7 @@ export interface ILocalAccount extends ISerializable { order: number; lastAccessedAt?: ICoreDate; devicePushIdentifier?: string; + deletionDate?: string; } @type("LocalAccount") @@ -41,6 +42,10 @@ export class LocalAccount extends Serializable implements ILocalAccount { @serialize() public devicePushIdentifier?: string; + @validate({ nullable: true }) + @serialize() + public deletionDate?: string; + public static from(value: ILocalAccount): LocalAccount { return this.fromAny(value); } diff --git a/packages/app-runtime/src/multiAccount/data/LocalAccountDTO.ts b/packages/app-runtime/src/multiAccount/data/LocalAccountDTO.ts index 69bb38ede..43724520c 100644 --- a/packages/app-runtime/src/multiAccount/data/LocalAccountDTO.ts +++ b/packages/app-runtime/src/multiAccount/data/LocalAccountDTO.ts @@ -6,4 +6,5 @@ export interface LocalAccountDTO { order: number; lastAccessedAt?: string; devicePushIdentifier?: string; + deletionDate?: string; } diff --git a/packages/app-runtime/src/multiAccount/data/LocalAccountMapper.ts b/packages/app-runtime/src/multiAccount/data/LocalAccountMapper.ts index 4de37fd74..7cec2986c 100644 --- a/packages/app-runtime/src/multiAccount/data/LocalAccountMapper.ts +++ b/packages/app-runtime/src/multiAccount/data/LocalAccountMapper.ts @@ -10,7 +10,8 @@ export class LocalAccountMapper { directory: localAccount.directory.toString(), order: localAccount.order, lastAccessedAt: localAccount.lastAccessedAt?.toString(), - devicePushIdentifier: localAccount.devicePushIdentifier + devicePushIdentifier: localAccount.devicePushIdentifier, + deletionDate: localAccount.deletionDate }; } } diff --git a/packages/app-runtime/test/modules/IdentityDeletionProcessStatusChangedModule.test.ts b/packages/app-runtime/test/modules/IdentityDeletionProcessStatusChangedModule.test.ts new file mode 100644 index 000000000..a078b89ee --- /dev/null +++ b/packages/app-runtime/test/modules/IdentityDeletionProcessStatusChangedModule.test.ts @@ -0,0 +1,78 @@ +import { Serializable } from "@js-soft/ts-serval"; +import { AppRuntime, DatawalletSynchronizedEvent, LocalAccountDTO, LocalAccountSession, RemoteNotificationEvent } from "../../src"; +import { TestUtil } from "../lib"; + +describe("IdentityDeletionProcessStatusChangedTest", function () { + let runtime: AppRuntime; + + let session: LocalAccountSession; + let accounts: LocalAccountDTO[]; + + beforeAll(async function () { + runtime = await TestUtil.createRuntime(); + await runtime.start(); + + accounts = await TestUtil.provideAccounts(runtime, 1); + + session = await runtime.selectAccount(accounts[0].id); + }); + + afterAll(async function () { + await runtime.stop(); + }); + + test("bli bla blub", async function () { + // await session.transportServices.identityDeletionProcesses.initiateIdentityDeletionProcess(); + + const account = await runtime.accountServices.getAccount(accounts[0].id); + + expect(account.deletionDate).toBeUndefined(); + + // await runtime.stop(); + // await runtime.start(); + // const account = await runtime.accountServices.getAccount(accounts[0].id); + + // expect(account.deletionDate).toBeDefined(); + + // await session.transportServices.identityDeletionProcesses.cancelIdentityDeletionProcess(); + + // expect(session.account.deletionDate).toBeUndefined(); + }); + + test.only("multiDevice information", async function () { + const runtimeDevice2 = await TestUtil.createRuntime(); + await runtimeDevice2.start(); + // const accountsDevice2 = TestUtil.provideAccounts(runtimeDevice2, 1); + + const newDevice = await session.transportServices.devices.createDevice({ name: "test", isAdmin: true }); + const token = await session.transportServices.devices.getDeviceOnboardingToken({ id: newDevice.value.id, profileName: "Test" }); + // const value = Serializable.fromUnknown(token.value.content) + const content: any = Serializable.fromUnknown(token.value.content); + + const [device] = await runtimeDevice2.multiAccountController.onboardDevice(content.sharedSecret, "test"); + // const session2 = await runtimeDevice2.selectAccount(device.id.toString()); + + // const account = await runtimeDevice2.accountServices.getAccount(accounts[0].id); + + await session.transportServices.identityDeletionProcesses.initiateIdentityDeletionProcess(); + + runtimeDevice2.nativeEnvironment.eventBus.publish( + new RemoteNotificationEvent({ + content: { + devicePushIdentifier: accounts[0].devicePushIdentifier, + eventName: "DatawalletModificationsCreated", + sentAt: new Date().toISOString(), + payload: {} + } + }) + ); + + const event = await TestUtil.awaitEvent(runtimeDevice2, DatawalletSynchronizedEvent); + + expect(event).toBeDefined(); + const session2 = await runtimeDevice2.selectAccount(device.id.toString()); + + // const account = await runtimeDevice2.accountServices.getAccount(accounts[0].id); + console.log(session2); + }, 120000); +}); diff --git a/packages/runtime/test/transport/relationships.test.ts b/packages/runtime/test/transport/relationships.test.ts index 42d163409..db3df4ef9 100644 --- a/packages/runtime/test/transport/relationships.test.ts +++ b/packages/runtime/test/transport/relationships.test.ts @@ -347,6 +347,38 @@ describe("Relationship status validations on active relationship", () => { }); }); +describe.only("Templator with active IdentityDeletionProcess", () => { + const serviceProvider = new RuntimeServiceProvider(); + let services1: TestRuntimeServices; + let services2: TestRuntimeServices; + + beforeAll(async () => { + const runtimeServices = await serviceProvider.launch(2, { enableRequestModule: true, enableDeciderModule: true, enableNotificationModule: true }); + services1 = runtimeServices[0]; + services2 = runtimeServices[1]; + }, 30000); + + afterAll(() => serviceProvider.stop()); + + test.only("returns error if templator has active IdentityDeletionProcess", async () => { + const templateId = (await exchangeTemplate(services1.transport, services2.transport)).id; + + const createRelationshipResponse = await services2.transport.relationships.createRelationship({ + templateId: templateId, + creationContent: emptyRelationshipCreationContent + }); + await services1.transport.identityDeletionProcesses.initiateIdentityDeletionProcess(); + + const response = await services2.transport.relationships.getRelationships({}); + + console.log(response); + // expect(createRelationshipResponse).toBeAnError( + // "The Identity who created the RelationshipTemplate is currently in the process of deleting itself. Thus, it is not possible to establish a Relationship to it.", + // "error.transport.relationships.activeIdentityDeletionProcessOfOwnerOfRelationshipTemplate" + // ); + }); +}); + describe("Relationships query", () => { test("query own relationship", async () => { await ensureActiveRelationship(services1.transport, services2.transport); From 4c04d6b3e2d79d5257d0bf7f8a4c599f7ae6bf6b Mon Sep 17 00:00:00 2001 From: Milena Czierlinski Date: Mon, 18 Nov 2024 18:46:50 +0100 Subject: [PATCH 02/42] feat: try to untangle changes --- ...entityDeletionProcessStatusChangedEvent.ts | 10 -- .../LocalAccountDeletionDateChangedEvent.ts | 9 ++ packages/app-runtime/src/events/index.ts | 1 + .../LocalAccountDeletionDateChangedModule.ts | 29 ++++ .../src/modules/appEvents/index.ts | 1 + ...ntityDeletionProcessStatusChangedModule.ts | 33 +++-- .../multiAccount/MultiAccountController.ts | 6 +- ...DeletionProcessStatusChangedModule.test.ts | 125 ++++++++++++------ 8 files changed, 150 insertions(+), 64 deletions(-) delete mode 100644 packages/app-runtime/src/events/IdentityDeletionProcessStatusChangedEvent.ts create mode 100644 packages/app-runtime/src/events/LocalAccountDeletionDateChangedEvent.ts create mode 100644 packages/app-runtime/src/modules/appEvents/LocalAccountDeletionDateChangedModule.ts diff --git a/packages/app-runtime/src/events/IdentityDeletionProcessStatusChangedEvent.ts b/packages/app-runtime/src/events/IdentityDeletionProcessStatusChangedEvent.ts deleted file mode 100644 index 5ce4a21f5..000000000 --- a/packages/app-runtime/src/events/IdentityDeletionProcessStatusChangedEvent.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { DataEvent } from "@nmshd/runtime"; -import { IdentityDeletionProcess } from "@nmshd/transport"; - -export class IdentityDeletionProcessStatusChangedEvent extends DataEvent { - public static readonly namespace: string = "app.identityDeletionProcessStatusChanged"; - - public constructor(address: string, data: IdentityDeletionProcess) { - super(IdentityDeletionProcessStatusChangedEvent.namespace, address, data); - } -} diff --git a/packages/app-runtime/src/events/LocalAccountDeletionDateChangedEvent.ts b/packages/app-runtime/src/events/LocalAccountDeletionDateChangedEvent.ts new file mode 100644 index 000000000..18d32f0f6 --- /dev/null +++ b/packages/app-runtime/src/events/LocalAccountDeletionDateChangedEvent.ts @@ -0,0 +1,9 @@ +import { DataEvent } from "@nmshd/runtime"; + +export class LocalAccountDeletionDateChangedEvent extends DataEvent { + public static readonly namespace: string = "app.localAccountDeletionDateChanged"; + + public constructor(address: string, data?: string) { + super(LocalAccountDeletionDateChangedEvent.namespace, address, data); + } +} diff --git a/packages/app-runtime/src/events/index.ts b/packages/app-runtime/src/events/index.ts index 5b512897c..fbf70b343 100644 --- a/packages/app-runtime/src/events/index.ts +++ b/packages/app-runtime/src/events/index.ts @@ -1,6 +1,7 @@ export * from "./AccountSelectedEvent"; export * from "./DatawalletSynchronizedEvent"; export * from "./ExternalEventReceivedEvent"; +export * from "./LocalAccountDeletionDateChangedEvent"; export * from "./MailReceivedEvent"; export * from "./OnboardingChangeReceivedEvent"; export * from "./RelationshipSelectedEvent"; diff --git a/packages/app-runtime/src/modules/appEvents/LocalAccountDeletionDateChangedModule.ts b/packages/app-runtime/src/modules/appEvents/LocalAccountDeletionDateChangedModule.ts new file mode 100644 index 000000000..353822870 --- /dev/null +++ b/packages/app-runtime/src/modules/appEvents/LocalAccountDeletionDateChangedModule.ts @@ -0,0 +1,29 @@ +import { AppRuntimeError } from "../../AppRuntimeError"; +import { LocalAccountDeletionDateChangedEvent } from "../../events"; +import { AppRuntimeModule, AppRuntimeModuleConfiguration } from "../AppRuntimeModule"; + +export interface LocalAccountDeletionDateChangedModuleConfig extends AppRuntimeModuleConfiguration {} + +export class LocalAccountDeletionDateChangedModuleError extends AppRuntimeError {} + +export class LocalAccountDeletionDateChangedModule extends AppRuntimeModule { + public async init(): Promise { + // Nothing to do here + } + + public start(): void { + this.subscribeToEvent(LocalAccountDeletionDateChangedEvent, this.handleLocalDeletionDateChanged.bind(this)); + } + + private async handleLocalDeletionDateChanged(event: LocalAccountDeletionDateChangedEvent) { + const session = await this.runtime.getOrCreateSession(event.eventTargetAddress); + + const deletionDate = event.data; + + // do something + } + + public stop(): void { + this.unsubscribeFromAllEvents(); + } +} diff --git a/packages/app-runtime/src/modules/appEvents/index.ts b/packages/app-runtime/src/modules/appEvents/index.ts index d4097f75f..b1eecc54b 100644 --- a/packages/app-runtime/src/modules/appEvents/index.ts +++ b/packages/app-runtime/src/modules/appEvents/index.ts @@ -1,4 +1,5 @@ export * from "./AppLaunchModule"; +export * from "./LocalAccountDeletionDateChangedModule"; export * from "./MailReceivedModule"; export * from "./OnboardingChangeReceivedModule"; export * from "./RelationshipTemplateProcessedModule"; diff --git a/packages/app-runtime/src/modules/runtimeEvents/IdentityDeletionProcessStatusChangedModule.ts b/packages/app-runtime/src/modules/runtimeEvents/IdentityDeletionProcessStatusChangedModule.ts index adb4c63d7..921d82aac 100644 --- a/packages/app-runtime/src/modules/runtimeEvents/IdentityDeletionProcessStatusChangedModule.ts +++ b/packages/app-runtime/src/modules/runtimeEvents/IdentityDeletionProcessStatusChangedModule.ts @@ -1,5 +1,6 @@ import { IdentityDeletionProcessStatus, IdentityDeletionProcessStatusChangedEvent } from "@nmshd/runtime"; import { AppRuntimeError } from "../../AppRuntimeError"; +import { LocalAccountDeletionDateChangedEvent } from "../../events"; import { AppRuntimeModule, AppRuntimeModuleConfiguration } from "../AppRuntimeModule"; export interface IdentityDeletionProcessStatusChangedModuleConfig extends AppRuntimeModuleConfiguration {} @@ -16,20 +17,30 @@ export class IdentityDeletionProcessStatusChangedModule extends AppRuntimeModule } private async handleIdentityDeletionProcessStatusChanged(event: IdentityDeletionProcessStatusChangedEvent) { - if (event.data.status === IdentityDeletionProcessStatus.Approved) { - this.runtime.currentSession.account.deletionDate = event.data.gracePeriodEndsAt; - await this.runtime.multiAccountController.updateLocalAccountDeletionDate(event.eventTargetAddress, event.data.gracePeriodEndsAt); - } else { - delete this.runtime.currentSession.account.deletionDate; - await this.runtime.multiAccountController.updateLocalAccountDeletionDate(event.eventTargetAddress); - } + const identityDeletionProcess = event.data; + + switch (identityDeletionProcess.status) { + case IdentityDeletionProcessStatus.Approved: + // TODO: why do we have to do this twice? + this.runtime.currentSession.account.deletionDate = event.data.gracePeriodEndsAt; + await this.runtime.multiAccountController.updateLocalAccountDeletionDate(event.eventTargetAddress, identityDeletionProcess.gracePeriodEndsAt); + + this.runtime.eventBus.publish(new LocalAccountDeletionDateChangedEvent(event.eventTargetAddress, identityDeletionProcess.gracePeriodEndsAt)); + break; - // await this.runtime.currentSession.transportServices.account.syncEverything(); - // await this.runtime.currentSession.transportServices.account.syncDatawallet(); + case IdentityDeletionProcessStatus.Cancelled: + const previousDeletionDate = this.runtime.currentSession.account.deletionDate; + if (!previousDeletionDate) break; - // fire LocalAccountChangedEvent + this.runtime.currentSession.account.deletionDate = undefined; + await this.runtime.multiAccountController.updateLocalAccountDeletionDate(event.eventTargetAddress); - // listen + this.runtime.eventBus.publish(new LocalAccountDeletionDateChangedEvent(event.eventTargetAddress)); + break; + + default: + break; + } } public override stop(): Promise | void { diff --git a/packages/app-runtime/src/multiAccount/MultiAccountController.ts b/packages/app-runtime/src/multiAccount/MultiAccountController.ts index 5503961fc..80f0d8b63 100644 --- a/packages/app-runtime/src/multiAccount/MultiAccountController.ts +++ b/packages/app-runtime/src/multiAccount/MultiAccountController.ts @@ -247,12 +247,8 @@ export class MultiAccountController { } const account = LocalAccount.from(oldAccount); - if (deletionDate === undefined) { - delete account.deletionDate; - } else { - account.deletionDate = deletionDate; - } + account.deletionDate = deletionDate ?? undefined; await this._localAccounts.update(oldAccount, account); } diff --git a/packages/app-runtime/test/modules/IdentityDeletionProcessStatusChangedModule.test.ts b/packages/app-runtime/test/modules/IdentityDeletionProcessStatusChangedModule.test.ts index a078b89ee..cc59b1c4a 100644 --- a/packages/app-runtime/test/modules/IdentityDeletionProcessStatusChangedModule.test.ts +++ b/packages/app-runtime/test/modules/IdentityDeletionProcessStatusChangedModule.test.ts @@ -1,18 +1,17 @@ import { Serializable } from "@js-soft/ts-serval"; -import { AppRuntime, DatawalletSynchronizedEvent, LocalAccountDTO, LocalAccountSession, RemoteNotificationEvent } from "../../src"; -import { TestUtil } from "../lib"; +import { IdentityDeletionProcessStatusChangedEvent } from "@nmshd/runtime"; +import { AppRuntime, DatawalletSynchronizedEvent, LocalAccountDeletionDateChangedEvent, LocalAccountSession, RemoteNotificationEvent } from "../../src"; +import { EventListener, TestUtil } from "../lib"; describe("IdentityDeletionProcessStatusChangedTest", function () { let runtime: AppRuntime; - let session: LocalAccountSession; - let accounts: LocalAccountDTO[]; beforeAll(async function () { runtime = await TestUtil.createRuntime(); await runtime.start(); - accounts = await TestUtil.provideAccounts(runtime, 1); + const accounts = await TestUtil.provideAccounts(runtime, 1); session = await runtime.selectAccount(accounts[0].id); }); @@ -21,25 +20,60 @@ describe("IdentityDeletionProcessStatusChangedTest", function () { await runtime.stop(); }); - test("bli bla blub", async function () { - // await session.transportServices.identityDeletionProcesses.initiateIdentityDeletionProcess(); + test("should fire an event and set the deletionDate of the LocalAccount initiating an IdentityDeletionProcess", async function () { + expect(session.account.deletionDate).toBeUndefined(); + + const eventListener = new EventListener(runtime, [LocalAccountDeletionDateChangedEvent, IdentityDeletionProcessStatusChangedEvent]); + eventListener.start(); + + const initiateDeletionResult = await session.transportServices.identityDeletionProcesses.initiateIdentityDeletionProcess(); + + eventListener.stop(); + const events = eventListener.getReceivedEvents(); + expect(events).toHaveLength(2); + + const identityDeletionProcessStatusChangedEvent = events[0].instance as IdentityDeletionProcessStatusChangedEvent; + expect(identityDeletionProcessStatusChangedEvent).toBeInstanceOf(IdentityDeletionProcessStatusChangedEvent); + expect(identityDeletionProcessStatusChangedEvent.data).toBeDefined(); + expect(identityDeletionProcessStatusChangedEvent.data.id).toBe(initiateDeletionResult.value.id); + + const localAccountDeletionDateChangedEvent = events[1].instance as LocalAccountDeletionDateChangedEvent; + expect(localAccountDeletionDateChangedEvent).toBeInstanceOf(LocalAccountDeletionDateChangedEvent); + expect(localAccountDeletionDateChangedEvent.data).toBeDefined(); + expect(localAccountDeletionDateChangedEvent.data).toBe(initiateDeletionResult.value.gracePeriodEndsAt); + + expect(session.account.deletionDate).toBeDefined(); + expect(session.account.deletionDate).toBe(initiateDeletionResult.value.gracePeriodEndsAt); + + await session.transportServices.identityDeletionProcesses.cancelIdentityDeletionProcess(); + expect(session.account.deletionDate).toBeUndefined(); + }); + + test("should fire an event and set the deletionDate of the LocalAccount cancelling an IdentityDeletionProcess", async function () { + await session.transportServices.identityDeletionProcesses.initiateIdentityDeletionProcess(); - const account = await runtime.accountServices.getAccount(accounts[0].id); + const eventListener = new EventListener(runtime, [LocalAccountDeletionDateChangedEvent, IdentityDeletionProcessStatusChangedEvent]); + eventListener.start(); - expect(account.deletionDate).toBeUndefined(); + const cancelDeletionResult = await session.transportServices.identityDeletionProcesses.cancelIdentityDeletionProcess(); - // await runtime.stop(); - // await runtime.start(); - // const account = await runtime.accountServices.getAccount(accounts[0].id); + eventListener.stop(); + const events = eventListener.getReceivedEvents(); + expect(events).toHaveLength(2); - // expect(account.deletionDate).toBeDefined(); + const identityDeletionProcessStatusChangedEvent = events[0].instance as IdentityDeletionProcessStatusChangedEvent; + expect(identityDeletionProcessStatusChangedEvent).toBeInstanceOf(IdentityDeletionProcessStatusChangedEvent); + expect(identityDeletionProcessStatusChangedEvent.data).toBeDefined(); + expect(identityDeletionProcessStatusChangedEvent.data.id).toBe(cancelDeletionResult.value.id); - // await session.transportServices.identityDeletionProcesses.cancelIdentityDeletionProcess(); + const localAccountDeletionDateChangedEvent = events[1].instance as LocalAccountDeletionDateChangedEvent; + expect(localAccountDeletionDateChangedEvent).toBeInstanceOf(LocalAccountDeletionDateChangedEvent); + expect(localAccountDeletionDateChangedEvent.data).toBeUndefined(); - // expect(session.account.deletionDate).toBeUndefined(); + expect(session.account.deletionDate).toBeUndefined(); }); - test.only("multiDevice information", async function () { + test("multiDevice information", async function () { const runtimeDevice2 = await TestUtil.createRuntime(); await runtimeDevice2.start(); // const accountsDevice2 = TestUtil.provideAccounts(runtimeDevice2, 1); @@ -49,30 +83,45 @@ describe("IdentityDeletionProcessStatusChangedTest", function () { // const value = Serializable.fromUnknown(token.value.content) const content: any = Serializable.fromUnknown(token.value.content); - const [device] = await runtimeDevice2.multiAccountController.onboardDevice(content.sharedSecret, "test"); - // const session2 = await runtimeDevice2.selectAccount(device.id.toString()); - + const [account2] = await runtimeDevice2.multiAccountController.onboardDevice(content.sharedSecret, "test"); + const session2 = await runtimeDevice2.selectAccount(account2.id.toString()); // const account = await runtimeDevice2.accountServices.getAccount(accounts[0].id); - await session.transportServices.identityDeletionProcesses.initiateIdentityDeletionProcess(); - - runtimeDevice2.nativeEnvironment.eventBus.publish( - new RemoteNotificationEvent({ - content: { - devicePushIdentifier: accounts[0].devicePushIdentifier, - eventName: "DatawalletModificationsCreated", - sentAt: new Date().toISOString(), - payload: {} - } - }) - ); - - const event = await TestUtil.awaitEvent(runtimeDevice2, DatawalletSynchronizedEvent); - - expect(event).toBeDefined(); - const session2 = await runtimeDevice2.selectAccount(device.id.toString()); + const eventListener = new EventListener(runtime, [ + LocalAccountDeletionDateChangedEvent, + IdentityDeletionProcessStatusChangedEvent, + DatawalletSynchronizedEvent, + RemoteNotificationEvent + ]); + eventListener.start(); + + await session2.transportServices.identityDeletionProcesses.initiateIdentityDeletionProcess(); + + eventListener.stop(); + const events = eventListener.getReceivedEvents(); + // expect(events).toHaveLength(2); + + // runtimeDevice2.nativeEnvironment.eventBus.publish( + // new RemoteNotificationEvent({ + // content: { + // devicePushIdentifier: accounts[0].devicePushIdentifier, + // eventName: "DatawalletModificationsCreated", + // sentAt: new Date().toISOString(), + // payload: {} + // } + // }) + // ); + + // const event = await TestUtil.awaitEvent(runtimeDevice2, DatawalletSynchronizedEvent); + // const event = await TestUtil.awaitEvent(runtimeDevice2, LocalAccountDeletionDateChangedEvent); + + // expect(event).toBeDefined(); + // console.log(event); + // const session2 = await runtimeDevice2.selectAccount(device.id.toString()); // const account = await runtimeDevice2.accountServices.getAccount(accounts[0].id); - console.log(session2); - }, 120000); + // console.log(session2); + + await runtimeDevice2.stop(); + }); }); From 6c0696d5f7abbe507070691bd7cd59b37d02c48c Mon Sep 17 00:00:00 2001 From: Milena Czierlinski Date: Tue, 19 Nov 2024 14:50:26 +0100 Subject: [PATCH 03/42] feat: add getAccounts(Not)InDeletion functions --- .../src/multiAccount/MultiAccountController.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/packages/app-runtime/src/multiAccount/MultiAccountController.ts b/packages/app-runtime/src/multiAccount/MultiAccountController.ts index 80f0d8b63..8d3e91c38 100644 --- a/packages/app-runtime/src/multiAccount/MultiAccountController.ts +++ b/packages/app-runtime/src/multiAccount/MultiAccountController.ts @@ -82,6 +82,19 @@ export class MultiAccountController { return dbAccounts.map((account) => LocalAccount.from(account)); } + // TODO: test + public async getAccountsInDeletion(): Promise { + const allAccounts = await this.getAccounts(); + const accountsInDeletion = allAccounts.filter((item) => item.deletionDate === undefined); + return accountsInDeletion; + } + + public async getAccountsNotInDeletion(): Promise { + const allAccounts = await this.getAccounts(); + const accountsNotInDeletion = allAccounts.filter((item) => item.deletionDate !== undefined); + return accountsNotInDeletion; + } + public async selectAccount(id: CoreId): Promise<[LocalAccount, AccountController]> { this._log.trace(`Selecting LocalAccount with id ${id}...`); const account = await this._localAccounts.read(id.toString()); From b7168078dc129c08eb0c51765e05667d73d3d7ea Mon Sep 17 00:00:00 2001 From: Milena Czierlinski Date: Tue, 19 Nov 2024 18:05:58 +0100 Subject: [PATCH 04/42] test: clean up IdentityDeletionProcessStatusChangedModule test --- ...DeletionProcessStatusChangedModule.test.ts | 105 +++++++++--------- 1 file changed, 51 insertions(+), 54 deletions(-) diff --git a/packages/app-runtime/test/modules/IdentityDeletionProcessStatusChangedModule.test.ts b/packages/app-runtime/test/modules/IdentityDeletionProcessStatusChangedModule.test.ts index cc59b1c4a..a3b00e236 100644 --- a/packages/app-runtime/test/modules/IdentityDeletionProcessStatusChangedModule.test.ts +++ b/packages/app-runtime/test/modules/IdentityDeletionProcessStatusChangedModule.test.ts @@ -1,9 +1,9 @@ import { Serializable } from "@js-soft/ts-serval"; -import { IdentityDeletionProcessStatusChangedEvent } from "@nmshd/runtime"; -import { AppRuntime, DatawalletSynchronizedEvent, LocalAccountDeletionDateChangedEvent, LocalAccountSession, RemoteNotificationEvent } from "../../src"; +import { IdentityDeletionProcessStatus, IdentityDeletionProcessStatusChangedEvent } from "@nmshd/runtime"; +import { AppRuntime, LocalAccountDeletionDateChangedEvent, LocalAccountSession } from "../../src"; import { EventListener, TestUtil } from "../lib"; -describe("IdentityDeletionProcessStatusChangedTest", function () { +describe("IdentityDeletionProcessStatusChanged", function () { let runtime: AppRuntime; let session: LocalAccountSession; @@ -16,6 +16,19 @@ describe("IdentityDeletionProcessStatusChangedTest", function () { session = await runtime.selectAccount(accounts[0].id); }); + afterEach(async () => { + const activeIdentityDeletionProcess = await session.transportServices.identityDeletionProcesses.getActiveIdentityDeletionProcess(); + if (!activeIdentityDeletionProcess.isSuccess) { + return; + } + + let abortResult; + if (activeIdentityDeletionProcess.value.status === IdentityDeletionProcessStatus.Approved) { + abortResult = await session.transportServices.identityDeletionProcesses.cancelIdentityDeletionProcess(); + } + if (abortResult?.isError) throw abortResult.error; + }); + afterAll(async function () { await runtime.stop(); }); @@ -44,9 +57,6 @@ describe("IdentityDeletionProcessStatusChangedTest", function () { expect(session.account.deletionDate).toBeDefined(); expect(session.account.deletionDate).toBe(initiateDeletionResult.value.gracePeriodEndsAt); - - await session.transportServices.identityDeletionProcesses.cancelIdentityDeletionProcess(); - expect(session.account.deletionDate).toBeUndefined(); }); test("should fire an event and set the deletionDate of the LocalAccount cancelling an IdentityDeletionProcess", async function () { @@ -73,55 +83,42 @@ describe("IdentityDeletionProcessStatusChangedTest", function () { expect(session.account.deletionDate).toBeUndefined(); }); - test("multiDevice information", async function () { - const runtimeDevice2 = await TestUtil.createRuntime(); - await runtimeDevice2.start(); - // const accountsDevice2 = TestUtil.provideAccounts(runtimeDevice2, 1); - - const newDevice = await session.transportServices.devices.createDevice({ name: "test", isAdmin: true }); - const token = await session.transportServices.devices.getDeviceOnboardingToken({ id: newDevice.value.id, profileName: "Test" }); - // const value = Serializable.fromUnknown(token.value.content) - const content: any = Serializable.fromUnknown(token.value.content); - - const [account2] = await runtimeDevice2.multiAccountController.onboardDevice(content.sharedSecret, "test"); - const session2 = await runtimeDevice2.selectAccount(account2.id.toString()); - // const account = await runtimeDevice2.accountServices.getAccount(accounts[0].id); - - const eventListener = new EventListener(runtime, [ - LocalAccountDeletionDateChangedEvent, - IdentityDeletionProcessStatusChangedEvent, - DatawalletSynchronizedEvent, - RemoteNotificationEvent - ]); - eventListener.start(); + describe("multiple devices", function () { + let runtimeDevice2: AppRuntime; + let sessionDevice2: LocalAccountSession; - await session2.transportServices.identityDeletionProcesses.initiateIdentityDeletionProcess(); + beforeAll(async function () { + runtimeDevice2 = await TestUtil.createRuntime(); + await runtimeDevice2.start(); - eventListener.stop(); - const events = eventListener.getReceivedEvents(); - // expect(events).toHaveLength(2); - - // runtimeDevice2.nativeEnvironment.eventBus.publish( - // new RemoteNotificationEvent({ - // content: { - // devicePushIdentifier: accounts[0].devicePushIdentifier, - // eventName: "DatawalletModificationsCreated", - // sentAt: new Date().toISOString(), - // payload: {} - // } - // }) - // ); - - // const event = await TestUtil.awaitEvent(runtimeDevice2, DatawalletSynchronizedEvent); - // const event = await TestUtil.awaitEvent(runtimeDevice2, LocalAccountDeletionDateChangedEvent); - - // expect(event).toBeDefined(); - // console.log(event); - // const session2 = await runtimeDevice2.selectAccount(device.id.toString()); - - // const account = await runtimeDevice2.accountServices.getAccount(accounts[0].id); - // console.log(session2); - - await runtimeDevice2.stop(); + const newDevice = await session.transportServices.devices.createDevice({ name: "test", isAdmin: true }); + const token = await session.transportServices.devices.getDeviceOnboardingToken({ id: newDevice.value.id, profileName: "Test" }); + const content: any = Serializable.fromUnknown(token.value.content); + + const [accountDevice2] = await runtimeDevice2.multiAccountController.onboardDevice(content.sharedSecret, "test"); + sessionDevice2 = await runtimeDevice2.selectAccount(accountDevice2.id.toString()); + }); + + afterAll(async function () { + await runtimeDevice2.stop(); + }); + + test("should set the deletionDate of the LocalAccount on a second device that is online", async function () { + await session.transportServices.identityDeletionProcesses.initiateIdentityDeletionProcess(); + expect(sessionDevice2.account.deletionDate).toBeUndefined(); + + await sessionDevice2.transportServices.account.syncDatawallet(); + expect(sessionDevice2.account.deletionDate).toBeDefined(); + }); + + test("should set the deletionDate of the LocalAccount on a second device that was offline", async function () { + await runtimeDevice2.stop(); + await session.transportServices.identityDeletionProcesses.initiateIdentityDeletionProcess(); + expect(sessionDevice2.account.deletionDate).toBeUndefined(); + + await runtimeDevice2.start(); + await sessionDevice2.transportServices.account.syncDatawallet(); + expect(sessionDevice2.account.deletionDate).toBeDefined(); + }); }); }); From 29d131363fd94fe02c24f4acc1a2d4a4dabb8878 Mon Sep 17 00:00:00 2001 From: Milena Czierlinski Date: Wed, 20 Nov 2024 15:07:43 +0100 Subject: [PATCH 05/42] refactor: stuff --- ...DeletionProcessStatusChangedModule.test.ts | 44 +++++++++---------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/packages/app-runtime/test/modules/IdentityDeletionProcessStatusChangedModule.test.ts b/packages/app-runtime/test/modules/IdentityDeletionProcessStatusChangedModule.test.ts index a3b00e236..924445404 100644 --- a/packages/app-runtime/test/modules/IdentityDeletionProcessStatusChangedModule.test.ts +++ b/packages/app-runtime/test/modules/IdentityDeletionProcessStatusChangedModule.test.ts @@ -4,42 +4,42 @@ import { AppRuntime, LocalAccountDeletionDateChangedEvent, LocalAccountSession } import { EventListener, TestUtil } from "../lib"; describe("IdentityDeletionProcessStatusChanged", function () { - let runtime: AppRuntime; - let session: LocalAccountSession; + let runtimeDevice1: AppRuntime; + let sessionDevice1: LocalAccountSession; beforeAll(async function () { - runtime = await TestUtil.createRuntime(); - await runtime.start(); + runtimeDevice1 = await TestUtil.createRuntime(); + await runtimeDevice1.start(); - const accounts = await TestUtil.provideAccounts(runtime, 1); + const accounts = await TestUtil.provideAccounts(runtimeDevice1, 1); - session = await runtime.selectAccount(accounts[0].id); + sessionDevice1 = await runtimeDevice1.selectAccount(accounts[0].id); }); afterEach(async () => { - const activeIdentityDeletionProcess = await session.transportServices.identityDeletionProcesses.getActiveIdentityDeletionProcess(); + const activeIdentityDeletionProcess = await sessionDevice1.transportServices.identityDeletionProcesses.getActiveIdentityDeletionProcess(); if (!activeIdentityDeletionProcess.isSuccess) { return; } let abortResult; if (activeIdentityDeletionProcess.value.status === IdentityDeletionProcessStatus.Approved) { - abortResult = await session.transportServices.identityDeletionProcesses.cancelIdentityDeletionProcess(); + abortResult = await sessionDevice1.transportServices.identityDeletionProcesses.cancelIdentityDeletionProcess(); } if (abortResult?.isError) throw abortResult.error; }); afterAll(async function () { - await runtime.stop(); + await runtimeDevice1.stop(); }); test("should fire an event and set the deletionDate of the LocalAccount initiating an IdentityDeletionProcess", async function () { - expect(session.account.deletionDate).toBeUndefined(); + expect(sessionDevice1.account.deletionDate).toBeUndefined(); - const eventListener = new EventListener(runtime, [LocalAccountDeletionDateChangedEvent, IdentityDeletionProcessStatusChangedEvent]); + const eventListener = new EventListener(runtimeDevice1, [LocalAccountDeletionDateChangedEvent, IdentityDeletionProcessStatusChangedEvent]); eventListener.start(); - const initiateDeletionResult = await session.transportServices.identityDeletionProcesses.initiateIdentityDeletionProcess(); + const initiateDeletionResult = await sessionDevice1.transportServices.identityDeletionProcesses.initiateIdentityDeletionProcess(); eventListener.stop(); const events = eventListener.getReceivedEvents(); @@ -55,17 +55,17 @@ describe("IdentityDeletionProcessStatusChanged", function () { expect(localAccountDeletionDateChangedEvent.data).toBeDefined(); expect(localAccountDeletionDateChangedEvent.data).toBe(initiateDeletionResult.value.gracePeriodEndsAt); - expect(session.account.deletionDate).toBeDefined(); - expect(session.account.deletionDate).toBe(initiateDeletionResult.value.gracePeriodEndsAt); + expect(sessionDevice1.account.deletionDate).toBeDefined(); + expect(sessionDevice1.account.deletionDate).toBe(initiateDeletionResult.value.gracePeriodEndsAt); }); test("should fire an event and set the deletionDate of the LocalAccount cancelling an IdentityDeletionProcess", async function () { - await session.transportServices.identityDeletionProcesses.initiateIdentityDeletionProcess(); + await sessionDevice1.transportServices.identityDeletionProcesses.initiateIdentityDeletionProcess(); - const eventListener = new EventListener(runtime, [LocalAccountDeletionDateChangedEvent, IdentityDeletionProcessStatusChangedEvent]); + const eventListener = new EventListener(runtimeDevice1, [LocalAccountDeletionDateChangedEvent, IdentityDeletionProcessStatusChangedEvent]); eventListener.start(); - const cancelDeletionResult = await session.transportServices.identityDeletionProcesses.cancelIdentityDeletionProcess(); + const cancelDeletionResult = await sessionDevice1.transportServices.identityDeletionProcesses.cancelIdentityDeletionProcess(); eventListener.stop(); const events = eventListener.getReceivedEvents(); @@ -80,7 +80,7 @@ describe("IdentityDeletionProcessStatusChanged", function () { expect(localAccountDeletionDateChangedEvent).toBeInstanceOf(LocalAccountDeletionDateChangedEvent); expect(localAccountDeletionDateChangedEvent.data).toBeUndefined(); - expect(session.account.deletionDate).toBeUndefined(); + expect(sessionDevice1.account.deletionDate).toBeUndefined(); }); describe("multiple devices", function () { @@ -91,8 +91,8 @@ describe("IdentityDeletionProcessStatusChanged", function () { runtimeDevice2 = await TestUtil.createRuntime(); await runtimeDevice2.start(); - const newDevice = await session.transportServices.devices.createDevice({ name: "test", isAdmin: true }); - const token = await session.transportServices.devices.getDeviceOnboardingToken({ id: newDevice.value.id, profileName: "Test" }); + const newDevice = await sessionDevice1.transportServices.devices.createDevice({ name: "test", isAdmin: true }); + const token = await sessionDevice1.transportServices.devices.getDeviceOnboardingToken({ id: newDevice.value.id, profileName: "Test" }); const content: any = Serializable.fromUnknown(token.value.content); const [accountDevice2] = await runtimeDevice2.multiAccountController.onboardDevice(content.sharedSecret, "test"); @@ -104,7 +104,7 @@ describe("IdentityDeletionProcessStatusChanged", function () { }); test("should set the deletionDate of the LocalAccount on a second device that is online", async function () { - await session.transportServices.identityDeletionProcesses.initiateIdentityDeletionProcess(); + await sessionDevice1.transportServices.identityDeletionProcesses.initiateIdentityDeletionProcess(); expect(sessionDevice2.account.deletionDate).toBeUndefined(); await sessionDevice2.transportServices.account.syncDatawallet(); @@ -113,7 +113,7 @@ describe("IdentityDeletionProcessStatusChanged", function () { test("should set the deletionDate of the LocalAccount on a second device that was offline", async function () { await runtimeDevice2.stop(); - await session.transportServices.identityDeletionProcesses.initiateIdentityDeletionProcess(); + await sessionDevice1.transportServices.identityDeletionProcesses.initiateIdentityDeletionProcess(); expect(sessionDevice2.account.deletionDate).toBeUndefined(); await runtimeDevice2.start(); From 9ee6fd408ff0130e302677241fb99c7982bf0a1a Mon Sep 17 00:00:00 2001 From: Milena Czierlinski Date: Wed, 20 Nov 2024 16:13:28 +0100 Subject: [PATCH 06/42] refactor: clean up changes --- ...yDeletionProcessStatusChangedModule.test.ts | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/app-runtime/test/modules/IdentityDeletionProcessStatusChangedModule.test.ts b/packages/app-runtime/test/modules/IdentityDeletionProcessStatusChangedModule.test.ts index 924445404..898f9e2b0 100644 --- a/packages/app-runtime/test/modules/IdentityDeletionProcessStatusChangedModule.test.ts +++ b/packages/app-runtime/test/modules/IdentityDeletionProcessStatusChangedModule.test.ts @@ -1,4 +1,3 @@ -import { Serializable } from "@js-soft/ts-serval"; import { IdentityDeletionProcessStatus, IdentityDeletionProcessStatusChangedEvent } from "@nmshd/runtime"; import { AppRuntime, LocalAccountDeletionDateChangedEvent, LocalAccountSession } from "../../src"; import { EventListener, TestUtil } from "../lib"; @@ -11,9 +10,8 @@ describe("IdentityDeletionProcessStatusChanged", function () { runtimeDevice1 = await TestUtil.createRuntime(); await runtimeDevice1.start(); - const accounts = await TestUtil.provideAccounts(runtimeDevice1, 1); - - sessionDevice1 = await runtimeDevice1.selectAccount(accounts[0].id); + const [localAccountDevice1] = await TestUtil.provideAccounts(runtimeDevice1, 1); + sessionDevice1 = await runtimeDevice1.selectAccount(localAccountDevice1.id); }); afterEach(async () => { @@ -91,12 +89,14 @@ describe("IdentityDeletionProcessStatusChanged", function () { runtimeDevice2 = await TestUtil.createRuntime(); await runtimeDevice2.start(); - const newDevice = await sessionDevice1.transportServices.devices.createDevice({ name: "test", isAdmin: true }); - const token = await sessionDevice1.transportServices.devices.getDeviceOnboardingToken({ id: newDevice.value.id, profileName: "Test" }); - const content: any = Serializable.fromUnknown(token.value.content); + const createDeviceResult = await sessionDevice1.transportServices.devices.createDevice({ name: "test", isAdmin: true }); + const onboardingInfoResult = await sessionDevice1.transportServices.devices.getDeviceOnboardingInfo({ id: createDeviceResult.value.id, profileName: "Test" }); + + const localAccountDevice2 = await runtimeDevice2.accountServices.onboardAccount(onboardingInfoResult.value); + sessionDevice2 = await runtimeDevice2.selectAccount(localAccountDevice2.id.toString()); - const [accountDevice2] = await runtimeDevice2.multiAccountController.onboardDevice(content.sharedSecret, "test"); - sessionDevice2 = await runtimeDevice2.selectAccount(accountDevice2.id.toString()); + await sessionDevice1.transportServices.account.syncDatawallet(); + await sessionDevice2.transportServices.account.syncDatawallet(); }); afterAll(async function () { From 7641e3e406783410764d233e0b8291a7ddfc1588 Mon Sep 17 00:00:00 2001 From: Milena Czierlinski Date: Thu, 21 Nov 2024 15:38:49 +0100 Subject: [PATCH 07/42] test: notes --- .../IdentityDeletionProcessStatusChangedModule.test.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/app-runtime/test/modules/IdentityDeletionProcessStatusChangedModule.test.ts b/packages/app-runtime/test/modules/IdentityDeletionProcessStatusChangedModule.test.ts index 898f9e2b0..8324e7e5b 100644 --- a/packages/app-runtime/test/modules/IdentityDeletionProcessStatusChangedModule.test.ts +++ b/packages/app-runtime/test/modules/IdentityDeletionProcessStatusChangedModule.test.ts @@ -103,11 +103,12 @@ describe("IdentityDeletionProcessStatusChanged", function () { await runtimeDevice2.stop(); }); - test("should set the deletionDate of the LocalAccount on a second device that is online", async function () { + test.only("should set the deletionDate of the LocalAccount on a second device that is online", async function () { await sessionDevice1.transportServices.identityDeletionProcesses.initiateIdentityDeletionProcess(); expect(sessionDevice2.account.deletionDate).toBeUndefined(); await sessionDevice2.transportServices.account.syncDatawallet(); + console.log(await sessionDevice2.transportServices.identityDeletionProcesses.getIdentityDeletionProcesses()); expect(sessionDevice2.account.deletionDate).toBeDefined(); }); @@ -116,6 +117,7 @@ describe("IdentityDeletionProcessStatusChanged", function () { await sessionDevice1.transportServices.identityDeletionProcesses.initiateIdentityDeletionProcess(); expect(sessionDevice2.account.deletionDate).toBeUndefined(); + await runtimeDevice2.init(); await runtimeDevice2.start(); await sessionDevice2.transportServices.account.syncDatawallet(); expect(sessionDevice2.account.deletionDate).toBeDefined(); From fa2df9ab18271bc2b6face08906364e14c4ffa8d Mon Sep 17 00:00:00 2001 From: Milena Czierlinski Date: Thu, 21 Nov 2024 17:58:16 +0100 Subject: [PATCH 08/42] feat: publish DatawalletSynchronizedEvent in AccountController --- packages/runtime/src/events/EventProxy.ts | 5 +++++ .../src/events/transport/DatawalletSynchronizedEvent.ts | 9 +++++++++ packages/runtime/src/events/transport/index.ts | 1 + .../transport/src/events/DatawalletSynchronizedEvent.ts | 9 +++++++++ packages/transport/src/events/index.ts | 1 + .../transport/src/modules/accounts/AccountController.ts | 5 ++++- 6 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 packages/runtime/src/events/transport/DatawalletSynchronizedEvent.ts create mode 100644 packages/transport/src/events/DatawalletSynchronizedEvent.ts diff --git a/packages/runtime/src/events/EventProxy.ts b/packages/runtime/src/events/EventProxy.ts index fdbf0f1eb..020e8ad17 100644 --- a/packages/runtime/src/events/EventProxy.ts +++ b/packages/runtime/src/events/EventProxy.ts @@ -23,6 +23,7 @@ import { ThirdPartyRelationshipAttributeSucceededEvent } from "./consumption"; import { + DatawalletSynchronizedEvent, IdentityDeletionProcessStatusChangedEvent, MessageDeliveredEvent, MessageReceivedEvent, @@ -108,6 +109,10 @@ export class EventProxy { this.subscribeToSourceEvent(transport.PeerDeletionCancelledEvent, (event) => { this.targetEventBus.publish(new PeerDeletionCancelledEvent(event.eventTargetAddress, RelationshipMapper.toRelationshipDTO(event.data))); }); + + this.subscribeToSourceEvent(transport.DatawalletSynchronizedEvent, (event) => { + this.targetEventBus.publish(new DatawalletSynchronizedEvent(event.eventTargetAddress)); + }); } private proxyConsumptionEvents() { diff --git a/packages/runtime/src/events/transport/DatawalletSynchronizedEvent.ts b/packages/runtime/src/events/transport/DatawalletSynchronizedEvent.ts new file mode 100644 index 000000000..1e641701f --- /dev/null +++ b/packages/runtime/src/events/transport/DatawalletSynchronizedEvent.ts @@ -0,0 +1,9 @@ +import { DataEvent } from "../DataEvent"; + +export class DatawalletSynchronizedEvent extends DataEvent { + public static readonly namespace: string = "transport.datawalletSynchronized"; + + public constructor(eventTargetAddress: string) { + super(DatawalletSynchronizedEvent.namespace, eventTargetAddress, undefined); + } +} diff --git a/packages/runtime/src/events/transport/index.ts b/packages/runtime/src/events/transport/index.ts index ea53d15f8..fc004346d 100644 --- a/packages/runtime/src/events/transport/index.ts +++ b/packages/runtime/src/events/transport/index.ts @@ -1,3 +1,4 @@ +export * from "./DatawalletSynchronizedEvent"; export * from "./IdentityDeletionProcessStatusChangedEvent"; export * from "./MessageDeliveredEvent"; export * from "./MessageReceivedEvent"; diff --git a/packages/transport/src/events/DatawalletSynchronizedEvent.ts b/packages/transport/src/events/DatawalletSynchronizedEvent.ts new file mode 100644 index 000000000..7ac5665bd --- /dev/null +++ b/packages/transport/src/events/DatawalletSynchronizedEvent.ts @@ -0,0 +1,9 @@ +import { TransportDataEvent } from "./TransportDataEvent"; + +export class DatawalletSynchronizedEvent extends TransportDataEvent { + public static readonly namespace: string = "transport.datawalletSynchronized"; + + public constructor(eventTargetAddress: string) { + super(DatawalletSynchronizedEvent.namespace, eventTargetAddress, undefined); + } +} diff --git a/packages/transport/src/events/index.ts b/packages/transport/src/events/index.ts index c3318f1a4..80fec200c 100644 --- a/packages/transport/src/events/index.ts +++ b/packages/transport/src/events/index.ts @@ -1,3 +1,4 @@ +export * from "./DatawalletSynchronizedEvent"; export * from "./IdentityDeletionProcessStatusChangedEvent"; export * from "./MessageDeliveredEvent"; export * from "./MessageReceivedEvent"; diff --git a/packages/transport/src/modules/accounts/AccountController.ts b/packages/transport/src/modules/accounts/AccountController.ts index 40b2592cd..bc473ffb3 100644 --- a/packages/transport/src/modules/accounts/AccountController.ts +++ b/packages/transport/src/modules/accounts/AccountController.ts @@ -8,6 +8,7 @@ import { CoreCrypto } from "../../core/CoreCrypto"; import { DbCollectionName } from "../../core/DbCollectionName"; import { DependencyOverrides } from "../../core/DependencyOverrides"; import { TransportLoggerFactory } from "../../core/TransportLoggerFactory"; +import { DatawalletSynchronizedEvent } from "../../events"; import { PasswordGenerator } from "../../util"; import { CertificateController } from "../certificates/CertificateController"; import { CertificateIssuer } from "../certificates/CertificateIssuer"; @@ -234,7 +235,9 @@ export class AccountController { return; } - return await this.synchronization.sync("OnlyDatawallet"); + await this.synchronization.sync("OnlyDatawallet"); + this.transport.eventBus.publish(new DatawalletSynchronizedEvent(this.identity.address.toString())); + return; } public async syncEverything(): Promise { From 5f66098a181a8a1c8659d9d20bd2949cf3e5413a Mon Sep 17 00:00:00 2001 From: Milena Czierlinski Date: Fri, 22 Nov 2024 15:28:00 +0100 Subject: [PATCH 09/42] test: receive DatawalletSynchronizedEvent calling syncDatawallet use case --- .../runtime/test/transport/account.test.ts | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/packages/runtime/test/transport/account.test.ts b/packages/runtime/test/transport/account.test.ts index 9697b9a4d..10ff337f0 100644 --- a/packages/runtime/test/transport/account.test.ts +++ b/packages/runtime/test/transport/account.test.ts @@ -1,17 +1,26 @@ import { CoreDate } from "@nmshd/core-types"; import { DateTime } from "luxon"; -import { DeviceDTO, DeviceOnboardingInfoDTO, TransportServices } from "../../src"; -import { emptyRelationshipTemplateContent, RuntimeServiceProvider, uploadFile } from "../lib"; +import { DatawalletSynchronizedEvent, DeviceDTO, DeviceOnboardingInfoDTO, TransportServices } from "../../src"; +import { emptyRelationshipTemplateContent, MockEventBus, RuntimeServiceProvider, uploadFile } from "../lib"; const serviceProvider = new RuntimeServiceProvider(); let sTransportServices: TransportServices; let rTransportServices: TransportServices; +let sEventBus: MockEventBus; + beforeAll(async () => { const runtimeServices = await serviceProvider.launch(2, { enableDatawallet: true }); sTransportServices = runtimeServices[0].transport; rTransportServices = runtimeServices[1].transport; + + sEventBus = runtimeServices[0].eventBus; }, 30000); + +beforeEach(() => { + sEventBus.reset(); +}); + afterAll(async () => await serviceProvider.stop()); describe("Sync", () => { @@ -49,6 +58,12 @@ describe("Automatic Datawallet Sync", () => { expect(oldSyncTime).not.toStrictEqual(newSyncTime); }); + test("should receive a DatawalletSynchronizedEvent", async () => { + await sTransportServices.account.syncDatawallet(); + + await expect(sEventBus).toHavePublished(DatawalletSynchronizedEvent); + }); + test("should not run an automatic datawallet sync", async () => { const disableResult = await sTransportServices.account.disableAutoSync(); expect(disableResult).toBeSuccessful(); From 7d850e146b5b0dc2a8d8397a64df2eaf72900b65 Mon Sep 17 00:00:00 2001 From: Milena Czierlinski Date: Fri, 22 Nov 2024 16:06:37 +0100 Subject: [PATCH 10/42] feat: use runtime event in app-runtime --- .../src/events/DatawalletSynchronizedEvent.ts | 9 --------- packages/app-runtime/src/events/index.ts | 1 - .../modules/pushNotifications/PushNotificationModule.ts | 3 +-- .../app-runtime/test/modules/PushNotification.test.ts | 3 ++- 4 files changed, 3 insertions(+), 13 deletions(-) delete mode 100644 packages/app-runtime/src/events/DatawalletSynchronizedEvent.ts diff --git a/packages/app-runtime/src/events/DatawalletSynchronizedEvent.ts b/packages/app-runtime/src/events/DatawalletSynchronizedEvent.ts deleted file mode 100644 index a9d01fa22..000000000 --- a/packages/app-runtime/src/events/DatawalletSynchronizedEvent.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { DataEvent } from "@nmshd/runtime"; - -export class DatawalletSynchronizedEvent extends DataEvent { - public static readonly namespace: string = "app.datawalletSynchronized"; - - public constructor(address: string) { - super(DatawalletSynchronizedEvent.namespace, address, undefined); - } -} diff --git a/packages/app-runtime/src/events/index.ts b/packages/app-runtime/src/events/index.ts index fbf70b343..be009dd2b 100644 --- a/packages/app-runtime/src/events/index.ts +++ b/packages/app-runtime/src/events/index.ts @@ -1,5 +1,4 @@ export * from "./AccountSelectedEvent"; -export * from "./DatawalletSynchronizedEvent"; export * from "./ExternalEventReceivedEvent"; export * from "./LocalAccountDeletionDateChangedEvent"; export * from "./MailReceivedEvent"; diff --git a/packages/app-runtime/src/modules/pushNotifications/PushNotificationModule.ts b/packages/app-runtime/src/modules/pushNotifications/PushNotificationModule.ts index 92df666a9..31f9141d2 100644 --- a/packages/app-runtime/src/modules/pushNotifications/PushNotificationModule.ts +++ b/packages/app-runtime/src/modules/pushNotifications/PushNotificationModule.ts @@ -1,6 +1,6 @@ import { Result } from "@js-soft/ts-utils"; import { AppRuntimeErrors } from "../../AppRuntimeErrors"; -import { AccountSelectedEvent, DatawalletSynchronizedEvent, ExternalEventReceivedEvent } from "../../events"; +import { AccountSelectedEvent, ExternalEventReceivedEvent } from "../../events"; import { RemoteNotificationEvent, RemoteNotificationRegistrationEvent } from "../../natives"; import { AppRuntimeModule, AppRuntimeModuleConfiguration } from "../AppRuntimeModule"; import { BackboneEventName, IBackboneEventContent } from "./IBackboneEventContent"; @@ -35,7 +35,6 @@ export class PushNotificationModule extends AppRuntimeModule Date: Fri, 22 Nov 2024 17:55:21 +0100 Subject: [PATCH 11/42] feat: add DatawalletSynchronized module --- packages/app-runtime/src/AppConfig.ts | 18 ++-- packages/app-runtime/src/AppRuntime.ts | 6 +- .../DatawalletSynchronizedModule.ts | 43 ++++++++++ .../src/modules/runtimeEvents/index.ts | 1 + .../DatawalletSynchronizedModule.test.ts | 86 +++++++++++++++++++ ...DeletionProcessStatusChangedModule.test.ts | 80 ++++------------- 6 files changed, 165 insertions(+), 69 deletions(-) create mode 100644 packages/app-runtime/src/modules/runtimeEvents/DatawalletSynchronizedModule.ts create mode 100644 packages/app-runtime/test/modules/DatawalletSynchronizedModule.test.ts diff --git a/packages/app-runtime/src/AppConfig.ts b/packages/app-runtime/src/AppConfig.ts index c6ea7679d..b92b91798 100644 --- a/packages/app-runtime/src/AppConfig.ts +++ b/packages/app-runtime/src/AppConfig.ts @@ -52,6 +52,18 @@ export function createAppConfig(...configs: AppConfigOverwrite[]): AppConfig { location: "onboardingChangeReceived", enabled: true }, + datawalletSynchronized: { + name: "datawalletSynchronized", + displayName: "Datawallet Synchronized Module", + location: "datawalletSynchronized", + enabled: true + }, + identityDeletionProcessStatusChanged: { + name: "identityDeletionProcessStatusChanged", + displayName: "Identity Deletion Process Status Changed Module", + location: "identityDeletionProcessStatusChanged", + enabled: true + }, messageReceived: { name: "messageReceived", displayName: "Message Received Module", @@ -70,12 +82,6 @@ export function createAppConfig(...configs: AppConfigOverwrite[]): AppConfig { location: "relationshipTemplateProcessed", enabled: true }, - identityDeletionProcessStatusChanged: { - name: "identityDeletionProcessStatusChanged", - displayName: "Identity Deletion Process Status Changed Module", - location: "identityDeletionProcessStatusChanged", - enabled: true - }, decider: { displayName: "Decider Module", name: "DeciderModule", diff --git a/packages/app-runtime/src/AppRuntime.ts b/packages/app-runtime/src/AppRuntime.ts index 0e9743986..84bc151d3 100644 --- a/packages/app-runtime/src/AppRuntime.ts +++ b/packages/app-runtime/src/AppRuntime.ts @@ -15,6 +15,7 @@ import { AppLaunchModule, AppRuntimeModuleConfiguration, AppSyncModule, + DatawalletSynchronizedModule, IAppRuntimeModuleConstructor, IdentityDeletionProcessStatusChangedModule, MailReceivedModule, @@ -299,10 +300,11 @@ export class AppRuntime extends Runtime { pushNotification: PushNotificationModule, mailReceived: MailReceivedModule, onboardingChangeReceived: OnboardingChangeReceivedModule, + datawalletSynchronized: DatawalletSynchronizedModule, + identityDeletionProcessStatusChanged: IdentityDeletionProcessStatusChangedModule, messageReceived: MessageReceivedModule, relationshipChanged: RelationshipChangedModule, - relationshipTemplateProcessed: RelationshipTemplateProcessedModule, - identityDeletionProcessStatusChanged: IdentityDeletionProcessStatusChangedModule + relationshipTemplateProcessed: RelationshipTemplateProcessedModule }; public static registerModule(moduleName: string, ctor: IAppRuntimeModuleConstructor): void { diff --git a/packages/app-runtime/src/modules/runtimeEvents/DatawalletSynchronizedModule.ts b/packages/app-runtime/src/modules/runtimeEvents/DatawalletSynchronizedModule.ts new file mode 100644 index 000000000..95fcadabd --- /dev/null +++ b/packages/app-runtime/src/modules/runtimeEvents/DatawalletSynchronizedModule.ts @@ -0,0 +1,43 @@ +import { DatawalletSynchronizedEvent } from "@nmshd/runtime"; +import { AppRuntimeError } from "../../AppRuntimeError"; +import { LocalAccountDeletionDateChangedEvent } from "../../events"; +import { AppRuntimeModule, AppRuntimeModuleConfiguration } from "../AppRuntimeModule"; + +export interface DatawalletSynchronizedModuleConfig extends AppRuntimeModuleConfiguration {} + +export class DatawalletSynchronizedModuleError extends AppRuntimeError {} + +export class DatawalletSynchronizedModule extends AppRuntimeModule { + public async init(): Promise { + // Nothing to do here + } + + public start(): Promise | void { + this.subscribeToEvent(DatawalletSynchronizedEvent, this.handleDatawalletSynchronized.bind(this)); + } + + private async handleDatawalletSynchronized(event: DatawalletSynchronizedEvent) { + const previousDeletionDate = this.runtime.currentSession.account.deletionDate; + + const services = await this.runtime.getServices(event.eventTargetAddress); + const identityDeletionProcessResult = await services.transportServices.identityDeletionProcesses.getIdentityDeletionProcesses(); + + if (identityDeletionProcessResult.isError) { + this.logger.error(identityDeletionProcessResult); + return; + } + + const newDeletionDate = identityDeletionProcessResult.value[0].gracePeriodEndsAt; + + if (previousDeletionDate === newDeletionDate) return; + + this.runtime.currentSession.account.deletionDate = newDeletionDate; + await this.runtime.multiAccountController.updateLocalAccountDeletionDate(event.eventTargetAddress, newDeletionDate); + + this.runtime.eventBus.publish(new LocalAccountDeletionDateChangedEvent(event.eventTargetAddress, newDeletionDate)); + } + + public override stop(): Promise | void { + this.unsubscribeFromAllEvents(); + } +} diff --git a/packages/app-runtime/src/modules/runtimeEvents/index.ts b/packages/app-runtime/src/modules/runtimeEvents/index.ts index 7a3710bda..e37a1aa45 100644 --- a/packages/app-runtime/src/modules/runtimeEvents/index.ts +++ b/packages/app-runtime/src/modules/runtimeEvents/index.ts @@ -1,3 +1,4 @@ +export * from "./DatawalletSynchronizedModule"; export * from "./IdentityDeletionProcessStatusChangedModule"; export * from "./MessageReceivedModule"; export * from "./RelationshipChangedModule"; diff --git a/packages/app-runtime/test/modules/DatawalletSynchronizedModule.test.ts b/packages/app-runtime/test/modules/DatawalletSynchronizedModule.test.ts new file mode 100644 index 000000000..0161f2bd6 --- /dev/null +++ b/packages/app-runtime/test/modules/DatawalletSynchronizedModule.test.ts @@ -0,0 +1,86 @@ +import { DatawalletSynchronizedEvent, IdentityDeletionProcessStatus } from "@nmshd/runtime"; +import { AppRuntime, LocalAccountDeletionDateChangedEvent, LocalAccountSession } from "../../src"; +import { EventListener, TestUtil } from "../lib"; + +describe("DatawalletSynchronized", function () { + let runtimeDevice1: AppRuntime; + let sessionDevice1: LocalAccountSession; + + let runtimeDevice2: AppRuntime; + let sessionDevice2: LocalAccountSession; + + beforeAll(async function () { + runtimeDevice1 = await TestUtil.createRuntime(); + await runtimeDevice1.start(); + + const [localAccountDevice1] = await TestUtil.provideAccounts(runtimeDevice1, 1); + sessionDevice1 = await runtimeDevice1.selectAccount(localAccountDevice1.id); + + runtimeDevice2 = await TestUtil.createRuntime(); + await runtimeDevice2.start(); + + const createDeviceResult = await sessionDevice1.transportServices.devices.createDevice({ name: "test", isAdmin: true }); + const onboardingInfoResult = await sessionDevice1.transportServices.devices.getDeviceOnboardingInfo({ id: createDeviceResult.value.id, profileName: "Test" }); + const localAccountDevice2 = await runtimeDevice2.accountServices.onboardAccount(onboardingInfoResult.value); + sessionDevice2 = await runtimeDevice2.selectAccount(localAccountDevice2.id.toString()); + + await sessionDevice1.transportServices.account.syncDatawallet(); + await sessionDevice2.transportServices.account.syncDatawallet(); + }); + + afterEach(async () => { + const activeIdentityDeletionProcess = await sessionDevice1.transportServices.identityDeletionProcesses.getActiveIdentityDeletionProcess(); + if (!activeIdentityDeletionProcess.isSuccess) { + return; + } + + let abortResult; + if (activeIdentityDeletionProcess.value.status === IdentityDeletionProcessStatus.Approved) { + abortResult = await sessionDevice1.transportServices.identityDeletionProcesses.cancelIdentityDeletionProcess(); + await sessionDevice2.transportServices.account.syncDatawallet(); + } + if (abortResult?.isError) throw abortResult.error; + }); + + afterAll(async function () { + await runtimeDevice1.stop(); + await runtimeDevice2.stop(); + }); + + // TODO: add test for cancelling IdentityDeletionProcess + + test("should publish a LocalAccountDeletionDateChangedEvent if the deletion date changed after a datawallet sync", async function () { + await sessionDevice1.transportServices.identityDeletionProcesses.initiateIdentityDeletionProcess(); + expect(sessionDevice2.account.deletionDate).toBeUndefined(); + + const eventListener = new EventListener(runtimeDevice2, [DatawalletSynchronizedEvent, LocalAccountDeletionDateChangedEvent]); + eventListener.start(); + + await sessionDevice2.transportServices.account.syncDatawallet(); + + eventListener.stop(); + const events = eventListener.getReceivedEvents(); + expect(events).toHaveLength(2); + }); + + test("should set the deletionDate of the LocalAccount on a second device that is online", async function () { + const initiateResult = await sessionDevice1.transportServices.identityDeletionProcesses.initiateIdentityDeletionProcess(); + expect(sessionDevice2.account.deletionDate).toBeUndefined(); + + await sessionDevice2.transportServices.account.syncDatawallet(); + + expect(sessionDevice2.account.deletionDate).toBeDefined(); + expect(sessionDevice2.account.deletionDate).toBe(initiateResult.value.gracePeriodEndsAt); + }); + + test("should set the deletionDate of the LocalAccount on a second device that was offline", async function () { + await runtimeDevice2.stop(); + await sessionDevice1.transportServices.identityDeletionProcesses.initiateIdentityDeletionProcess(); + expect(sessionDevice2.account.deletionDate).toBeUndefined(); + + await runtimeDevice2.init(); + await runtimeDevice2.start(); + await sessionDevice2.transportServices.account.syncDatawallet(); + expect(sessionDevice2.account.deletionDate).toBeDefined(); + }); +}); diff --git a/packages/app-runtime/test/modules/IdentityDeletionProcessStatusChangedModule.test.ts b/packages/app-runtime/test/modules/IdentityDeletionProcessStatusChangedModule.test.ts index 8324e7e5b..6551df508 100644 --- a/packages/app-runtime/test/modules/IdentityDeletionProcessStatusChangedModule.test.ts +++ b/packages/app-runtime/test/modules/IdentityDeletionProcessStatusChangedModule.test.ts @@ -3,41 +3,42 @@ import { AppRuntime, LocalAccountDeletionDateChangedEvent, LocalAccountSession } import { EventListener, TestUtil } from "../lib"; describe("IdentityDeletionProcessStatusChanged", function () { - let runtimeDevice1: AppRuntime; - let sessionDevice1: LocalAccountSession; + let runtime: AppRuntime; + let session: LocalAccountSession; beforeAll(async function () { - runtimeDevice1 = await TestUtil.createRuntime(); - await runtimeDevice1.start(); + runtime = await TestUtil.createRuntime(); + await runtime.start(); - const [localAccountDevice1] = await TestUtil.provideAccounts(runtimeDevice1, 1); - sessionDevice1 = await runtimeDevice1.selectAccount(localAccountDevice1.id); + const [localAccount] = await TestUtil.provideAccounts(runtime, 1); + session = await runtime.selectAccount(localAccount.id); }); afterEach(async () => { - const activeIdentityDeletionProcess = await sessionDevice1.transportServices.identityDeletionProcesses.getActiveIdentityDeletionProcess(); + const activeIdentityDeletionProcess = await session.transportServices.identityDeletionProcesses.getActiveIdentityDeletionProcess(); if (!activeIdentityDeletionProcess.isSuccess) { return; } let abortResult; if (activeIdentityDeletionProcess.value.status === IdentityDeletionProcessStatus.Approved) { - abortResult = await sessionDevice1.transportServices.identityDeletionProcesses.cancelIdentityDeletionProcess(); + abortResult = await session.transportServices.identityDeletionProcesses.cancelIdentityDeletionProcess(); } if (abortResult?.isError) throw abortResult.error; }); afterAll(async function () { - await runtimeDevice1.stop(); + await runtime.stop(); }); + // TODO: split test test("should fire an event and set the deletionDate of the LocalAccount initiating an IdentityDeletionProcess", async function () { - expect(sessionDevice1.account.deletionDate).toBeUndefined(); + expect(session.account.deletionDate).toBeUndefined(); - const eventListener = new EventListener(runtimeDevice1, [LocalAccountDeletionDateChangedEvent, IdentityDeletionProcessStatusChangedEvent]); + const eventListener = new EventListener(runtime, [LocalAccountDeletionDateChangedEvent, IdentityDeletionProcessStatusChangedEvent]); eventListener.start(); - const initiateDeletionResult = await sessionDevice1.transportServices.identityDeletionProcesses.initiateIdentityDeletionProcess(); + const initiateDeletionResult = await session.transportServices.identityDeletionProcesses.initiateIdentityDeletionProcess(); eventListener.stop(); const events = eventListener.getReceivedEvents(); @@ -53,17 +54,17 @@ describe("IdentityDeletionProcessStatusChanged", function () { expect(localAccountDeletionDateChangedEvent.data).toBeDefined(); expect(localAccountDeletionDateChangedEvent.data).toBe(initiateDeletionResult.value.gracePeriodEndsAt); - expect(sessionDevice1.account.deletionDate).toBeDefined(); - expect(sessionDevice1.account.deletionDate).toBe(initiateDeletionResult.value.gracePeriodEndsAt); + expect(session.account.deletionDate).toBeDefined(); + expect(session.account.deletionDate).toBe(initiateDeletionResult.value.gracePeriodEndsAt); }); test("should fire an event and set the deletionDate of the LocalAccount cancelling an IdentityDeletionProcess", async function () { - await sessionDevice1.transportServices.identityDeletionProcesses.initiateIdentityDeletionProcess(); + await session.transportServices.identityDeletionProcesses.initiateIdentityDeletionProcess(); - const eventListener = new EventListener(runtimeDevice1, [LocalAccountDeletionDateChangedEvent, IdentityDeletionProcessStatusChangedEvent]); + const eventListener = new EventListener(runtime, [LocalAccountDeletionDateChangedEvent, IdentityDeletionProcessStatusChangedEvent]); eventListener.start(); - const cancelDeletionResult = await sessionDevice1.transportServices.identityDeletionProcesses.cancelIdentityDeletionProcess(); + const cancelDeletionResult = await session.transportServices.identityDeletionProcesses.cancelIdentityDeletionProcess(); eventListener.stop(); const events = eventListener.getReceivedEvents(); @@ -78,49 +79,6 @@ describe("IdentityDeletionProcessStatusChanged", function () { expect(localAccountDeletionDateChangedEvent).toBeInstanceOf(LocalAccountDeletionDateChangedEvent); expect(localAccountDeletionDateChangedEvent.data).toBeUndefined(); - expect(sessionDevice1.account.deletionDate).toBeUndefined(); - }); - - describe("multiple devices", function () { - let runtimeDevice2: AppRuntime; - let sessionDevice2: LocalAccountSession; - - beforeAll(async function () { - runtimeDevice2 = await TestUtil.createRuntime(); - await runtimeDevice2.start(); - - const createDeviceResult = await sessionDevice1.transportServices.devices.createDevice({ name: "test", isAdmin: true }); - const onboardingInfoResult = await sessionDevice1.transportServices.devices.getDeviceOnboardingInfo({ id: createDeviceResult.value.id, profileName: "Test" }); - - const localAccountDevice2 = await runtimeDevice2.accountServices.onboardAccount(onboardingInfoResult.value); - sessionDevice2 = await runtimeDevice2.selectAccount(localAccountDevice2.id.toString()); - - await sessionDevice1.transportServices.account.syncDatawallet(); - await sessionDevice2.transportServices.account.syncDatawallet(); - }); - - afterAll(async function () { - await runtimeDevice2.stop(); - }); - - test.only("should set the deletionDate of the LocalAccount on a second device that is online", async function () { - await sessionDevice1.transportServices.identityDeletionProcesses.initiateIdentityDeletionProcess(); - expect(sessionDevice2.account.deletionDate).toBeUndefined(); - - await sessionDevice2.transportServices.account.syncDatawallet(); - console.log(await sessionDevice2.transportServices.identityDeletionProcesses.getIdentityDeletionProcesses()); - expect(sessionDevice2.account.deletionDate).toBeDefined(); - }); - - test("should set the deletionDate of the LocalAccount on a second device that was offline", async function () { - await runtimeDevice2.stop(); - await sessionDevice1.transportServices.identityDeletionProcesses.initiateIdentityDeletionProcess(); - expect(sessionDevice2.account.deletionDate).toBeUndefined(); - - await runtimeDevice2.init(); - await runtimeDevice2.start(); - await sessionDevice2.transportServices.account.syncDatawallet(); - expect(sessionDevice2.account.deletionDate).toBeDefined(); - }); + expect(session.account.deletionDate).toBeUndefined(); }); }); From 7bede00005f25f9704e5ef7c0d81998b5e79105d Mon Sep 17 00:00:00 2001 From: Milena Czierlinski Date: Fri, 22 Nov 2024 17:55:41 +0100 Subject: [PATCH 12/42] chore: remove LocalAccountDeletionDateChangedModule --- .../LocalAccountDeletionDateChangedModule.ts | 29 ------------------- .../src/modules/appEvents/index.ts | 1 - 2 files changed, 30 deletions(-) delete mode 100644 packages/app-runtime/src/modules/appEvents/LocalAccountDeletionDateChangedModule.ts diff --git a/packages/app-runtime/src/modules/appEvents/LocalAccountDeletionDateChangedModule.ts b/packages/app-runtime/src/modules/appEvents/LocalAccountDeletionDateChangedModule.ts deleted file mode 100644 index 353822870..000000000 --- a/packages/app-runtime/src/modules/appEvents/LocalAccountDeletionDateChangedModule.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { AppRuntimeError } from "../../AppRuntimeError"; -import { LocalAccountDeletionDateChangedEvent } from "../../events"; -import { AppRuntimeModule, AppRuntimeModuleConfiguration } from "../AppRuntimeModule"; - -export interface LocalAccountDeletionDateChangedModuleConfig extends AppRuntimeModuleConfiguration {} - -export class LocalAccountDeletionDateChangedModuleError extends AppRuntimeError {} - -export class LocalAccountDeletionDateChangedModule extends AppRuntimeModule { - public async init(): Promise { - // Nothing to do here - } - - public start(): void { - this.subscribeToEvent(LocalAccountDeletionDateChangedEvent, this.handleLocalDeletionDateChanged.bind(this)); - } - - private async handleLocalDeletionDateChanged(event: LocalAccountDeletionDateChangedEvent) { - const session = await this.runtime.getOrCreateSession(event.eventTargetAddress); - - const deletionDate = event.data; - - // do something - } - - public stop(): void { - this.unsubscribeFromAllEvents(); - } -} diff --git a/packages/app-runtime/src/modules/appEvents/index.ts b/packages/app-runtime/src/modules/appEvents/index.ts index b1eecc54b..d4097f75f 100644 --- a/packages/app-runtime/src/modules/appEvents/index.ts +++ b/packages/app-runtime/src/modules/appEvents/index.ts @@ -1,5 +1,4 @@ export * from "./AppLaunchModule"; -export * from "./LocalAccountDeletionDateChangedModule"; export * from "./MailReceivedModule"; export * from "./OnboardingChangeReceivedModule"; export * from "./RelationshipTemplateProcessedModule"; From af6f3218db60c85a15635d4831938bdf25fd5a39 Mon Sep 17 00:00:00 2001 From: Milena Czierlinski Date: Mon, 25 Nov 2024 12:22:15 +0100 Subject: [PATCH 13/42] test: clean up IdentityDeletionProcessStatusChangedModule test --- ...DeletionProcessStatusChangedModule.test.ts | 26 +++++++++++++------ 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/packages/app-runtime/test/modules/IdentityDeletionProcessStatusChangedModule.test.ts b/packages/app-runtime/test/modules/IdentityDeletionProcessStatusChangedModule.test.ts index 6551df508..e3807f3d1 100644 --- a/packages/app-runtime/test/modules/IdentityDeletionProcessStatusChangedModule.test.ts +++ b/packages/app-runtime/test/modules/IdentityDeletionProcessStatusChangedModule.test.ts @@ -31,10 +31,24 @@ describe("IdentityDeletionProcessStatusChanged", function () { await runtime.stop(); }); - // TODO: split test - test("should fire an event and set the deletionDate of the LocalAccount initiating an IdentityDeletionProcess", async function () { + test("should set the deletionDate of the LocalAccount initiating an IdentityDeletionProcess", async function () { expect(session.account.deletionDate).toBeUndefined(); + const initiateDeletionResult = await session.transportServices.identityDeletionProcesses.initiateIdentityDeletionProcess(); + + expect(session.account.deletionDate).toBeDefined(); + expect(session.account.deletionDate).toBe(initiateDeletionResult.value.gracePeriodEndsAt); + }); + + test("should unset the deletionDate of the LocalAccount cancelling an IdentityDeletionProcess", async function () { + await session.transportServices.identityDeletionProcesses.initiateIdentityDeletionProcess(); + expect(session.account.deletionDate).toBeDefined(); + + await session.transportServices.identityDeletionProcesses.cancelIdentityDeletionProcess(); + expect(session.account.deletionDate).toBeUndefined(); + }); + + test("should fire a LocalAccountDeletionDateChangedEvent initiating an IdentityDeletionProcess", async function () { const eventListener = new EventListener(runtime, [LocalAccountDeletionDateChangedEvent, IdentityDeletionProcessStatusChangedEvent]); eventListener.start(); @@ -53,13 +67,11 @@ describe("IdentityDeletionProcessStatusChanged", function () { expect(localAccountDeletionDateChangedEvent).toBeInstanceOf(LocalAccountDeletionDateChangedEvent); expect(localAccountDeletionDateChangedEvent.data).toBeDefined(); expect(localAccountDeletionDateChangedEvent.data).toBe(initiateDeletionResult.value.gracePeriodEndsAt); - - expect(session.account.deletionDate).toBeDefined(); - expect(session.account.deletionDate).toBe(initiateDeletionResult.value.gracePeriodEndsAt); }); - test("should fire an event and set the deletionDate of the LocalAccount cancelling an IdentityDeletionProcess", async function () { + test("should fire a LocalAccountDeletionDateChangedEvent cancelling an IdentityDeletionProcess", async function () { await session.transportServices.identityDeletionProcesses.initiateIdentityDeletionProcess(); + await TestUtil.awaitEvent(runtime, LocalAccountDeletionDateChangedEvent); const eventListener = new EventListener(runtime, [LocalAccountDeletionDateChangedEvent, IdentityDeletionProcessStatusChangedEvent]); eventListener.start(); @@ -78,7 +90,5 @@ describe("IdentityDeletionProcessStatusChanged", function () { const localAccountDeletionDateChangedEvent = events[1].instance as LocalAccountDeletionDateChangedEvent; expect(localAccountDeletionDateChangedEvent).toBeInstanceOf(LocalAccountDeletionDateChangedEvent); expect(localAccountDeletionDateChangedEvent.data).toBeUndefined(); - - expect(session.account.deletionDate).toBeUndefined(); }); }); From 273b1fff0734ab9e30af669795cc5f268dc686a2 Mon Sep 17 00:00:00 2001 From: Milena Czierlinski Date: Mon, 25 Nov 2024 16:23:34 +0100 Subject: [PATCH 14/42] fix: DatawalletSynchronizedModule --- .../DatawalletSynchronizedModule.ts | 16 ++- .../DatawalletSynchronizedModule.test.ts | 100 +++++++++++++++--- 2 files changed, 100 insertions(+), 16 deletions(-) diff --git a/packages/app-runtime/src/modules/runtimeEvents/DatawalletSynchronizedModule.ts b/packages/app-runtime/src/modules/runtimeEvents/DatawalletSynchronizedModule.ts index 95fcadabd..3d678e3f2 100644 --- a/packages/app-runtime/src/modules/runtimeEvents/DatawalletSynchronizedModule.ts +++ b/packages/app-runtime/src/modules/runtimeEvents/DatawalletSynchronizedModule.ts @@ -1,4 +1,4 @@ -import { DatawalletSynchronizedEvent } from "@nmshd/runtime"; +import { DatawalletSynchronizedEvent, IdentityDeletionProcessStatus } from "@nmshd/runtime"; import { AppRuntimeError } from "../../AppRuntimeError"; import { LocalAccountDeletionDateChangedEvent } from "../../events"; import { AppRuntimeModule, AppRuntimeModuleConfiguration } from "../AppRuntimeModule"; @@ -27,7 +27,19 @@ export class DatawalletSynchronizedModule extends AppRuntimeModule Date: Mon, 25 Nov 2024 16:24:49 +0100 Subject: [PATCH 15/42] fix: don't publish event updating LocalAccount deletionDate --- ...ntityDeletionProcessStatusChangedModule.ts | 8 +-- ...DeletionProcessStatusChangedModule.test.ts | 50 ++----------------- 2 files changed, 4 insertions(+), 54 deletions(-) diff --git a/packages/app-runtime/src/modules/runtimeEvents/IdentityDeletionProcessStatusChangedModule.ts b/packages/app-runtime/src/modules/runtimeEvents/IdentityDeletionProcessStatusChangedModule.ts index 921d82aac..dead705c7 100644 --- a/packages/app-runtime/src/modules/runtimeEvents/IdentityDeletionProcessStatusChangedModule.ts +++ b/packages/app-runtime/src/modules/runtimeEvents/IdentityDeletionProcessStatusChangedModule.ts @@ -1,6 +1,5 @@ import { IdentityDeletionProcessStatus, IdentityDeletionProcessStatusChangedEvent } from "@nmshd/runtime"; import { AppRuntimeError } from "../../AppRuntimeError"; -import { LocalAccountDeletionDateChangedEvent } from "../../events"; import { AppRuntimeModule, AppRuntimeModuleConfiguration } from "../AppRuntimeModule"; export interface IdentityDeletionProcessStatusChangedModuleConfig extends AppRuntimeModuleConfiguration {} @@ -21,11 +20,8 @@ export class IdentityDeletionProcessStatusChangedModule extends AppRuntimeModule switch (identityDeletionProcess.status) { case IdentityDeletionProcessStatus.Approved: - // TODO: why do we have to do this twice? - this.runtime.currentSession.account.deletionDate = event.data.gracePeriodEndsAt; + this.runtime.currentSession.account.deletionDate = identityDeletionProcess.gracePeriodEndsAt; await this.runtime.multiAccountController.updateLocalAccountDeletionDate(event.eventTargetAddress, identityDeletionProcess.gracePeriodEndsAt); - - this.runtime.eventBus.publish(new LocalAccountDeletionDateChangedEvent(event.eventTargetAddress, identityDeletionProcess.gracePeriodEndsAt)); break; case IdentityDeletionProcessStatus.Cancelled: @@ -34,8 +30,6 @@ export class IdentityDeletionProcessStatusChangedModule extends AppRuntimeModule this.runtime.currentSession.account.deletionDate = undefined; await this.runtime.multiAccountController.updateLocalAccountDeletionDate(event.eventTargetAddress); - - this.runtime.eventBus.publish(new LocalAccountDeletionDateChangedEvent(event.eventTargetAddress)); break; default: diff --git a/packages/app-runtime/test/modules/IdentityDeletionProcessStatusChangedModule.test.ts b/packages/app-runtime/test/modules/IdentityDeletionProcessStatusChangedModule.test.ts index e3807f3d1..4a6a8a4ab 100644 --- a/packages/app-runtime/test/modules/IdentityDeletionProcessStatusChangedModule.test.ts +++ b/packages/app-runtime/test/modules/IdentityDeletionProcessStatusChangedModule.test.ts @@ -1,6 +1,6 @@ -import { IdentityDeletionProcessStatus, IdentityDeletionProcessStatusChangedEvent } from "@nmshd/runtime"; -import { AppRuntime, LocalAccountDeletionDateChangedEvent, LocalAccountSession } from "../../src"; -import { EventListener, TestUtil } from "../lib"; +import { IdentityDeletionProcessStatus } from "@nmshd/runtime"; +import { AppRuntime, LocalAccountSession } from "../../src"; +import { TestUtil } from "../lib"; describe("IdentityDeletionProcessStatusChanged", function () { let runtime: AppRuntime; @@ -47,48 +47,4 @@ describe("IdentityDeletionProcessStatusChanged", function () { await session.transportServices.identityDeletionProcesses.cancelIdentityDeletionProcess(); expect(session.account.deletionDate).toBeUndefined(); }); - - test("should fire a LocalAccountDeletionDateChangedEvent initiating an IdentityDeletionProcess", async function () { - const eventListener = new EventListener(runtime, [LocalAccountDeletionDateChangedEvent, IdentityDeletionProcessStatusChangedEvent]); - eventListener.start(); - - const initiateDeletionResult = await session.transportServices.identityDeletionProcesses.initiateIdentityDeletionProcess(); - - eventListener.stop(); - const events = eventListener.getReceivedEvents(); - expect(events).toHaveLength(2); - - const identityDeletionProcessStatusChangedEvent = events[0].instance as IdentityDeletionProcessStatusChangedEvent; - expect(identityDeletionProcessStatusChangedEvent).toBeInstanceOf(IdentityDeletionProcessStatusChangedEvent); - expect(identityDeletionProcessStatusChangedEvent.data).toBeDefined(); - expect(identityDeletionProcessStatusChangedEvent.data.id).toBe(initiateDeletionResult.value.id); - - const localAccountDeletionDateChangedEvent = events[1].instance as LocalAccountDeletionDateChangedEvent; - expect(localAccountDeletionDateChangedEvent).toBeInstanceOf(LocalAccountDeletionDateChangedEvent); - expect(localAccountDeletionDateChangedEvent.data).toBeDefined(); - expect(localAccountDeletionDateChangedEvent.data).toBe(initiateDeletionResult.value.gracePeriodEndsAt); - }); - - test("should fire a LocalAccountDeletionDateChangedEvent cancelling an IdentityDeletionProcess", async function () { - await session.transportServices.identityDeletionProcesses.initiateIdentityDeletionProcess(); - await TestUtil.awaitEvent(runtime, LocalAccountDeletionDateChangedEvent); - - const eventListener = new EventListener(runtime, [LocalAccountDeletionDateChangedEvent, IdentityDeletionProcessStatusChangedEvent]); - eventListener.start(); - - const cancelDeletionResult = await session.transportServices.identityDeletionProcesses.cancelIdentityDeletionProcess(); - - eventListener.stop(); - const events = eventListener.getReceivedEvents(); - expect(events).toHaveLength(2); - - const identityDeletionProcessStatusChangedEvent = events[0].instance as IdentityDeletionProcessStatusChangedEvent; - expect(identityDeletionProcessStatusChangedEvent).toBeInstanceOf(IdentityDeletionProcessStatusChangedEvent); - expect(identityDeletionProcessStatusChangedEvent.data).toBeDefined(); - expect(identityDeletionProcessStatusChangedEvent.data.id).toBe(cancelDeletionResult.value.id); - - const localAccountDeletionDateChangedEvent = events[1].instance as LocalAccountDeletionDateChangedEvent; - expect(localAccountDeletionDateChangedEvent).toBeInstanceOf(LocalAccountDeletionDateChangedEvent); - expect(localAccountDeletionDateChangedEvent.data).toBeUndefined(); - }); }); From b535752bea69e2c5dfb28bed8ff977b6bda8cc9f Mon Sep 17 00:00:00 2001 From: Milena Czierlinski Date: Mon, 25 Nov 2024 17:38:28 +0100 Subject: [PATCH 16/42] fix and test: getAccounts(Not)InDeletion --- .../multiAccount/MultiAccountController.ts | 5 +- .../MultiAccountController.test.ts | 68 +++++++++++++++++++ 2 files changed, 70 insertions(+), 3 deletions(-) create mode 100644 packages/app-runtime/test/multiAccount/MultiAccountController.test.ts diff --git a/packages/app-runtime/src/multiAccount/MultiAccountController.ts b/packages/app-runtime/src/multiAccount/MultiAccountController.ts index 8d3e91c38..c47d7a23d 100644 --- a/packages/app-runtime/src/multiAccount/MultiAccountController.ts +++ b/packages/app-runtime/src/multiAccount/MultiAccountController.ts @@ -82,16 +82,15 @@ export class MultiAccountController { return dbAccounts.map((account) => LocalAccount.from(account)); } - // TODO: test public async getAccountsInDeletion(): Promise { const allAccounts = await this.getAccounts(); - const accountsInDeletion = allAccounts.filter((item) => item.deletionDate === undefined); + const accountsInDeletion = allAccounts.filter((item) => item.deletionDate !== undefined); return accountsInDeletion; } public async getAccountsNotInDeletion(): Promise { const allAccounts = await this.getAccounts(); - const accountsNotInDeletion = allAccounts.filter((item) => item.deletionDate !== undefined); + const accountsNotInDeletion = allAccounts.filter((item) => item.deletionDate === undefined); return accountsNotInDeletion; } diff --git a/packages/app-runtime/test/multiAccount/MultiAccountController.test.ts b/packages/app-runtime/test/multiAccount/MultiAccountController.test.ts new file mode 100644 index 000000000..158848e28 --- /dev/null +++ b/packages/app-runtime/test/multiAccount/MultiAccountController.test.ts @@ -0,0 +1,68 @@ +import { IdentityDeletionProcessStatus } from "@nmshd/runtime"; +import { AppRuntime, LocalAccountDTO, LocalAccountSession } from "../../src"; +import { TestUtil } from "../lib"; + +describe("MultiAccountController", function () { + let runtime: AppRuntime; + + let account1: LocalAccountDTO; + let account2: LocalAccountDTO; + let account3: LocalAccountDTO; + + let session1: LocalAccountSession; + let session2: LocalAccountSession; + let session3: LocalAccountSession; + + beforeAll(async function () { + runtime = await TestUtil.createRuntime(); + await runtime.start(); + + [account1, account2, account3] = await TestUtil.provideAccounts(runtime, 3); + + session1 = await runtime.selectAccount(account1.id); + session2 = await runtime.selectAccount(account2.id); + session3 = await runtime.selectAccount(account3.id); + }); + + afterEach(async () => { + for (const session of [session1, session2, session3]) { + const activeIdentityDeletionProcess = await session.transportServices.identityDeletionProcesses.getActiveIdentityDeletionProcess(); + if (!activeIdentityDeletionProcess.isSuccess) { + return; + } + + let abortResult; + if (activeIdentityDeletionProcess.value.status === IdentityDeletionProcessStatus.Approved) { + abortResult = await session.transportServices.identityDeletionProcesses.cancelIdentityDeletionProcess(); + } + if (abortResult?.isError) throw abortResult.error; + } + }); + + afterAll(async function () { + await runtime.stop(); + }); + + test("should get all accounts in deletion", async function () { + await session1.transportServices.identityDeletionProcesses.initiateIdentityDeletionProcess(); + await session2.transportServices.identityDeletionProcesses.initiateIdentityDeletionProcess(); + + const accountsInDeletion = await runtime.multiAccountController.getAccountsInDeletion(); + expect(accountsInDeletion).toHaveLength(2); + + const addressesInDeletion = accountsInDeletion.map((account) => account.address!.toString()); + expect(addressesInDeletion).toContain(account1.address); + expect(addressesInDeletion).toContain(account2.address); + }); + + test("should get all accounts not in deletion", async function () { + await session1.transportServices.identityDeletionProcesses.initiateIdentityDeletionProcess(); + await session2.transportServices.identityDeletionProcesses.initiateIdentityDeletionProcess(); + + const accountsNotInDeletion = await runtime.multiAccountController.getAccountsNotInDeletion(); + expect(accountsNotInDeletion).toHaveLength(1); + + const addressesNotInDeletion = accountsNotInDeletion.map((account) => account.address!.toString()); + expect(addressesNotInDeletion).toContain(account3.address); + }); +}); From b518fa9f4ec2179351b01c8d1b8119bc794a9c53 Mon Sep 17 00:00:00 2001 From: Milena Czierlinski Date: Tue, 26 Nov 2024 13:34:52 +0100 Subject: [PATCH 17/42] test: don's skip tests --- .../test/modules/DatawalletSynchronizedModule.test.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/app-runtime/test/modules/DatawalletSynchronizedModule.test.ts b/packages/app-runtime/test/modules/DatawalletSynchronizedModule.test.ts index dd367b3ae..9343328d4 100644 --- a/packages/app-runtime/test/modules/DatawalletSynchronizedModule.test.ts +++ b/packages/app-runtime/test/modules/DatawalletSynchronizedModule.test.ts @@ -73,13 +73,13 @@ describe("DatawalletSynchronized", function () { expect(sessionDevice2.account.deletionDate).toBeUndefined(); }); - test.skip("should set the deletionDate of the LocalAccount initiating an IdentityDeletionProcess on a second device that was offline", async function () { + test("should set the deletionDate of the LocalAccount initiating an IdentityDeletionProcess on a second device that was offline", async function () { await runtimeDevice2.stop(); await sessionDevice1.transportServices.identityDeletionProcesses.initiateIdentityDeletionProcess(); expect(sessionDevice2.account.deletionDate).toBeUndefined(); - await runtimeDevice2.init(); + await runtimeDevice2.init(); // TODO: do the modules need to be turned on? await runtimeDevice2.start(); await sessionDevice2.transportServices.account.syncDatawallet(); @@ -88,7 +88,7 @@ describe("DatawalletSynchronized", function () { expect(sessionDevice2.account.deletionDate).toBeDefined(); }); - test.skip("should unset the deletionDate of the LocalAccount cancelling an IdentityDeletionProcess on a second device that was offline", async function () { + test("should unset the deletionDate of the LocalAccount cancelling an IdentityDeletionProcess on a second device that was offline", async function () { await sessionDevice1.transportServices.identityDeletionProcesses.initiateIdentityDeletionProcess(); await sessionDevice2.transportServices.account.syncDatawallet(); await TestUtil.awaitEvent(runtimeDevice2, LocalAccountDeletionDateChangedEvent); From 9abeb756efcf7b60ba2c217a146ec157f5a31aec Mon Sep 17 00:00:00 2001 From: Milena Czierlinski Date: Tue, 26 Nov 2024 17:26:20 +0100 Subject: [PATCH 18/42] test: remove unrelated test --- .../test/transport/relationships.test.ts | 32 ------------------- 1 file changed, 32 deletions(-) diff --git a/packages/runtime/test/transport/relationships.test.ts b/packages/runtime/test/transport/relationships.test.ts index db3df4ef9..42d163409 100644 --- a/packages/runtime/test/transport/relationships.test.ts +++ b/packages/runtime/test/transport/relationships.test.ts @@ -347,38 +347,6 @@ describe("Relationship status validations on active relationship", () => { }); }); -describe.only("Templator with active IdentityDeletionProcess", () => { - const serviceProvider = new RuntimeServiceProvider(); - let services1: TestRuntimeServices; - let services2: TestRuntimeServices; - - beforeAll(async () => { - const runtimeServices = await serviceProvider.launch(2, { enableRequestModule: true, enableDeciderModule: true, enableNotificationModule: true }); - services1 = runtimeServices[0]; - services2 = runtimeServices[1]; - }, 30000); - - afterAll(() => serviceProvider.stop()); - - test.only("returns error if templator has active IdentityDeletionProcess", async () => { - const templateId = (await exchangeTemplate(services1.transport, services2.transport)).id; - - const createRelationshipResponse = await services2.transport.relationships.createRelationship({ - templateId: templateId, - creationContent: emptyRelationshipCreationContent - }); - await services1.transport.identityDeletionProcesses.initiateIdentityDeletionProcess(); - - const response = await services2.transport.relationships.getRelationships({}); - - console.log(response); - // expect(createRelationshipResponse).toBeAnError( - // "The Identity who created the RelationshipTemplate is currently in the process of deleting itself. Thus, it is not possible to establish a Relationship to it.", - // "error.transport.relationships.activeIdentityDeletionProcessOfOwnerOfRelationshipTemplate" - // ); - }); -}); - describe("Relationships query", () => { test("query own relationship", async () => { await ensureActiveRelationship(services1.transport, services2.transport); From 20d507733cb9c35a0e141a05010fc2333c5fb0c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20K=C3=B6nig?= Date: Wed, 27 Nov 2024 15:11:54 +0100 Subject: [PATCH 19/42] chore: remove dangerous getters --- packages/app-runtime/src/AppRuntime.ts | 24 +----------------------- 1 file changed, 1 insertion(+), 23 deletions(-) diff --git a/packages/app-runtime/src/AppRuntime.ts b/packages/app-runtime/src/AppRuntime.ts index 84bc151d3..6852d4327 100644 --- a/packages/app-runtime/src/AppRuntime.ts +++ b/packages/app-runtime/src/AppRuntime.ts @@ -9,7 +9,7 @@ import { AppConfig, AppConfigOverwrite, createAppConfig } from "./AppConfig"; import { AppRuntimeErrors } from "./AppRuntimeErrors"; import { AppRuntimeServices } from "./AppRuntimeServices"; import { AppStringProcessor } from "./AppStringProcessor"; -import { AccountSelectedEvent, RelationshipSelectedEvent } from "./events"; +import { AccountSelectedEvent } from "./events"; import { AppServices, IUIBridge } from "./extensibility"; import { AppLaunchModule, @@ -88,14 +88,6 @@ export class AppRuntime extends Runtime { private readonly sessionStorage = new SessionStorage(); - public get currentAccount(): LocalAccountDTO { - return this.sessionStorage.currentSession.account; - } - - public get currentSession(): LocalAccountSession { - return this.sessionStorage.currentSession; - } - public getSessions(): LocalAccountSession[] { return this.sessionStorage.getSessions(); } @@ -211,20 +203,6 @@ export class AppRuntime extends Runtime { return UserfriendlyResult.ok(accountSelectionResult.value); } - public async selectRelationship(id?: string): Promise { - if (!id) { - this.currentSession.selectedRelationship = undefined; - return; - } - - const result = await this.currentSession.appServices.relationships.renderRelationship(id); - if (result.isError) throw result.error; - - const relationship = result.value; - this.currentSession.selectedRelationship = relationship; - this.eventBus.publish(new RelationshipSelectedEvent(this.currentSession.address, relationship)); - } - public getHealth(): Promise { const health = { isHealthy: true, From 863f39d409f3ba6769441ebf3b70603f7dbedbce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20K=C3=B6nig?= Date: Wed, 27 Nov 2024 15:12:38 +0100 Subject: [PATCH 20/42] fix: write deletionDate to cached local account from MultiAccountController --- .../app-runtime/src/multiAccount/MultiAccountController.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/app-runtime/src/multiAccount/MultiAccountController.ts b/packages/app-runtime/src/multiAccount/MultiAccountController.ts index c47d7a23d..a30dd1dab 100644 --- a/packages/app-runtime/src/multiAccount/MultiAccountController.ts +++ b/packages/app-runtime/src/multiAccount/MultiAccountController.ts @@ -262,6 +262,9 @@ export class MultiAccountController { account.deletionDate = deletionDate ?? undefined; await this._localAccounts.update(oldAccount, account); + + const cachedAccount = this.sessionStorage.findSession(address)?.account; + if (cachedAccount) cachedAccount.deletionDate = deletionDate; } public async updateLastAccessedAt(accountId: string): Promise { From f5b17dc2d870025c3442c6bbdaebb393792ad94e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20K=C3=B6nig?= Date: Wed, 27 Nov 2024 15:12:46 +0100 Subject: [PATCH 21/42] fix: use correct apis --- .../modules/runtimeEvents/DatawalletSynchronizedModule.ts | 4 ++-- .../IdentityDeletionProcessStatusChangedModule.ts | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/app-runtime/src/modules/runtimeEvents/DatawalletSynchronizedModule.ts b/packages/app-runtime/src/modules/runtimeEvents/DatawalletSynchronizedModule.ts index 3d678e3f2..9f2d0ff9c 100644 --- a/packages/app-runtime/src/modules/runtimeEvents/DatawalletSynchronizedModule.ts +++ b/packages/app-runtime/src/modules/runtimeEvents/DatawalletSynchronizedModule.ts @@ -17,7 +17,8 @@ export class DatawalletSynchronizedModule extends AppRuntimeModule Date: Wed, 27 Nov 2024 15:41:20 +0100 Subject: [PATCH 22/42] refactor: massively simplify tests --- .../DatawalletSynchronizedModule.test.ts | 109 +++--------------- 1 file changed, 17 insertions(+), 92 deletions(-) diff --git a/packages/app-runtime/test/modules/DatawalletSynchronizedModule.test.ts b/packages/app-runtime/test/modules/DatawalletSynchronizedModule.test.ts index 9343328d4..6a50ca323 100644 --- a/packages/app-runtime/test/modules/DatawalletSynchronizedModule.test.ts +++ b/packages/app-runtime/test/modules/DatawalletSynchronizedModule.test.ts @@ -1,6 +1,7 @@ -import { DatawalletSynchronizedEvent, IdentityDeletionProcessStatus } from "@nmshd/runtime"; +import { CoreId } from "@nmshd/core-types"; +import { IdentityDeletionProcessStatus } from "@nmshd/runtime"; import { AppRuntime, LocalAccountDeletionDateChangedEvent, LocalAccountSession } from "../../src"; -import { EventListener, TestUtil } from "../lib"; +import { TestUtil } from "../lib"; describe("DatawalletSynchronized", function () { let runtimeDevice1: AppRuntime; @@ -34,13 +35,13 @@ describe("DatawalletSynchronized", function () { return; } - let abortResult; if (activeIdentityDeletionProcess.value.status === IdentityDeletionProcessStatus.Approved) { - abortResult = await sessionDevice1.transportServices.identityDeletionProcesses.cancelIdentityDeletionProcess(); + const abortResult = await sessionDevice1.transportServices.identityDeletionProcesses.cancelIdentityDeletionProcess(); + if (abortResult.isError) throw abortResult.error; + await sessionDevice2.transportServices.account.syncDatawallet(); - // await TestUtil.awaitEvent(runtimeDevice2, LocalAccountDeletionDateChangedEvent); + await TestUtil.awaitEvent(runtimeDevice2, LocalAccountDeletionDateChangedEvent); } - if (abortResult?.isError) throw abortResult.error; }); afterAll(async function () { @@ -48,111 +49,35 @@ describe("DatawalletSynchronized", function () { await runtimeDevice2.stop(); }); - test("should set the deletionDate of the LocalAccount initiate an IdentityDeletionProcess on a second device that is online", async function () { + test("should set the deletionDate on the LocalAccount on a second device when an IdentityDeletionProcess is initiated", async function () { const initiateResult = await sessionDevice1.transportServices.identityDeletionProcesses.initiateIdentityDeletionProcess(); expect(sessionDevice2.account.deletionDate).toBeUndefined(); await sessionDevice2.transportServices.account.syncDatawallet(); - await TestUtil.awaitEvent(runtimeDevice2, LocalAccountDeletionDateChangedEvent); + const event = await TestUtil.awaitEvent(runtimeDevice2, LocalAccountDeletionDateChangedEvent); + expect(event.data).toBe(initiateResult.value.gracePeriodEndsAt); - expect(sessionDevice2.account.deletionDate).toBeDefined(); expect(sessionDevice2.account.deletionDate).toBe(initiateResult.value.gracePeriodEndsAt); - }); - - test("should unset the deletionDate of the LocalAccount cancelling an IdentityDeletionProcess on a second device that is online", async function () { - await sessionDevice1.transportServices.identityDeletionProcesses.initiateIdentityDeletionProcess(); - await sessionDevice2.transportServices.account.syncDatawallet(); - await TestUtil.awaitEvent(runtimeDevice2, LocalAccountDeletionDateChangedEvent); - - await sessionDevice1.transportServices.identityDeletionProcesses.cancelIdentityDeletionProcess(); - expect(sessionDevice2.account.deletionDate).toBeDefined(); - - await sessionDevice2.transportServices.account.syncDatawallet(); - await TestUtil.awaitEvent(runtimeDevice2, LocalAccountDeletionDateChangedEvent); - expect(sessionDevice2.account.deletionDate).toBeUndefined(); - }); - - test("should set the deletionDate of the LocalAccount initiating an IdentityDeletionProcess on a second device that was offline", async function () { - await runtimeDevice2.stop(); - - await sessionDevice1.transportServices.identityDeletionProcesses.initiateIdentityDeletionProcess(); - expect(sessionDevice2.account.deletionDate).toBeUndefined(); - - await runtimeDevice2.init(); // TODO: do the modules need to be turned on? - await runtimeDevice2.start(); - - await sessionDevice2.transportServices.account.syncDatawallet(); - // await TestUtil.awaitEvent(runtimeDevice2, LocalAccountDeletionDateChangedEvent); - - expect(sessionDevice2.account.deletionDate).toBeDefined(); + const account = await runtimeDevice2.multiAccountController.getAccount(CoreId.from(sessionDevice2.account.id)); + expect(account.deletionDate).toBe(initiateResult.value.gracePeriodEndsAt); }); - test("should unset the deletionDate of the LocalAccount cancelling an IdentityDeletionProcess on a second device that was offline", async function () { + test("should unset the deletionDate on the LocalAccount on a second device when an IdentityDeletionProcess is cancelled", async function () { await sessionDevice1.transportServices.identityDeletionProcesses.initiateIdentityDeletionProcess(); await sessionDevice2.transportServices.account.syncDatawallet(); await TestUtil.awaitEvent(runtimeDevice2, LocalAccountDeletionDateChangedEvent); - await runtimeDevice2.stop(); - await sessionDevice1.transportServices.identityDeletionProcesses.cancelIdentityDeletionProcess(); expect(sessionDevice2.account.deletionDate).toBeDefined(); - await runtimeDevice2.init(); - await runtimeDevice2.start(); - await sessionDevice2.transportServices.account.syncDatawallet(); - // await TestUtil.awaitEvent(runtimeDevice2, LocalAccountDeletionDateChangedEvent); + const event = await TestUtil.awaitEvent(runtimeDevice2, LocalAccountDeletionDateChangedEvent); + expect(event.data).toBeUndefined(); - expect(sessionDevice2.account.deletionDate).toBeDefined(); - }); - - test("should publish a LocalAccountDeletionDateChangedEvent if a deletion date is set after a datawallet sync", async function () { - const initiateDeletionResult = await sessionDevice1.transportServices.identityDeletionProcesses.initiateIdentityDeletionProcess(); expect(sessionDevice2.account.deletionDate).toBeUndefined(); - const eventListener = new EventListener(runtimeDevice2, [DatawalletSynchronizedEvent, LocalAccountDeletionDateChangedEvent]); - eventListener.start(); - - await sessionDevice2.transportServices.account.syncDatawallet(); - await TestUtil.awaitEvent(runtimeDevice2, LocalAccountDeletionDateChangedEvent); - - eventListener.stop(); - const events = eventListener.getReceivedEvents(); - expect(events).toHaveLength(2); - - const identityDeletionProcessStatusChangedEvent = events[0].instance as DatawalletSynchronizedEvent; - expect(identityDeletionProcessStatusChangedEvent).toBeInstanceOf(DatawalletSynchronizedEvent); - - const localAccountDeletionDateChangedEvent = events[1].instance as LocalAccountDeletionDateChangedEvent; - expect(localAccountDeletionDateChangedEvent).toBeInstanceOf(LocalAccountDeletionDateChangedEvent); - expect(localAccountDeletionDateChangedEvent.data).toBeDefined(); - expect(localAccountDeletionDateChangedEvent.data).toBe(initiateDeletionResult.value.gracePeriodEndsAt); - }); - - test("should publish a LocalAccountDeletionDateChangedEvent if a deletion date is unset after a datawallet sync", async function () { - await sessionDevice1.transportServices.identityDeletionProcesses.initiateIdentityDeletionProcess(); - await sessionDevice2.transportServices.account.syncDatawallet(); - await TestUtil.awaitEvent(runtimeDevice2, LocalAccountDeletionDateChangedEvent); - - await sessionDevice1.transportServices.identityDeletionProcesses.cancelIdentityDeletionProcess(); - expect(sessionDevice2.account.deletionDate).toBeDefined(); - - const eventListener = new EventListener(runtimeDevice2, [DatawalletSynchronizedEvent, LocalAccountDeletionDateChangedEvent]); - eventListener.start(); - - await sessionDevice2.transportServices.account.syncDatawallet(); - await TestUtil.awaitEvent(runtimeDevice2, LocalAccountDeletionDateChangedEvent); - - eventListener.stop(); - const events = eventListener.getReceivedEvents(); - expect(events).toHaveLength(2); - - const identityDeletionProcessStatusChangedEvent = events[0].instance as DatawalletSynchronizedEvent; - expect(identityDeletionProcessStatusChangedEvent).toBeInstanceOf(DatawalletSynchronizedEvent); - - const localAccountDeletionDateChangedEvent = events[1].instance as LocalAccountDeletionDateChangedEvent; - expect(localAccountDeletionDateChangedEvent).toBeInstanceOf(LocalAccountDeletionDateChangedEvent); - expect(localAccountDeletionDateChangedEvent.data).toBeUndefined(); + const account = await runtimeDevice2.multiAccountController.getAccount(CoreId.from(sessionDevice2.account.id)); + expect(account.deletionDate).toBeUndefined(); }); }); From 1279d3428276fad0d88f4f2e06b3d3b75a9ba9a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20K=C3=B6nig?= Date: Wed, 27 Nov 2024 15:49:31 +0100 Subject: [PATCH 23/42] chore: naming --- .../test/modules/DatawalletSynchronizedModule.test.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/app-runtime/test/modules/DatawalletSynchronizedModule.test.ts b/packages/app-runtime/test/modules/DatawalletSynchronizedModule.test.ts index 6a50ca323..0dd34e5a4 100644 --- a/packages/app-runtime/test/modules/DatawalletSynchronizedModule.test.ts +++ b/packages/app-runtime/test/modules/DatawalletSynchronizedModule.test.ts @@ -50,17 +50,17 @@ describe("DatawalletSynchronized", function () { }); test("should set the deletionDate on the LocalAccount on a second device when an IdentityDeletionProcess is initiated", async function () { - const initiateResult = await sessionDevice1.transportServices.identityDeletionProcesses.initiateIdentityDeletionProcess(); + const initiateDeletionResult = await sessionDevice1.transportServices.identityDeletionProcesses.initiateIdentityDeletionProcess(); expect(sessionDevice2.account.deletionDate).toBeUndefined(); await sessionDevice2.transportServices.account.syncDatawallet(); const event = await TestUtil.awaitEvent(runtimeDevice2, LocalAccountDeletionDateChangedEvent); - expect(event.data).toBe(initiateResult.value.gracePeriodEndsAt); + expect(event.data).toBe(initiateDeletionResult.value.gracePeriodEndsAt); - expect(sessionDevice2.account.deletionDate).toBe(initiateResult.value.gracePeriodEndsAt); + expect(sessionDevice2.account.deletionDate).toBe(initiateDeletionResult.value.gracePeriodEndsAt); const account = await runtimeDevice2.multiAccountController.getAccount(CoreId.from(sessionDevice2.account.id)); - expect(account.deletionDate).toBe(initiateResult.value.gracePeriodEndsAt); + expect(account.deletionDate).toBe(initiateDeletionResult.value.gracePeriodEndsAt); }); test("should unset the deletionDate on the LocalAccount on a second device when an IdentityDeletionProcess is cancelled", async function () { From f927644f77f9a34cf16a74dcb6080c2423e69d75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20K=C3=B6nig?= Date: Wed, 27 Nov 2024 15:49:43 +0100 Subject: [PATCH 24/42] chore: more asserts --- .../IdentityDeletionProcessStatusChangedModule.test.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/app-runtime/test/modules/IdentityDeletionProcessStatusChangedModule.test.ts b/packages/app-runtime/test/modules/IdentityDeletionProcessStatusChangedModule.test.ts index 4a6a8a4ab..79b4d0a7c 100644 --- a/packages/app-runtime/test/modules/IdentityDeletionProcessStatusChangedModule.test.ts +++ b/packages/app-runtime/test/modules/IdentityDeletionProcessStatusChangedModule.test.ts @@ -1,3 +1,4 @@ +import { CoreId } from "@nmshd/core-types"; import { IdentityDeletionProcessStatus } from "@nmshd/runtime"; import { AppRuntime, LocalAccountSession } from "../../src"; import { TestUtil } from "../lib"; @@ -36,8 +37,10 @@ describe("IdentityDeletionProcessStatusChanged", function () { const initiateDeletionResult = await session.transportServices.identityDeletionProcesses.initiateIdentityDeletionProcess(); - expect(session.account.deletionDate).toBeDefined(); expect(session.account.deletionDate).toBe(initiateDeletionResult.value.gracePeriodEndsAt); + + const account = await runtime.multiAccountController.getAccount(CoreId.from(session.account.id)); + expect(account.deletionDate).toBe(initiateDeletionResult.value.gracePeriodEndsAt); }); test("should unset the deletionDate of the LocalAccount cancelling an IdentityDeletionProcess", async function () { @@ -46,5 +49,8 @@ describe("IdentityDeletionProcessStatusChanged", function () { await session.transportServices.identityDeletionProcesses.cancelIdentityDeletionProcess(); expect(session.account.deletionDate).toBeUndefined(); + + const account = await runtime.multiAccountController.getAccount(CoreId.from(session.account.id)); + expect(account.deletionDate).toBeUndefined(); }); }); From 325bed014a85d3b403831f37b3f7813a47a36ea1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20K=C3=B6nig?= Date: Wed, 27 Nov 2024 16:06:30 +0100 Subject: [PATCH 25/42] refactor: move event publish location --- .../transport/src/modules/accounts/AccountController.ts | 7 +------ packages/transport/src/modules/sync/SyncController.ts | 3 +++ 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/packages/transport/src/modules/accounts/AccountController.ts b/packages/transport/src/modules/accounts/AccountController.ts index bc473ffb3..6dee85801 100644 --- a/packages/transport/src/modules/accounts/AccountController.ts +++ b/packages/transport/src/modules/accounts/AccountController.ts @@ -8,7 +8,6 @@ import { CoreCrypto } from "../../core/CoreCrypto"; import { DbCollectionName } from "../../core/DbCollectionName"; import { DependencyOverrides } from "../../core/DependencyOverrides"; import { TransportLoggerFactory } from "../../core/TransportLoggerFactory"; -import { DatawalletSynchronizedEvent } from "../../events"; import { PasswordGenerator } from "../../util"; import { CertificateController } from "../certificates/CertificateController"; import { CertificateIssuer } from "../certificates/CertificateIssuer"; @@ -231,13 +230,9 @@ export class AccountController { } public async syncDatawallet(force = false): Promise { - if (!force && !this.autoSync) { - return; - } + if (!force && !this.autoSync) return; await this.synchronization.sync("OnlyDatawallet"); - this.transport.eventBus.publish(new DatawalletSynchronizedEvent(this.identity.address.toString())); - return; } public async syncEverything(): Promise { diff --git a/packages/transport/src/modules/sync/SyncController.ts b/packages/transport/src/modules/sync/SyncController.ts index 638bfce7d..7be642e25 100644 --- a/packages/transport/src/modules/sync/SyncController.ts +++ b/packages/transport/src/modules/sync/SyncController.ts @@ -3,6 +3,7 @@ import { log } from "@js-soft/ts-utils"; import { CoreDate, CoreError, CoreId } from "@nmshd/core-types"; import { ControllerName, RequestError, TransportController, TransportCoreErrors, TransportError, TransportLoggerFactory } from "../../core"; import { DependencyOverrides } from "../../core/DependencyOverrides"; +import { DatawalletSynchronizedEvent } from "../../events/DatawalletSynchronizedEvent"; import { AccountController } from "../accounts/AccountController"; import { ChangedItems } from "./ChangedItems"; import { DatawalletModificationMapper } from "./DatawalletModificationMapper"; @@ -98,6 +99,8 @@ export class SyncController extends TransportController { } finally { if (this.datawalletEnabled && (await this.unpushedDatawalletModifications.exists())) { await this.syncDatawallet().catch((e) => this.log.error(e)); + + this.transport.eventBus.publish(new DatawalletSynchronizedEvent(this.parent.identity.address.toString())); } } } From e4386a223ad29252bdf68393b436a8609959e17d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20K=C3=B6nig?= Date: Wed, 27 Nov 2024 16:39:56 +0100 Subject: [PATCH 26/42] fix: change location of publishing --- packages/transport/src/modules/sync/SyncController.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/transport/src/modules/sync/SyncController.ts b/packages/transport/src/modules/sync/SyncController.ts index 7be642e25..7c2b01d74 100644 --- a/packages/transport/src/modules/sync/SyncController.ts +++ b/packages/transport/src/modules/sync/SyncController.ts @@ -99,9 +99,9 @@ export class SyncController extends TransportController { } finally { if (this.datawalletEnabled && (await this.unpushedDatawalletModifications.exists())) { await this.syncDatawallet().catch((e) => this.log.error(e)); - - this.transport.eventBus.publish(new DatawalletSynchronizedEvent(this.parent.identity.address.toString())); } + + this.transport.eventBus.publish(new DatawalletSynchronizedEvent(this.parent.identity.address.toString())); } } From de6eb61a135702bc5ea1582e1f3322c53f2217da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20K=C3=B6nig?= Date: Thu, 28 Nov 2024 08:29:20 +0100 Subject: [PATCH 27/42] refactor: collect IDs for changed objects during datawallet sync --- .../src/modules/sync/ChangedItems.ts | 7 +- .../sync/DatawalletModificationsProcessor.ts | 9 +++ .../src/modules/sync/SyncController.ts | 77 ++++++++----------- 3 files changed, 49 insertions(+), 44 deletions(-) diff --git a/packages/transport/src/modules/sync/ChangedItems.ts b/packages/transport/src/modules/sync/ChangedItems.ts index 4f9c9a01f..7ffde03e5 100644 --- a/packages/transport/src/modules/sync/ChangedItems.ts +++ b/packages/transport/src/modules/sync/ChangedItems.ts @@ -12,7 +12,8 @@ export class ChangedItems implements IChangedItems { public constructor( public readonly relationships: Relationship[] = [], public readonly messages: Message[] = [], - public readonly identityDeletionProcesses: IdentityDeletionProcess[] = [] + public readonly identityDeletionProcesses: IdentityDeletionProcess[] = [], + public readonly changedObjectIdentifiersDuringDatawalletSync: string[] = [] ) {} public addItem(item: Relationship | Message | IdentityDeletionProcess): void { @@ -24,4 +25,8 @@ export class ChangedItems implements IChangedItems { this.identityDeletionProcesses.push(item); } } + + public addChangedObjectsIdentifiersDuringDatawalletSync(identifiers: string[]): void { + this.changedObjectIdentifiersDuringDatawalletSync.push(...identifiers); + } } diff --git a/packages/transport/src/modules/sync/DatawalletModificationsProcessor.ts b/packages/transport/src/modules/sync/DatawalletModificationsProcessor.ts index 4ebc5a778..3e6d6bde0 100644 --- a/packages/transport/src/modules/sync/DatawalletModificationsProcessor.ts +++ b/packages/transport/src/modules/sync/DatawalletModificationsProcessor.ts @@ -32,6 +32,11 @@ export class DatawalletModificationsProcessor { private readonly cacheChanges: DatawalletModification[]; private readonly deletedObjectIdentifiers: string[] = []; + private readonly _changedObjectIdentifiers: Set = new Set(); + public get changedObjectIdentifiers(): string[] { + return Array.from(this._changedObjectIdentifiers); + } + public get log(): ILogger { return this.logger; } @@ -64,6 +69,8 @@ export class DatawalletModificationsProcessor { const modificationsGroupedByObjectIdentifier = _.groupBy(this.modificationsWithoutCacheChanges, (m) => m.objectIdentifier); for (const objectIdentifier in modificationsGroupedByObjectIdentifier) { + this._changedObjectIdentifiers.add(objectIdentifier); + const currentModifications = modificationsGroupedByObjectIdentifier[objectIdentifier]; const targetCollectionName = currentModifications[0].collection; @@ -137,6 +144,8 @@ export class DatawalletModificationsProcessor { this.ensureAllItemsAreCacheable(); const cacheChangesWithoutDeletes = this.cacheChanges.filter((c) => !this.deletedObjectIdentifiers.some((d) => c.objectIdentifier.equals(d))); + cacheChangesWithoutDeletes.map((c) => c.objectIdentifier.toString()).forEach((objectIdentifier) => this._changedObjectIdentifiers.add(objectIdentifier)); + const cacheChangesGroupedByCollection = this.groupCacheChangesByCollection(cacheChangesWithoutDeletes); const caches = await this.cacheFetcher.fetchCacheFor({ diff --git a/packages/transport/src/modules/sync/SyncController.ts b/packages/transport/src/modules/sync/SyncController.ts index 7c2b01d74..70ef5cfc6 100644 --- a/packages/transport/src/modules/sync/SyncController.ts +++ b/packages/transport/src/modules/sync/SyncController.ts @@ -68,10 +68,10 @@ export class SyncController extends TransportController { private currentSync?: LocalSyncRun; private currentSyncRun?: BackboneSyncRun; - public async sync(whatToSync: "OnlyDatawallet"): Promise; + public async sync(whatToSync: "OnlyDatawallet"): Promise; public async sync(whatToSync: "Everything"): Promise; - public async sync(whatToSync: WhatToSync): Promise; - public async sync(whatToSync: WhatToSync = "Everything"): Promise { + public async sync(whatToSync: WhatToSync): Promise; + public async sync(whatToSync: WhatToSync = "Everything"): Promise { if (this.currentSync?.includes(whatToSync)) { return await this.currentSync.promise; } @@ -94,32 +94,33 @@ export class SyncController extends TransportController { } private async _syncAndResyncDatawallet(whatToSync: WhatToSync = "Everything") { + const changedItems = new ChangedItems(); + try { - return await this._sync(whatToSync); + await this._sync(whatToSync, changedItems); } finally { if (this.datawalletEnabled && (await this.unpushedDatawalletModifications.exists())) { - await this.syncDatawallet().catch((e) => this.log.error(e)); + await this.syncDatawallet(changedItems).catch((e) => this.log.error(e)); } this.transport.eventBus.publish(new DatawalletSynchronizedEvent(this.parent.identity.address.toString())); } + + return changedItems; } @log() - private async _sync(whatToSync: WhatToSync): Promise { - if (whatToSync === "OnlyDatawallet") { - const value = await this.syncDatawallet(); - return value; - } + private async _sync(whatToSync: WhatToSync, changedItems: ChangedItems): Promise { + if (whatToSync === "OnlyDatawallet") return await this.syncDatawallet(changedItems); - const externalEventSyncResult = await this.syncExternalEvents(); + const externalEventSyncResult = await this.syncExternalEvents(changedItems); await this.setLastCompletedSyncTime(); - if (externalEventSyncResult.externalEventResults.some((r) => r.errorCode !== undefined)) { + if (externalEventSyncResult.some((r) => r.errorCode !== undefined)) { throw new CoreError( "error.transport.errorWhileApplyingExternalEvents", - externalEventSyncResult.externalEventResults + externalEventSyncResult .filter((r) => r.errorCode !== undefined) .map((r) => r.errorCode) .join(" | ") @@ -127,36 +128,28 @@ export class SyncController extends TransportController { } if (this.datawalletEnabled && (await this.unpushedDatawalletModifications.exists())) { - await this.syncDatawallet().catch((e) => this.log.error(e)); + await this.syncDatawallet(changedItems).catch((e) => this.log.error(e)); } - - return externalEventSyncResult.changedItems; } - private async syncExternalEvents(): Promise<{ - externalEventResults: FinalizeSyncRunRequestExternalEventResult[]; - changedItems: ChangedItems; - }> { + private async syncExternalEvents(changedItems: ChangedItems): Promise { const syncRunWasStarted = await this.startExternalEventsSyncRun(); + if (!syncRunWasStarted) { - await this.syncDatawallet(); - return { - changedItems: new ChangedItems(), - externalEventResults: [] - }; + await this.syncDatawallet(changedItems); + return []; } await this.applyIncomingDatawalletModifications(); - const result = await this.applyIncomingExternalEvents(); - await this.finalizeExternalEventsSyncRun(result.externalEventResults); + const result = await this.applyIncomingExternalEvents(changedItems); + await this.finalizeExternalEventsSyncRun(result); return result; } @log() - private async syncDatawallet() { - if (!this.datawalletEnabled) { - return; - } + private async syncDatawallet(changedItems: ChangedItems): Promise { + if (!this.datawalletEnabled) return; + const identityDatawalletVersion = await this.getIdentityDatawalletVersion(); if (this.config.supportedDatawalletVersion < identityDatawalletVersion) { @@ -168,7 +161,9 @@ export class SyncController extends TransportController { this.log.trace("Synchronization of Datawallet events started..."); try { - await this.applyIncomingDatawalletModifications(); + const changedObjectIdentifiers = await this.applyIncomingDatawalletModifications(); + changedItems.addChangedObjectsIdentifiersDuringDatawalletSync(changedObjectIdentifiers); + await this.pushLocalDatawalletModifications(); await this.setLastCompletedDatawalletSyncTime(); @@ -254,13 +249,11 @@ export class SyncController extends TransportController { } } - private async applyIncomingDatawalletModifications() { + private async applyIncomingDatawalletModifications(): Promise { const getDatawalletModificationsResult = await this.client.getDatawalletModifications({ localIndex: await this.getLocalDatawalletModificationIndex() }); const encryptedIncomingModifications = await getDatawalletModificationsResult.value.collect(); - if (encryptedIncomingModifications.length === 0) { - return; - } + if (encryptedIncomingModifications.length === 0) return []; const incomingModifications = await this.decryptDatawalletModifications(encryptedIncomingModifications); @@ -278,6 +271,8 @@ export class SyncController extends TransportController { this.log.trace(`${incomingModifications.length} incoming modifications executed`, incomingModifications); await this.updateLocalDatawalletModificationIndex(encryptedIncomingModifications.sort(descending)[0].index); + + return datawalletModificationsProcessor.changedObjectIdentifiers; } private async promiseAllWithProgess(promises: Promise[], callback: (percentage: number) => void) { @@ -385,7 +380,7 @@ export class SyncController extends TransportController { return this.currentSyncRun !== undefined; } - private async applyIncomingExternalEvents() { + private async applyIncomingExternalEvents(changedItems: ChangedItems): Promise { const getExternalEventsResult = await this.client.getExternalEventsOfSyncRun(this.currentSyncRun!.id.toString()); if (getExternalEventsResult.isError) throw getExternalEventsResult.error; @@ -393,7 +388,6 @@ export class SyncController extends TransportController { const externalEvents = await getExternalEventsResult.value.collect(); const results: FinalizeSyncRunRequestExternalEventResult[] = []; - const changedItems = new ChangedItems(); for (const externalEvent of externalEvents) { try { @@ -416,10 +410,7 @@ export class SyncController extends TransportController { } } - return { - externalEventResults: results, - changedItems: changedItems - }; + return results; } private async finalizeExternalEventsSyncRun(externalEventResults: FinalizeSyncRunRequestExternalEventResult[]): Promise { @@ -514,7 +505,7 @@ function descending(modification1: BackboneDatawalletModification, modification2 class LocalSyncRun { public constructor( - public readonly promise: Promise, + public readonly promise: Promise, public readonly whatToSync: WhatToSync ) {} From 63e4261f2b79540d474b3ba12df57cac1c50f5f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20K=C3=B6nig?= Date: Thu, 28 Nov 2024 09:04:37 +0100 Subject: [PATCH 28/42] feat: re-publish event --- .../src/modules/accounts/AccountController.ts | 22 +++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/packages/transport/src/modules/accounts/AccountController.ts b/packages/transport/src/modules/accounts/AccountController.ts index 6dee85801..f16f56912 100644 --- a/packages/transport/src/modules/accounts/AccountController.ts +++ b/packages/transport/src/modules/accounts/AccountController.ts @@ -8,6 +8,7 @@ import { CoreCrypto } from "../../core/CoreCrypto"; import { DbCollectionName } from "../../core/DbCollectionName"; import { DependencyOverrides } from "../../core/DependencyOverrides"; import { TransportLoggerFactory } from "../../core/TransportLoggerFactory"; +import { IdentityDeletionProcessStatusChangedEvent } from "../../events/IdentityDeletionProcessStatusChangedEvent"; import { PasswordGenerator } from "../../util"; import { CertificateController } from "../certificates/CertificateController"; import { CertificateIssuer } from "../certificates/CertificateIssuer"; @@ -232,11 +233,28 @@ export class AccountController { public async syncDatawallet(force = false): Promise { if (!force && !this.autoSync) return; - await this.synchronization.sync("OnlyDatawallet"); + const changedItems = await this.synchronization.sync("OnlyDatawallet"); + await this.triggerEventsForChangedItems(changedItems); } public async syncEverything(): Promise { - return await this.synchronization.sync("Everything"); + const changedItems = await this.synchronization.sync("Everything"); + await this.triggerEventsForChangedItems(changedItems); + + return changedItems; + } + + private async triggerEventsForChangedItems(changedItems: ChangedItems) { + const changedIdentityDeletionProcessIds = changedItems.changedObjectIdentifiersDuringDatawalletSync.filter((x) => x.startsWith("IDP")); + for (const id in changedIdentityDeletionProcessIds) { + const changedIdentityDeletionProcesses = await this.identityDeletionProcess.getIdentityDeletionProcess(id); + if (!changedIdentityDeletionProcesses) { + this.log.error(`IdentityDeletionProcess with id ${id} not found for re-triggering event. Skipping.`); + continue; + } + + this.transport.eventBus.publish(new IdentityDeletionProcessStatusChangedEvent(this.identity.address.toString(), changedIdentityDeletionProcesses)); + } } public async getLastCompletedSyncTime(): Promise { From af3932d07c405e61fe954a856dc25aa6d2b19ac0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20K=C3=B6nig?= Date: Thu, 28 Nov 2024 10:54:42 +0100 Subject: [PATCH 29/42] refactor: update usage --- packages/app-runtime/src/AppConfig.ts | 2 +- .../DatawalletSynchronizedModule.test.ts | 3 +- ...DeletionProcessStatusChangedModule.test.ts | 72 +++++++++++++++++-- .../src/modules/accounts/AccountController.ts | 1 + 4 files changed, 71 insertions(+), 7 deletions(-) diff --git a/packages/app-runtime/src/AppConfig.ts b/packages/app-runtime/src/AppConfig.ts index b92b91798..79239d114 100644 --- a/packages/app-runtime/src/AppConfig.ts +++ b/packages/app-runtime/src/AppConfig.ts @@ -56,7 +56,7 @@ export function createAppConfig(...configs: AppConfigOverwrite[]): AppConfig { name: "datawalletSynchronized", displayName: "Datawallet Synchronized Module", location: "datawalletSynchronized", - enabled: true + enabled: false }, identityDeletionProcessStatusChanged: { name: "identityDeletionProcessStatusChanged", diff --git a/packages/app-runtime/test/modules/DatawalletSynchronizedModule.test.ts b/packages/app-runtime/test/modules/DatawalletSynchronizedModule.test.ts index 0dd34e5a4..bcc3284e3 100644 --- a/packages/app-runtime/test/modules/DatawalletSynchronizedModule.test.ts +++ b/packages/app-runtime/test/modules/DatawalletSynchronizedModule.test.ts @@ -3,7 +3,8 @@ import { IdentityDeletionProcessStatus } from "@nmshd/runtime"; import { AppRuntime, LocalAccountDeletionDateChangedEvent, LocalAccountSession } from "../../src"; import { TestUtil } from "../lib"; -describe("DatawalletSynchronized", function () { +// eslint-disable-next-line jest/no-disabled-tests +describe.skip("DatawalletSynchronized", function () { let runtimeDevice1: AppRuntime; let sessionDevice1: LocalAccountSession; diff --git a/packages/app-runtime/test/modules/IdentityDeletionProcessStatusChangedModule.test.ts b/packages/app-runtime/test/modules/IdentityDeletionProcessStatusChangedModule.test.ts index 79b4d0a7c..3af8e32df 100644 --- a/packages/app-runtime/test/modules/IdentityDeletionProcessStatusChangedModule.test.ts +++ b/packages/app-runtime/test/modules/IdentityDeletionProcessStatusChangedModule.test.ts @@ -1,17 +1,21 @@ import { CoreId } from "@nmshd/core-types"; import { IdentityDeletionProcessStatus } from "@nmshd/runtime"; -import { AppRuntime, LocalAccountSession } from "../../src"; +import { AppRuntime, LocalAccountDeletionDateChangedEvent, LocalAccountDTO, LocalAccountSession } from "../../src"; import { TestUtil } from "../lib"; describe("IdentityDeletionProcessStatusChanged", function () { let runtime: AppRuntime; + let localAccount: LocalAccountDTO; let session: LocalAccountSession; + let runtimeDevice2: AppRuntime | undefined; + let sessionDevice2: LocalAccountSession | undefined; + beforeAll(async function () { runtime = await TestUtil.createRuntime(); await runtime.start(); - const [localAccount] = await TestUtil.provideAccounts(runtime, 1); + [localAccount] = await TestUtil.provideAccounts(runtime, 1); session = await runtime.selectAccount(localAccount.id); }); @@ -21,11 +25,15 @@ describe("IdentityDeletionProcessStatusChanged", function () { return; } - let abortResult; if (activeIdentityDeletionProcess.value.status === IdentityDeletionProcessStatus.Approved) { - abortResult = await session.transportServices.identityDeletionProcesses.cancelIdentityDeletionProcess(); + const abortResult = await session.transportServices.identityDeletionProcesses.cancelIdentityDeletionProcess(); + if (abortResult.isError) throw abortResult.error; + + if (sessionDevice2 && runtimeDevice2 && sessionDevice2.account.deletionDate) { + await sessionDevice2.transportServices.account.syncDatawallet(); + await TestUtil.awaitEvent(runtimeDevice2, LocalAccountDeletionDateChangedEvent); + } } - if (abortResult?.isError) throw abortResult.error; }); afterAll(async function () { @@ -53,4 +61,58 @@ describe("IdentityDeletionProcessStatusChanged", function () { const account = await runtime.multiAccountController.getAccount(CoreId.from(session.account.id)); expect(account.deletionDate).toBeUndefined(); }); + + describe("multi device", function () { + let runtimeDevice2: AppRuntime; + let sessionDevice2: LocalAccountSession; + + beforeAll(async function () { + runtimeDevice2 = await TestUtil.createRuntime(); + await runtimeDevice2.start(); + + const createDeviceResult = await session.transportServices.devices.createDevice({ name: "test", isAdmin: true }); + const onboardingInfoResult = await session.transportServices.devices.getDeviceOnboardingInfo({ id: createDeviceResult.value.id, profileName: "Test" }); + const localAccountDevice2 = await runtimeDevice2.accountServices.onboardAccount(onboardingInfoResult.value); + sessionDevice2 = await runtimeDevice2.selectAccount(localAccountDevice2.id.toString()); + + await session.transportServices.account.syncDatawallet(); + await sessionDevice2.transportServices.account.syncDatawallet(); + }); + + afterAll(async function () { + await runtimeDevice2.stop(); + }); + + test("should set the deletionDate on the LocalAccount on a second device when an IdentityDeletionProcess is initiated", async function () { + const initiateDeletionResult = await session.transportServices.identityDeletionProcesses.initiateIdentityDeletionProcess(); + expect(sessionDevice2.account.deletionDate).toBeUndefined(); + + await sessionDevice2.transportServices.account.syncDatawallet(); + const event = await TestUtil.awaitEvent(runtimeDevice2, LocalAccountDeletionDateChangedEvent); + expect(event.data).toBe(initiateDeletionResult.value.gracePeriodEndsAt); + + expect(sessionDevice2.account.deletionDate).toBe(initiateDeletionResult.value.gracePeriodEndsAt); + + const account = await runtimeDevice2.multiAccountController.getAccount(CoreId.from(sessionDevice2.account.id)); + expect(account.deletionDate).toBe(initiateDeletionResult.value.gracePeriodEndsAt); + }); + + test("should unset the deletionDate on the LocalAccount on a second device when an IdentityDeletionProcess is cancelled", async function () { + await session.transportServices.identityDeletionProcesses.initiateIdentityDeletionProcess(); + await sessionDevice2.transportServices.account.syncDatawallet(); + await TestUtil.awaitEvent(runtimeDevice2, LocalAccountDeletionDateChangedEvent); + + await session.transportServices.identityDeletionProcesses.cancelIdentityDeletionProcess(); + expect(sessionDevice2.account.deletionDate).toBeDefined(); + + await sessionDevice2.transportServices.account.syncDatawallet(); + const event = await TestUtil.awaitEvent(runtimeDevice2, LocalAccountDeletionDateChangedEvent); + expect(event.data).toBeUndefined(); + + expect(sessionDevice2.account.deletionDate).toBeUndefined(); + + const account = await runtimeDevice2.multiAccountController.getAccount(CoreId.from(sessionDevice2.account.id)); + expect(account.deletionDate).toBeUndefined(); + }); + }); }); diff --git a/packages/transport/src/modules/accounts/AccountController.ts b/packages/transport/src/modules/accounts/AccountController.ts index f16f56912..b1a2d853f 100644 --- a/packages/transport/src/modules/accounts/AccountController.ts +++ b/packages/transport/src/modules/accounts/AccountController.ts @@ -253,6 +253,7 @@ export class AccountController { continue; } + // TODO: should we really trigger this event or would a IdentityDeletionProcessStatusChangedDuringDatawalletSyncEvent better? this.transport.eventBus.publish(new IdentityDeletionProcessStatusChangedEvent(this.identity.address.toString(), changedIdentityDeletionProcesses)); } } From c1452ecc6fb3654a51ecd84c73e4b8e240dcf682 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20K=C3=B6nig?= Date: Mon, 2 Dec 2024 11:14:26 +0100 Subject: [PATCH 30/42] chore: simplify method --- packages/transport/src/modules/sync/SyncController.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/packages/transport/src/modules/sync/SyncController.ts b/packages/transport/src/modules/sync/SyncController.ts index 70ef5cfc6..e44399b53 100644 --- a/packages/transport/src/modules/sync/SyncController.ts +++ b/packages/transport/src/modules/sync/SyncController.ts @@ -68,9 +68,6 @@ export class SyncController extends TransportController { private currentSync?: LocalSyncRun; private currentSyncRun?: BackboneSyncRun; - public async sync(whatToSync: "OnlyDatawallet"): Promise; - public async sync(whatToSync: "Everything"): Promise; - public async sync(whatToSync: WhatToSync): Promise; public async sync(whatToSync: WhatToSync = "Everything"): Promise { if (this.currentSync?.includes(whatToSync)) { return await this.currentSync.promise; From 63153cbd179d450b951cd4d0d102768145e25948 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20K=C3=B6nig?= Date: Mon, 2 Dec 2024 11:36:35 +0100 Subject: [PATCH 31/42] refactor: remove all occurences of the DatawalletSynchronizedModule --- packages/app-runtime/src/AppConfig.ts | 6 -- packages/app-runtime/src/AppRuntime.ts | 2 - .../DatawalletSynchronizedModule.ts | 59 ------------- .../src/modules/runtimeEvents/index.ts | 1 - .../DatawalletSynchronizedModule.test.ts | 84 ------------------- 5 files changed, 152 deletions(-) delete mode 100644 packages/app-runtime/src/modules/runtimeEvents/DatawalletSynchronizedModule.ts delete mode 100644 packages/app-runtime/test/modules/DatawalletSynchronizedModule.test.ts diff --git a/packages/app-runtime/src/AppConfig.ts b/packages/app-runtime/src/AppConfig.ts index 79239d114..2cd3b6e15 100644 --- a/packages/app-runtime/src/AppConfig.ts +++ b/packages/app-runtime/src/AppConfig.ts @@ -52,12 +52,6 @@ export function createAppConfig(...configs: AppConfigOverwrite[]): AppConfig { location: "onboardingChangeReceived", enabled: true }, - datawalletSynchronized: { - name: "datawalletSynchronized", - displayName: "Datawallet Synchronized Module", - location: "datawalletSynchronized", - enabled: false - }, identityDeletionProcessStatusChanged: { name: "identityDeletionProcessStatusChanged", displayName: "Identity Deletion Process Status Changed Module", diff --git a/packages/app-runtime/src/AppRuntime.ts b/packages/app-runtime/src/AppRuntime.ts index 6852d4327..4cc341735 100644 --- a/packages/app-runtime/src/AppRuntime.ts +++ b/packages/app-runtime/src/AppRuntime.ts @@ -15,7 +15,6 @@ import { AppLaunchModule, AppRuntimeModuleConfiguration, AppSyncModule, - DatawalletSynchronizedModule, IAppRuntimeModuleConstructor, IdentityDeletionProcessStatusChangedModule, MailReceivedModule, @@ -278,7 +277,6 @@ export class AppRuntime extends Runtime { pushNotification: PushNotificationModule, mailReceived: MailReceivedModule, onboardingChangeReceived: OnboardingChangeReceivedModule, - datawalletSynchronized: DatawalletSynchronizedModule, identityDeletionProcessStatusChanged: IdentityDeletionProcessStatusChangedModule, messageReceived: MessageReceivedModule, relationshipChanged: RelationshipChangedModule, diff --git a/packages/app-runtime/src/modules/runtimeEvents/DatawalletSynchronizedModule.ts b/packages/app-runtime/src/modules/runtimeEvents/DatawalletSynchronizedModule.ts deleted file mode 100644 index 45e8cb76d..000000000 --- a/packages/app-runtime/src/modules/runtimeEvents/DatawalletSynchronizedModule.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { CoreDate } from "@nmshd/core-types"; -import { DatawalletSynchronizedEvent, IdentityDeletionProcessStatus } from "@nmshd/runtime"; -import { AppRuntimeError } from "../../AppRuntimeError"; -import { LocalAccountDeletionDateChangedEvent } from "../../events"; -import { LocalAccountMapper } from "../../multiAccount"; -import { AppRuntimeModule, AppRuntimeModuleConfiguration } from "../AppRuntimeModule"; - -export interface DatawalletSynchronizedModuleConfig extends AppRuntimeModuleConfiguration {} - -export class DatawalletSynchronizedModuleError extends AppRuntimeError {} - -export class DatawalletSynchronizedModule extends AppRuntimeModule { - public async init(): Promise { - // Nothing to do here - } - - public start(): Promise | void { - this.subscribeToEvent(DatawalletSynchronizedEvent, this.handleDatawalletSynchronized.bind(this)); - } - - private async handleDatawalletSynchronized(event: DatawalletSynchronizedEvent) { - const services = await this.runtime.getServices(event.eventTargetAddress); - const identityDeletionProcessResult = await services.transportServices.identityDeletionProcesses.getIdentityDeletionProcesses(); - - if (identityDeletionProcessResult.isError) { - this.logger.error(identityDeletionProcessResult); - return; - } - - if (identityDeletionProcessResult.value.length === 0) return; - - const mostRecentIdentityDeletionProcess = identityDeletionProcessResult.value.at(-1)!; - let newDeletionDate; - switch (mostRecentIdentityDeletionProcess.status) { - case IdentityDeletionProcessStatus.Approved: - newDeletionDate = CoreDate.from(mostRecentIdentityDeletionProcess.gracePeriodEndsAt!); - break; - case IdentityDeletionProcessStatus.Cancelled: - case IdentityDeletionProcessStatus.Rejected: - case IdentityDeletionProcessStatus.WaitingForApproval: - newDeletionDate = undefined; - break; - } - - const account = await this.runtime.multiAccountController.getAccountByAddress(event.eventTargetAddress); - const previousDeletionDate = account.deletionDate; - - if (previousDeletionDate === newDeletionDate) return; - - await this.runtime.multiAccountController.updateLocalAccountDeletionDate(event.eventTargetAddress, newDeletionDate); - - const updatedAccount = await this.runtime.multiAccountController.getAccountByAddress(event.eventTargetAddress); - this.runtime.eventBus.publish(new LocalAccountDeletionDateChangedEvent(event.eventTargetAddress, LocalAccountMapper.toLocalAccountDTO(updatedAccount))); - } - - public override stop(): Promise | void { - this.unsubscribeFromAllEvents(); - } -} diff --git a/packages/app-runtime/src/modules/runtimeEvents/index.ts b/packages/app-runtime/src/modules/runtimeEvents/index.ts index e37a1aa45..7a3710bda 100644 --- a/packages/app-runtime/src/modules/runtimeEvents/index.ts +++ b/packages/app-runtime/src/modules/runtimeEvents/index.ts @@ -1,4 +1,3 @@ -export * from "./DatawalletSynchronizedModule"; export * from "./IdentityDeletionProcessStatusChangedModule"; export * from "./MessageReceivedModule"; export * from "./RelationshipChangedModule"; diff --git a/packages/app-runtime/test/modules/DatawalletSynchronizedModule.test.ts b/packages/app-runtime/test/modules/DatawalletSynchronizedModule.test.ts deleted file mode 100644 index 115c76376..000000000 --- a/packages/app-runtime/test/modules/DatawalletSynchronizedModule.test.ts +++ /dev/null @@ -1,84 +0,0 @@ -import { CoreId } from "@nmshd/core-types"; -import { IdentityDeletionProcessStatus } from "@nmshd/runtime"; -import { AppRuntime, LocalAccountDeletionDateChangedEvent, LocalAccountMapper, LocalAccountSession } from "../../src"; -import { TestUtil } from "../lib"; - -// eslint-disable-next-line jest/no-disabled-tests -describe.skip("DatawalletSynchronized", function () { - let runtimeDevice1: AppRuntime; - let sessionDevice1: LocalAccountSession; - - let runtimeDevice2: AppRuntime; - let sessionDevice2: LocalAccountSession; - - beforeAll(async function () { - runtimeDevice1 = await TestUtil.createRuntime(); - await runtimeDevice1.start(); - - const [localAccountDevice1] = await TestUtil.provideAccounts(runtimeDevice1, 1); - sessionDevice1 = await runtimeDevice1.selectAccount(localAccountDevice1.id); - - runtimeDevice2 = await TestUtil.createRuntime(); - await runtimeDevice2.start(); - - const createDeviceResult = await sessionDevice1.transportServices.devices.createDevice({ name: "test", isAdmin: true }); - const onboardingInfoResult = await sessionDevice1.transportServices.devices.getDeviceOnboardingInfo({ id: createDeviceResult.value.id, profileName: "Test" }); - const localAccountDevice2 = await runtimeDevice2.accountServices.onboardAccount(onboardingInfoResult.value); - sessionDevice2 = await runtimeDevice2.selectAccount(localAccountDevice2.id.toString()); - - await sessionDevice1.transportServices.account.syncDatawallet(); - await sessionDevice2.transportServices.account.syncDatawallet(); - }); - - afterEach(async () => { - const activeIdentityDeletionProcess = await sessionDevice1.transportServices.identityDeletionProcesses.getActiveIdentityDeletionProcess(); - if (!activeIdentityDeletionProcess.isSuccess) return; - - if (activeIdentityDeletionProcess.value.status === IdentityDeletionProcessStatus.Approved) { - const abortResult = await sessionDevice1.transportServices.identityDeletionProcesses.cancelIdentityDeletionProcess(); - if (abortResult.isError) throw abortResult.error; - - await sessionDevice2.transportServices.account.syncDatawallet(); - await TestUtil.awaitEvent(runtimeDevice2, LocalAccountDeletionDateChangedEvent); - } - }); - - afterAll(async function () { - await runtimeDevice1.stop(); - await runtimeDevice2.stop(); - }); - - test("should set the deletionDate on the LocalAccount on a second device when an IdentityDeletionProcess is initiated", async function () { - const initiateDeletionResult = await sessionDevice1.transportServices.identityDeletionProcesses.initiateIdentityDeletionProcess(); - expect(sessionDevice2.account.deletionDate).toBeUndefined(); - - await sessionDevice2.transportServices.account.syncDatawallet(); - const event = await TestUtil.awaitEvent(runtimeDevice2, LocalAccountDeletionDateChangedEvent); - const updatedAccount = await runtimeDevice2.multiAccountController.getAccountByAddress(sessionDevice2.account.address!); - - expect(event.data).toStrictEqual(LocalAccountMapper.toLocalAccountDTO(updatedAccount)); - expect(event.data.deletionDate).toBe(initiateDeletionResult.value.gracePeriodEndsAt); - - const account = await runtimeDevice2.multiAccountController.getAccount(CoreId.from(sessionDevice2.account.id)); - expect(account.deletionDate!.toString()).toBe(initiateDeletionResult.value.gracePeriodEndsAt); - }); - - test("should unset the deletionDate on the LocalAccount on a second device when an IdentityDeletionProcess is cancelled", async function () { - await sessionDevice1.transportServices.identityDeletionProcesses.initiateIdentityDeletionProcess(); - await sessionDevice2.transportServices.account.syncDatawallet(); - await TestUtil.awaitEvent(runtimeDevice2, LocalAccountDeletionDateChangedEvent); - - await sessionDevice1.transportServices.identityDeletionProcesses.cancelIdentityDeletionProcess(); - expect(sessionDevice2.account.deletionDate).toBeDefined(); - - await sessionDevice2.transportServices.account.syncDatawallet(); - const event = await TestUtil.awaitEvent(runtimeDevice2, LocalAccountDeletionDateChangedEvent); - const updatedAccount = await runtimeDevice2.multiAccountController.getAccountByAddress(sessionDevice2.account.address!); - - expect(event.data).toStrictEqual(LocalAccountMapper.toLocalAccountDTO(updatedAccount)); - expect(event.data.deletionDate).toBeUndefined(); - - const account = await runtimeDevice2.multiAccountController.getAccount(CoreId.from(sessionDevice2.account.id)); - expect(account.deletionDate).toBeUndefined(); - }); -}); From 96d267013bd44869cac88ac9f0ce08feec94ee43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20K=C3=B6nig?= Date: Mon, 2 Dec 2024 12:05:10 +0100 Subject: [PATCH 32/42] fix: publish event --- ...ntityDeletionProcessStatusChangedModule.ts | 22 +++++++++++++------ .../multiAccount/MultiAccountController.ts | 4 +++- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/packages/app-runtime/src/modules/runtimeEvents/IdentityDeletionProcessStatusChangedModule.ts b/packages/app-runtime/src/modules/runtimeEvents/IdentityDeletionProcessStatusChangedModule.ts index a8306b593..cc737a229 100644 --- a/packages/app-runtime/src/modules/runtimeEvents/IdentityDeletionProcessStatusChangedModule.ts +++ b/packages/app-runtime/src/modules/runtimeEvents/IdentityDeletionProcessStatusChangedModule.ts @@ -1,6 +1,8 @@ import { CoreDate } from "@nmshd/core-types"; import { IdentityDeletionProcessStatus, IdentityDeletionProcessStatusChangedEvent } from "@nmshd/runtime"; import { AppRuntimeError } from "../../AppRuntimeError"; +import { LocalAccountDeletionDateChangedEvent } from "../../events"; +import { LocalAccountMapper } from "../../multiAccount/data/LocalAccountMapper"; import { AppRuntimeModule, AppRuntimeModuleConfiguration } from "../AppRuntimeModule"; export interface IdentityDeletionProcessStatusChangedModuleConfig extends AppRuntimeModuleConfiguration {} @@ -21,16 +23,11 @@ export class IdentityDeletionProcessStatusChangedModule extends AppRuntimeModule switch (identityDeletionProcess.status) { case IdentityDeletionProcessStatus.Approved: - await this.runtime.multiAccountController.updateLocalAccountDeletionDate(event.eventTargetAddress, CoreDate.from(identityDeletionProcess.gracePeriodEndsAt!)); + await this.updateLocalAccountDeletionDate(event.eventTargetAddress, CoreDate.from(identityDeletionProcess.gracePeriodEndsAt!)); break; case IdentityDeletionProcessStatus.Cancelled: - const account = await this.runtime.multiAccountController.getAccountByAddress(event.eventTargetAddress); - const previousDeletionDate = account.deletionDate; - - if (!previousDeletionDate) break; - - await this.runtime.multiAccountController.updateLocalAccountDeletionDate(event.eventTargetAddress, undefined); + await this.updateLocalAccountDeletionDate(event.eventTargetAddress, undefined); break; default: @@ -38,6 +35,17 @@ export class IdentityDeletionProcessStatusChangedModule extends AppRuntimeModule } } + private async updateLocalAccountDeletionDate(eventTargetAddress: string, deletionDate: CoreDate | undefined) { + const account = await this.runtime.multiAccountController.getAccountByAddress(eventTargetAddress); + const previousDeletionDate = account.deletionDate; + + if (!deletionDate && !previousDeletionDate) return; + if (deletionDate && previousDeletionDate && deletionDate.equals(previousDeletionDate)) return; + + const localAccount = await this.runtime.multiAccountController.updateLocalAccountDeletionDate(eventTargetAddress, deletionDate); + this.runtime.eventBus.publish(new LocalAccountDeletionDateChangedEvent(eventTargetAddress, LocalAccountMapper.toLocalAccountDTO(localAccount))); + } + public override stop(): Promise | void { this.unsubscribeFromAllEvents(); } diff --git a/packages/app-runtime/src/multiAccount/MultiAccountController.ts b/packages/app-runtime/src/multiAccount/MultiAccountController.ts index 252590aa1..8026155a0 100644 --- a/packages/app-runtime/src/multiAccount/MultiAccountController.ts +++ b/packages/app-runtime/src/multiAccount/MultiAccountController.ts @@ -251,7 +251,7 @@ export class MultiAccountController { await this._localAccounts.update(oldAccount, renamedAccount); } - public async updateLocalAccountDeletionDate(address: string, deletionDate?: CoreDate): Promise { + public async updateLocalAccountDeletionDate(address: string, deletionDate?: CoreDate): Promise { const oldAccount = await this._localAccounts.findOne({ address }); if (!oldAccount) { @@ -265,6 +265,8 @@ export class MultiAccountController { const cachedAccount = this.sessionStorage.findSession(address)?.account; if (cachedAccount) cachedAccount.deletionDate = deletionDate?.toString(); + + return account; } public async updateLastAccessedAt(accountId: string): Promise { From 273fce583684f96d1c94480c3bf8f14205df428e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20K=C3=B6nig?= Date: Mon, 2 Dec 2024 12:05:22 +0100 Subject: [PATCH 33/42] fix: for..of instead of for..in --- packages/transport/src/modules/accounts/AccountController.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/transport/src/modules/accounts/AccountController.ts b/packages/transport/src/modules/accounts/AccountController.ts index b1a2d853f..5e2e67589 100644 --- a/packages/transport/src/modules/accounts/AccountController.ts +++ b/packages/transport/src/modules/accounts/AccountController.ts @@ -246,7 +246,7 @@ export class AccountController { private async triggerEventsForChangedItems(changedItems: ChangedItems) { const changedIdentityDeletionProcessIds = changedItems.changedObjectIdentifiersDuringDatawalletSync.filter((x) => x.startsWith("IDP")); - for (const id in changedIdentityDeletionProcessIds) { + for (const id of changedIdentityDeletionProcessIds) { const changedIdentityDeletionProcesses = await this.identityDeletionProcess.getIdentityDeletionProcess(id); if (!changedIdentityDeletionProcesses) { this.log.error(`IdentityDeletionProcess with id ${id} not found for re-triggering event. Skipping.`); From 9768fa5188ea6f10dcb1bcb9dcbef06186b4390a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20K=C3=B6nig?= Date: Mon, 2 Dec 2024 12:05:38 +0100 Subject: [PATCH 34/42] fix: properly await events --- ...DeletionProcessStatusChangedModule.test.ts | 48 +++++++++---------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/packages/app-runtime/test/modules/IdentityDeletionProcessStatusChangedModule.test.ts b/packages/app-runtime/test/modules/IdentityDeletionProcessStatusChangedModule.test.ts index 1f23557e9..8b26f0cd8 100644 --- a/packages/app-runtime/test/modules/IdentityDeletionProcessStatusChangedModule.test.ts +++ b/packages/app-runtime/test/modules/IdentityDeletionProcessStatusChangedModule.test.ts @@ -28,14 +28,16 @@ describe("IdentityDeletionProcessStatusChanged", function () { if (abortResult.isError) throw abortResult.error; if (sessionDevice2 && runtimeDevice2 && sessionDevice2.account.deletionDate) { + const promise = TestUtil.awaitEvent(runtimeDevice2, LocalAccountDeletionDateChangedEvent); await sessionDevice2.transportServices.account.syncDatawallet(); - await TestUtil.awaitEvent(runtimeDevice2, LocalAccountDeletionDateChangedEvent); + await promise; } } }); afterAll(async function () { await runtime.stop(); + await runtimeDevice2?.stop(); }); test("should set the deletionDate of the LocalAccount initiating an IdentityDeletionProcess", async function () { @@ -46,7 +48,7 @@ describe("IdentityDeletionProcessStatusChanged", function () { expect(session.account.deletionDate).toBe(initiateDeletionResult.value.gracePeriodEndsAt); const account = await runtime.multiAccountController.getAccount(CoreId.from(session.account.id)); - expect(account.deletionDate).toBe(initiateDeletionResult.value.gracePeriodEndsAt); + expect(account.deletionDate!.toString()).toBe(initiateDeletionResult.value.gracePeriodEndsAt!.toString()); }); test("should unset the deletionDate of the LocalAccount cancelling an IdentityDeletionProcess", async function () { @@ -61,9 +63,6 @@ describe("IdentityDeletionProcessStatusChanged", function () { }); describe("multi device", function () { - let runtimeDevice2: AppRuntime; - let sessionDevice2: LocalAccountSession; - beforeAll(async function () { runtimeDevice2 = await TestUtil.createRuntime(); await runtimeDevice2.start(); @@ -77,39 +76,40 @@ describe("IdentityDeletionProcessStatusChanged", function () { await sessionDevice2.transportServices.account.syncDatawallet(); }); - afterAll(async function () { - await runtimeDevice2.stop(); - }); - test("should set the deletionDate on the LocalAccount on a second device when an IdentityDeletionProcess is initiated", async function () { const initiateDeletionResult = await session.transportServices.identityDeletionProcesses.initiateIdentityDeletionProcess(); - expect(sessionDevice2.account.deletionDate).toBeUndefined(); + expect(sessionDevice2!.account.deletionDate).toBeUndefined(); - await sessionDevice2.transportServices.account.syncDatawallet(); - const event = await TestUtil.awaitEvent(runtimeDevice2, LocalAccountDeletionDateChangedEvent); - expect(event.data).toBe(initiateDeletionResult.value.gracePeriodEndsAt); + const eventPromise = TestUtil.awaitEvent(runtimeDevice2!, LocalAccountDeletionDateChangedEvent, 3000); + await sessionDevice2!.transportServices.account.syncDatawallet(); - expect(sessionDevice2.account.deletionDate).toBe(initiateDeletionResult.value.gracePeriodEndsAt); + const event = await eventPromise; + expect(event.data.deletionDate!.toString()).toBe(initiateDeletionResult.value.gracePeriodEndsAt); - const account = await runtimeDevice2.multiAccountController.getAccount(CoreId.from(sessionDevice2.account.id)); - expect(account.deletionDate).toBe(initiateDeletionResult.value.gracePeriodEndsAt); + expect(sessionDevice2!.account.deletionDate!.toString()).toStrictEqual(initiateDeletionResult.value.gracePeriodEndsAt); + + const account = await runtimeDevice2!.multiAccountController.getAccount(CoreId.from(sessionDevice2!.account.id)); + expect(account.deletionDate!.toString()).toBe(initiateDeletionResult.value.gracePeriodEndsAt!.toString()); }); test("should unset the deletionDate on the LocalAccount on a second device when an IdentityDeletionProcess is cancelled", async function () { await session.transportServices.identityDeletionProcesses.initiateIdentityDeletionProcess(); - await sessionDevice2.transportServices.account.syncDatawallet(); - await TestUtil.awaitEvent(runtimeDevice2, LocalAccountDeletionDateChangedEvent); + const promise = TestUtil.awaitEvent(runtimeDevice2!, LocalAccountDeletionDateChangedEvent); + await sessionDevice2!.transportServices.account.syncDatawallet(); + await promise; await session.transportServices.identityDeletionProcesses.cancelIdentityDeletionProcess(); - expect(sessionDevice2.account.deletionDate).toBeDefined(); + expect(sessionDevice2!.account.deletionDate).toBeDefined(); - await sessionDevice2.transportServices.account.syncDatawallet(); - const event = await TestUtil.awaitEvent(runtimeDevice2, LocalAccountDeletionDateChangedEvent); - expect(event.data).toBeUndefined(); + const eventPromise = TestUtil.awaitEvent(runtimeDevice2!, LocalAccountDeletionDateChangedEvent, 2000); + await sessionDevice2!.transportServices.account.syncDatawallet(); + + const event = await eventPromise; + expect(event.data.deletionDate).toBeUndefined(); - expect(sessionDevice2.account.deletionDate).toBeUndefined(); + expect(sessionDevice2!.account.deletionDate).toBeUndefined(); - const account = await runtimeDevice2.multiAccountController.getAccount(CoreId.from(sessionDevice2.account.id)); + const account = await runtimeDevice2!.multiAccountController.getAccount(CoreId.from(sessionDevice2!.account.id)); expect(account.deletionDate).toBeUndefined(); }); }); From 6134537e82df2b5d781cda3afb80c8be4d6aec0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20K=C3=B6nig?= Date: Mon, 2 Dec 2024 18:18:08 +0100 Subject: [PATCH 35/42] chore: remove unused testutils --- packages/app-runtime/test/lib/TestUtil.ts | 42 +---------------------- 1 file changed, 1 insertion(+), 41 deletions(-) diff --git a/packages/app-runtime/test/lib/TestUtil.ts b/packages/app-runtime/test/lib/TestUtil.ts index 2cebf2259..99ad11317 100644 --- a/packages/app-runtime/test/lib/TestUtil.ts +++ b/packages/app-runtime/test/lib/TestUtil.ts @@ -1,7 +1,7 @@ /* eslint-disable jest/no-standalone-expect */ import { ILoggerFactory } from "@js-soft/logging-abstractions"; import { SimpleLoggerFactory } from "@js-soft/simple-logger"; -import { EventBus, Result, sleep, SubscriptionTarget } from "@js-soft/ts-utils"; +import { EventBus, Result, sleep } from "@js-soft/ts-utils"; import { ArbitraryMessageContent, ArbitraryRelationshipCreationContent, ArbitraryRelationshipTemplateContent } from "@nmshd/content"; import { CoreDate } from "@nmshd/core-types"; import { @@ -81,46 +81,6 @@ export class TestUtil { TransportLoggerFactory.init(this.oldLogger); } - public static async awaitEvent( - runtime: AppRuntime, - subscriptionTarget: SubscriptionTarget, - timeout?: number, - assertionFunction?: (t: TEvent) => boolean - ): Promise { - const eventBus = runtime.eventBus; - let subscriptionId: number; - - const eventPromise = new Promise((resolve) => { - subscriptionId = eventBus.subscribe(subscriptionTarget, (event: TEvent) => { - if (assertionFunction && !assertionFunction(event)) return; - - resolve(event); - }); - }); - if (!timeout) { - return await eventPromise.finally(() => eventBus.unsubscribe(subscriptionId)); - } - - let timeoutId: NodeJS.Timeout; - const timeoutPromise = new Promise((_resolve, reject) => { - timeoutId = setTimeout( - () => reject(new Error(`timeout exceeded for waiting for event ${typeof subscriptionTarget === "string" ? subscriptionTarget : subscriptionTarget.name}`)), - timeout - ); - }); - - return await Promise.race([eventPromise, timeoutPromise]).finally(() => { - eventBus.unsubscribe(subscriptionId); - clearTimeout(timeoutId); - }); - } - - public static async expectEvent(runtime: AppRuntime, subscriptionTarget: SubscriptionTarget, timeoutInMS = 1000): Promise { - const eventInstance: T = await this.awaitEvent(runtime, subscriptionTarget, timeoutInMS); - expect(eventInstance, "Event received").toBeDefined(); - return eventInstance; - } - public static expectThrows(method: Function | Promise, errorMessageRegexp: RegExp | string): void { let error: Error | undefined; try { From 5900cfd03f9f01d2197aa43be2190d19ba14a53d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20K=C3=B6nig?= Date: Mon, 2 Dec 2024 18:18:42 +0100 Subject: [PATCH 36/42] chore: use proper eventbus --- ...DeletionProcessStatusChangedModule.test.ts | 22 +++++++++---------- 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/packages/app-runtime/test/modules/IdentityDeletionProcessStatusChangedModule.test.ts b/packages/app-runtime/test/modules/IdentityDeletionProcessStatusChangedModule.test.ts index 8b26f0cd8..7a36e3d11 100644 --- a/packages/app-runtime/test/modules/IdentityDeletionProcessStatusChangedModule.test.ts +++ b/packages/app-runtime/test/modules/IdentityDeletionProcessStatusChangedModule.test.ts @@ -1,13 +1,14 @@ import { CoreId } from "@nmshd/core-types"; import { IdentityDeletionProcessStatus } from "@nmshd/runtime"; import { AppRuntime, LocalAccountDeletionDateChangedEvent, LocalAccountDTO, LocalAccountSession } from "../../src"; -import { TestUtil } from "../lib"; +import { MockEventBus, TestUtil } from "../lib"; describe("IdentityDeletionProcessStatusChanged", function () { let runtime: AppRuntime; let localAccount: LocalAccountDTO; let session: LocalAccountSession; + const eventBus = new MockEventBus(); let runtimeDevice2: AppRuntime | undefined; let sessionDevice2: LocalAccountSession | undefined; @@ -28,9 +29,8 @@ describe("IdentityDeletionProcessStatusChanged", function () { if (abortResult.isError) throw abortResult.error; if (sessionDevice2 && runtimeDevice2 && sessionDevice2.account.deletionDate) { - const promise = TestUtil.awaitEvent(runtimeDevice2, LocalAccountDeletionDateChangedEvent); await sessionDevice2.transportServices.account.syncDatawallet(); - await promise; + await eventBus.waitForRunningEventHandlers(); } } }); @@ -38,6 +38,9 @@ describe("IdentityDeletionProcessStatusChanged", function () { afterAll(async function () { await runtime.stop(); await runtimeDevice2?.stop(); + + // this eventbus is not defenitely cleaned up with runtime2, so it needs to be closed manually + await eventBus.close(); }); test("should set the deletionDate of the LocalAccount initiating an IdentityDeletionProcess", async function () { @@ -64,7 +67,7 @@ describe("IdentityDeletionProcessStatusChanged", function () { describe("multi device", function () { beforeAll(async function () { - runtimeDevice2 = await TestUtil.createRuntime(); + runtimeDevice2 = await TestUtil.createRuntime(undefined, undefined, eventBus); await runtimeDevice2.start(); const createDeviceResult = await session.transportServices.devices.createDevice({ name: "test", isAdmin: true }); @@ -80,11 +83,9 @@ describe("IdentityDeletionProcessStatusChanged", function () { const initiateDeletionResult = await session.transportServices.identityDeletionProcesses.initiateIdentityDeletionProcess(); expect(sessionDevice2!.account.deletionDate).toBeUndefined(); - const eventPromise = TestUtil.awaitEvent(runtimeDevice2!, LocalAccountDeletionDateChangedEvent, 3000); await sessionDevice2!.transportServices.account.syncDatawallet(); - const event = await eventPromise; - expect(event.data.deletionDate!.toString()).toBe(initiateDeletionResult.value.gracePeriodEndsAt); + await expect(eventBus).toHavePublished(LocalAccountDeletionDateChangedEvent, (e) => e.data.deletionDate! === initiateDeletionResult.value.gracePeriodEndsAt!); expect(sessionDevice2!.account.deletionDate!.toString()).toStrictEqual(initiateDeletionResult.value.gracePeriodEndsAt); @@ -94,18 +95,15 @@ describe("IdentityDeletionProcessStatusChanged", function () { test("should unset the deletionDate on the LocalAccount on a second device when an IdentityDeletionProcess is cancelled", async function () { await session.transportServices.identityDeletionProcesses.initiateIdentityDeletionProcess(); - const promise = TestUtil.awaitEvent(runtimeDevice2!, LocalAccountDeletionDateChangedEvent); await sessionDevice2!.transportServices.account.syncDatawallet(); - await promise; + await expect(eventBus).toHavePublished(LocalAccountDeletionDateChangedEvent); await session.transportServices.identityDeletionProcesses.cancelIdentityDeletionProcess(); expect(sessionDevice2!.account.deletionDate).toBeDefined(); - const eventPromise = TestUtil.awaitEvent(runtimeDevice2!, LocalAccountDeletionDateChangedEvent, 2000); await sessionDevice2!.transportServices.account.syncDatawallet(); - const event = await eventPromise; - expect(event.data.deletionDate).toBeUndefined(); + await expect(eventBus).toHavePublished(LocalAccountDeletionDateChangedEvent, (e) => e.data.deletionDate === undefined); expect(sessionDevice2!.account.deletionDate).toBeUndefined(); From 3e179d11fb32ab261103d6a6a4b47c8c0fce0875 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20K=C3=B6nig?= Date: Tue, 3 Dec 2024 09:04:57 +0100 Subject: [PATCH 37/42] chore: use for loop --- .../src/modules/sync/DatawalletModificationsProcessor.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/transport/src/modules/sync/DatawalletModificationsProcessor.ts b/packages/transport/src/modules/sync/DatawalletModificationsProcessor.ts index 3e6d6bde0..cc78fcf09 100644 --- a/packages/transport/src/modules/sync/DatawalletModificationsProcessor.ts +++ b/packages/transport/src/modules/sync/DatawalletModificationsProcessor.ts @@ -144,7 +144,10 @@ export class DatawalletModificationsProcessor { this.ensureAllItemsAreCacheable(); const cacheChangesWithoutDeletes = this.cacheChanges.filter((c) => !this.deletedObjectIdentifiers.some((d) => c.objectIdentifier.equals(d))); - cacheChangesWithoutDeletes.map((c) => c.objectIdentifier.toString()).forEach((objectIdentifier) => this._changedObjectIdentifiers.add(objectIdentifier)); + + for (const objectIdentifier of cacheChangesWithoutDeletes.map((c) => c.objectIdentifier.toString())) { + this._changedObjectIdentifiers.add(objectIdentifier); + } const cacheChangesGroupedByCollection = this.groupCacheChangesByCollection(cacheChangesWithoutDeletes); From 6fa3e6a4ba43973387f5490ac3985efa6ac999fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20K=C3=B6nig?= Date: Tue, 3 Dec 2024 12:16:01 +0100 Subject: [PATCH 38/42] refactor: allow to announce that there are changes in IdentityDeletionProcesses without having a specific --- packages/runtime/src/events/EventProxy.ts | 5 ++++- .../transport/IdentityDeletionProcessStatusChangedEvent.ts | 4 ++-- .../src/events/IdentityDeletionProcessStatusChangedEvent.ts | 4 ++-- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/packages/runtime/src/events/EventProxy.ts b/packages/runtime/src/events/EventProxy.ts index 020e8ad17..a05977fa0 100644 --- a/packages/runtime/src/events/EventProxy.ts +++ b/packages/runtime/src/events/EventProxy.ts @@ -94,7 +94,10 @@ export class EventProxy { this.subscribeToSourceEvent(transport.IdentityDeletionProcessStatusChangedEvent, (event) => { this.targetEventBus.publish( - new IdentityDeletionProcessStatusChangedEvent(event.eventTargetAddress, IdentityDeletionProcessMapper.toIdentityDeletionProcessDTO(event.data)) + new IdentityDeletionProcessStatusChangedEvent( + event.eventTargetAddress, + event.data ? IdentityDeletionProcessMapper.toIdentityDeletionProcessDTO(event.data) : undefined + ) ); }); diff --git a/packages/runtime/src/events/transport/IdentityDeletionProcessStatusChangedEvent.ts b/packages/runtime/src/events/transport/IdentityDeletionProcessStatusChangedEvent.ts index ff15e9962..823731a72 100644 --- a/packages/runtime/src/events/transport/IdentityDeletionProcessStatusChangedEvent.ts +++ b/packages/runtime/src/events/transport/IdentityDeletionProcessStatusChangedEvent.ts @@ -1,10 +1,10 @@ import { IdentityDeletionProcessDTO } from "../../types/transport/IdentityDeletionProcessDTO"; import { DataEvent } from "../DataEvent"; -export class IdentityDeletionProcessStatusChangedEvent extends DataEvent { +export class IdentityDeletionProcessStatusChangedEvent extends DataEvent { public static readonly namespace = "transport.identityDeletionProcessStatusChanged"; - public constructor(eventTargetAddress: string, data: IdentityDeletionProcessDTO) { + public constructor(eventTargetAddress: string, data?: IdentityDeletionProcessDTO) { super(IdentityDeletionProcessStatusChangedEvent.namespace, eventTargetAddress, data); } } diff --git a/packages/transport/src/events/IdentityDeletionProcessStatusChangedEvent.ts b/packages/transport/src/events/IdentityDeletionProcessStatusChangedEvent.ts index 33f47f9bf..032f835c8 100644 --- a/packages/transport/src/events/IdentityDeletionProcessStatusChangedEvent.ts +++ b/packages/transport/src/events/IdentityDeletionProcessStatusChangedEvent.ts @@ -1,10 +1,10 @@ import { IdentityDeletionProcess } from "../modules"; import { TransportDataEvent } from "./TransportDataEvent"; -export class IdentityDeletionProcessStatusChangedEvent extends TransportDataEvent { +export class IdentityDeletionProcessStatusChangedEvent extends TransportDataEvent { public static readonly namespace = "transport.identityDeletionProcessStatusChanged"; - public constructor(eventTargetAddress: string, data: IdentityDeletionProcess) { + public constructor(eventTargetAddress: string, data?: IdentityDeletionProcess) { super(IdentityDeletionProcessStatusChangedEvent.namespace, eventTargetAddress, data); } } From 5ac587056f063de6c63ea8b6c39f16aa79a3d60b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20K=C3=B6nig?= Date: Tue, 3 Dec 2024 12:25:23 +0100 Subject: [PATCH 39/42] chore: simplify publishing --- .../src/modules/accounts/AccountController.ts | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/packages/transport/src/modules/accounts/AccountController.ts b/packages/transport/src/modules/accounts/AccountController.ts index d36ef4ef2..df78544ec 100644 --- a/packages/transport/src/modules/accounts/AccountController.ts +++ b/packages/transport/src/modules/accounts/AccountController.ts @@ -237,27 +237,19 @@ export class AccountController { if (!force && !this.autoSync) return; const changedItems = await this.synchronization.sync("OnlyDatawallet"); - await this.triggerEventsForChangedItems(changedItems); + this.triggerEventsForChangedItems(changedItems); } public async syncEverything(): Promise { const changedItems = await this.synchronization.sync("Everything"); - await this.triggerEventsForChangedItems(changedItems); + this.triggerEventsForChangedItems(changedItems); return changedItems; } - private async triggerEventsForChangedItems(changedItems: ChangedItems) { - const changedIdentityDeletionProcessIds = changedItems.changedObjectIdentifiersDuringDatawalletSync.filter((x) => x.startsWith("IDP")); - for (const id of changedIdentityDeletionProcessIds) { - const changedIdentityDeletionProcesses = await this.identityDeletionProcess.getIdentityDeletionProcess(id); - if (!changedIdentityDeletionProcesses) { - this.log.error(`IdentityDeletionProcess with id ${id} not found for re-triggering event. Skipping.`); - continue; - } - - // TODO: should we really trigger this event or would a IdentityDeletionProcessStatusChangedDuringDatawalletSyncEvent better? - this.transport.eventBus.publish(new IdentityDeletionProcessStatusChangedEvent(this.identity.address.toString(), changedIdentityDeletionProcesses)); + private triggerEventsForChangedItems(changedItems: ChangedItems) { + if (changedItems.changedObjectIdentifiersDuringDatawalletSync.some((x) => x.startsWith("IDP"))) { + this.transport.eventBus.publish(new IdentityDeletionProcessStatusChangedEvent(this.identity.address.toString())); } } From a05ef57bd94995c526d325ddcfe4fc0102bd20aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20K=C3=B6nig?= Date: Tue, 3 Dec 2024 12:31:38 +0100 Subject: [PATCH 40/42] chore: test wording --- packages/app-runtime/test/lib/TestUtil.ts | 2 +- ...DeletionProcessStatusChangedModule.test.ts | 48 +++++++++---------- .../test/runtime/AccountName.test.ts | 2 +- .../test/runtime/TranslationProvider.test.ts | 2 +- packages/consumption/test/core/TestUtil.ts | 2 +- .../modules/devices/DeviceOnboarding.test.ts | 4 +- 6 files changed, 29 insertions(+), 31 deletions(-) diff --git a/packages/app-runtime/test/lib/TestUtil.ts b/packages/app-runtime/test/lib/TestUtil.ts index 99ad11317..2190dbcf6 100644 --- a/packages/app-runtime/test/lib/TestUtil.ts +++ b/packages/app-runtime/test/lib/TestUtil.ts @@ -252,7 +252,7 @@ export class TestUtil { expiresAt: CoreDate.utc().add({ minutes: 5 }).toString(), filename: "Test.bin", mimetype: "application/json", - title: "Test", + title: "aFileName", content: fileContent }); return file.value; diff --git a/packages/app-runtime/test/modules/IdentityDeletionProcessStatusChangedModule.test.ts b/packages/app-runtime/test/modules/IdentityDeletionProcessStatusChangedModule.test.ts index 7a36e3d11..45dd906b8 100644 --- a/packages/app-runtime/test/modules/IdentityDeletionProcessStatusChangedModule.test.ts +++ b/packages/app-runtime/test/modules/IdentityDeletionProcessStatusChangedModule.test.ts @@ -9,8 +9,8 @@ describe("IdentityDeletionProcessStatusChanged", function () { let session: LocalAccountSession; const eventBus = new MockEventBus(); - let runtimeDevice2: AppRuntime | undefined; - let sessionDevice2: LocalAccountSession | undefined; + let runtime2: AppRuntime | undefined; + let session2: LocalAccountSession | undefined; beforeAll(async function () { runtime = await TestUtil.createRuntime(); @@ -28,8 +28,8 @@ describe("IdentityDeletionProcessStatusChanged", function () { const abortResult = await session.transportServices.identityDeletionProcesses.cancelIdentityDeletionProcess(); if (abortResult.isError) throw abortResult.error; - if (sessionDevice2 && runtimeDevice2 && sessionDevice2.account.deletionDate) { - await sessionDevice2.transportServices.account.syncDatawallet(); + if (session2 && runtime2 && session2.account.deletionDate) { + await session2.transportServices.account.syncDatawallet(); await eventBus.waitForRunningEventHandlers(); } } @@ -37,9 +37,7 @@ describe("IdentityDeletionProcessStatusChanged", function () { afterAll(async function () { await runtime.stop(); - await runtimeDevice2?.stop(); - - // this eventbus is not defenitely cleaned up with runtime2, so it needs to be closed manually + await runtime2?.stop(); await eventBus.close(); }); @@ -67,47 +65,47 @@ describe("IdentityDeletionProcessStatusChanged", function () { describe("multi device", function () { beforeAll(async function () { - runtimeDevice2 = await TestUtil.createRuntime(undefined, undefined, eventBus); - await runtimeDevice2.start(); + runtime2 = await TestUtil.createRuntime(undefined, undefined, eventBus); + await runtime2.start(); - const createDeviceResult = await session.transportServices.devices.createDevice({ name: "test", isAdmin: true }); - const onboardingInfoResult = await session.transportServices.devices.getDeviceOnboardingInfo({ id: createDeviceResult.value.id, profileName: "Test" }); - const localAccountDevice2 = await runtimeDevice2.accountServices.onboardAccount(onboardingInfoResult.value); - sessionDevice2 = await runtimeDevice2.selectAccount(localAccountDevice2.id.toString()); + const createDeviceResult = await session.transportServices.devices.createDevice({ name: "aName", isAdmin: true }); + const onboardingInfoResult = await session.transportServices.devices.getDeviceOnboardingInfo({ id: createDeviceResult.value.id, profileName: "aProfileName" }); + const localAccountDevice2 = await runtime2.accountServices.onboardAccount(onboardingInfoResult.value); + session2 = await runtime2.selectAccount(localAccountDevice2.id.toString()); await session.transportServices.account.syncDatawallet(); - await sessionDevice2.transportServices.account.syncDatawallet(); + await session2.transportServices.account.syncDatawallet(); }); - test("should set the deletionDate on the LocalAccount on a second device when an IdentityDeletionProcess is initiated", async function () { + test("should set the deletionDate of the LocalAccount on a second device when an IdentityDeletionProcess is initiated", async function () { const initiateDeletionResult = await session.transportServices.identityDeletionProcesses.initiateIdentityDeletionProcess(); - expect(sessionDevice2!.account.deletionDate).toBeUndefined(); + expect(session2!.account.deletionDate).toBeUndefined(); - await sessionDevice2!.transportServices.account.syncDatawallet(); + await session2!.transportServices.account.syncDatawallet(); await expect(eventBus).toHavePublished(LocalAccountDeletionDateChangedEvent, (e) => e.data.deletionDate! === initiateDeletionResult.value.gracePeriodEndsAt!); - expect(sessionDevice2!.account.deletionDate!.toString()).toStrictEqual(initiateDeletionResult.value.gracePeriodEndsAt); + expect(session2!.account.deletionDate!.toString()).toStrictEqual(initiateDeletionResult.value.gracePeriodEndsAt); - const account = await runtimeDevice2!.multiAccountController.getAccount(CoreId.from(sessionDevice2!.account.id)); + const account = await runtime2!.multiAccountController.getAccount(CoreId.from(session2!.account.id)); expect(account.deletionDate!.toString()).toBe(initiateDeletionResult.value.gracePeriodEndsAt!.toString()); }); - test("should unset the deletionDate on the LocalAccount on a second device when an IdentityDeletionProcess is cancelled", async function () { + test("should unset the deletionDate of the LocalAccount on a second device when an IdentityDeletionProcess is cancelled", async function () { await session.transportServices.identityDeletionProcesses.initiateIdentityDeletionProcess(); - await sessionDevice2!.transportServices.account.syncDatawallet(); + await session2!.transportServices.account.syncDatawallet(); await expect(eventBus).toHavePublished(LocalAccountDeletionDateChangedEvent); await session.transportServices.identityDeletionProcesses.cancelIdentityDeletionProcess(); - expect(sessionDevice2!.account.deletionDate).toBeDefined(); + expect(session2!.account.deletionDate).toBeDefined(); - await sessionDevice2!.transportServices.account.syncDatawallet(); + await session2!.transportServices.account.syncDatawallet(); await expect(eventBus).toHavePublished(LocalAccountDeletionDateChangedEvent, (e) => e.data.deletionDate === undefined); - expect(sessionDevice2!.account.deletionDate).toBeUndefined(); + expect(session2!.account.deletionDate).toBeUndefined(); - const account = await runtimeDevice2!.multiAccountController.getAccount(CoreId.from(sessionDevice2!.account.id)); + const account = await runtime2!.multiAccountController.getAccount(CoreId.from(session2!.account.id)); expect(account.deletionDate).toBeUndefined(); }); }); diff --git a/packages/app-runtime/test/runtime/AccountName.test.ts b/packages/app-runtime/test/runtime/AccountName.test.ts index 099ddbaf0..e6dd5fa14 100644 --- a/packages/app-runtime/test/runtime/AccountName.test.ts +++ b/packages/app-runtime/test/runtime/AccountName.test.ts @@ -18,7 +18,7 @@ describe("Test setting the account name", function () { }); test("should set the account name", async function () { - const accountName = "test"; + const accountName = "anAccountName"; expect(localAccount).toBeDefined(); diff --git a/packages/app-runtime/test/runtime/TranslationProvider.test.ts b/packages/app-runtime/test/runtime/TranslationProvider.test.ts index 73c45c95b..fb2c5fa70 100644 --- a/packages/app-runtime/test/runtime/TranslationProvider.test.ts +++ b/packages/app-runtime/test/runtime/TranslationProvider.test.ts @@ -37,7 +37,7 @@ describe("TranslationProvider", function () { }); test("should translate 'test' to the default message", async function () { - const translation = await runtime.translate("test"); + const translation = await runtime.translate("aKeyWithoutAvailableTranslation"); expect(translation).toBeInstanceOf(Result); expect(translation.isSuccess).toBe(true); expect(translation.value).toBe(noTranslationAvailable); diff --git a/packages/consumption/test/core/TestUtil.ts b/packages/consumption/test/core/TestUtil.ts index d5ef334f8..6d5c0ae54 100644 --- a/packages/consumption/test/core/TestUtil.ts +++ b/packages/consumption/test/core/TestUtil.ts @@ -465,7 +465,7 @@ export class TestUtil { public static async uploadFile(from: AccountController, fileContent: CoreBuffer): Promise { const params: ISendFileParameters = { buffer: fileContent, - title: "Test", + title: "aFileName", description: "Dies ist eine Beschreibung", filename: "Test.bin", filemodified: CoreDate.from("2019-09-30T00:00:00.000Z"), diff --git a/packages/transport/test/modules/devices/DeviceOnboarding.test.ts b/packages/transport/test/modules/devices/DeviceOnboarding.test.ts index 4f835151b..3457ff6f9 100644 --- a/packages/transport/test/modules/devices/DeviceOnboarding.test.ts +++ b/packages/transport/test/modules/devices/DeviceOnboarding.test.ts @@ -35,10 +35,10 @@ describe("Device Onboarding", function () { }); test("should create correct device", async function () { - newDevice = await device1Account.devices.sendDevice({ name: "Test", isAdmin: true }); + newDevice = await device1Account.devices.sendDevice({ name: "aDeviceName", isAdmin: true }); await device1Account.syncDatawallet(); expect(newDevice).toBeInstanceOf(Device); - expect(newDevice.name).toBe("Test"); + expect(newDevice.name).toBe("aDeviceName"); expect(newDevice.publicKey).toBeUndefined(); expect(newDevice.operatingSystem).toBeUndefined(); expect(newDevice.lastLoginAt).toBeUndefined(); From 5cc8abfbf329d88be63a36472437d77d4d0ebb01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20K=C3=B6nig?= Date: Tue, 3 Dec 2024 12:46:25 +0100 Subject: [PATCH 41/42] feat: update logic --- ...ntityDeletionProcessStatusChangedModule.ts | 15 ++- ...DeletionProcessStatusChangedModule.test.ts | 101 ++++++++++++------ 2 files changed, 84 insertions(+), 32 deletions(-) diff --git a/packages/app-runtime/src/modules/runtimeEvents/IdentityDeletionProcessStatusChangedModule.ts b/packages/app-runtime/src/modules/runtimeEvents/IdentityDeletionProcessStatusChangedModule.ts index cc737a229..be0319832 100644 --- a/packages/app-runtime/src/modules/runtimeEvents/IdentityDeletionProcessStatusChangedModule.ts +++ b/packages/app-runtime/src/modules/runtimeEvents/IdentityDeletionProcessStatusChangedModule.ts @@ -21,6 +21,17 @@ export class IdentityDeletionProcessStatusChangedModule extends AppRuntimeModule private async handleIdentityDeletionProcessStatusChanged(event: IdentityDeletionProcessStatusChangedEvent) { const identityDeletionProcess = event.data; + if (!identityDeletionProcess) { + const services = await this.runtime.getServices(event.eventTargetAddress); + const identityDeletionProcesses = await services.transportServices.identityDeletionProcesses.getIdentityDeletionProcesses(); + + const approvedIdentityDeletionProcess = identityDeletionProcesses.value.filter((idp) => idp.status === IdentityDeletionProcessStatus.Approved).at(0); + const deletionDate = approvedIdentityDeletionProcess?.gracePeriodEndsAt ? CoreDate.from(approvedIdentityDeletionProcess.gracePeriodEndsAt) : undefined; + + await this.updateLocalAccountDeletionDate(event.eventTargetAddress, deletionDate, true); + return; + } + switch (identityDeletionProcess.status) { case IdentityDeletionProcessStatus.Approved: await this.updateLocalAccountDeletionDate(event.eventTargetAddress, CoreDate.from(identityDeletionProcess.gracePeriodEndsAt!)); @@ -35,7 +46,7 @@ export class IdentityDeletionProcessStatusChangedModule extends AppRuntimeModule } } - private async updateLocalAccountDeletionDate(eventTargetAddress: string, deletionDate: CoreDate | undefined) { + private async updateLocalAccountDeletionDate(eventTargetAddress: string, deletionDate: CoreDate | undefined, publishEvent = false) { const account = await this.runtime.multiAccountController.getAccountByAddress(eventTargetAddress); const previousDeletionDate = account.deletionDate; @@ -43,6 +54,8 @@ export class IdentityDeletionProcessStatusChangedModule extends AppRuntimeModule if (deletionDate && previousDeletionDate && deletionDate.equals(previousDeletionDate)) return; const localAccount = await this.runtime.multiAccountController.updateLocalAccountDeletionDate(eventTargetAddress, deletionDate); + + if (!publishEvent) return; this.runtime.eventBus.publish(new LocalAccountDeletionDateChangedEvent(eventTargetAddress, LocalAccountMapper.toLocalAccountDTO(localAccount))); } diff --git a/packages/app-runtime/test/modules/IdentityDeletionProcessStatusChangedModule.test.ts b/packages/app-runtime/test/modules/IdentityDeletionProcessStatusChangedModule.test.ts index 45dd906b8..e11cd4457 100644 --- a/packages/app-runtime/test/modules/IdentityDeletionProcessStatusChangedModule.test.ts +++ b/packages/app-runtime/test/modules/IdentityDeletionProcessStatusChangedModule.test.ts @@ -4,86 +4,90 @@ import { AppRuntime, LocalAccountDeletionDateChangedEvent, LocalAccountDTO, Loca import { MockEventBus, TestUtil } from "../lib"; describe("IdentityDeletionProcessStatusChanged", function () { - let runtime: AppRuntime; + const eventBusRuntime1 = new MockEventBus(); + let runtime1: AppRuntime; let localAccount: LocalAccountDTO; - let session: LocalAccountSession; + let session1: LocalAccountSession; - const eventBus = new MockEventBus(); + const eventBusRuntime2 = new MockEventBus(); let runtime2: AppRuntime | undefined; let session2: LocalAccountSession | undefined; beforeAll(async function () { - runtime = await TestUtil.createRuntime(); - await runtime.start(); + runtime1 = await TestUtil.createRuntime(undefined, undefined, eventBusRuntime1); + await runtime1.start(); - [localAccount] = await TestUtil.provideAccounts(runtime, 1); - session = await runtime.selectAccount(localAccount.id); + [localAccount] = await TestUtil.provideAccounts(runtime1, 1); + session1 = await runtime1.selectAccount(localAccount.id); }); afterEach(async () => { - const activeIdentityDeletionProcess = await session.transportServices.identityDeletionProcesses.getActiveIdentityDeletionProcess(); + const activeIdentityDeletionProcess = await session1.transportServices.identityDeletionProcesses.getActiveIdentityDeletionProcess(); if (!activeIdentityDeletionProcess.isSuccess) return; if (activeIdentityDeletionProcess.value.status === IdentityDeletionProcessStatus.Approved) { - const abortResult = await session.transportServices.identityDeletionProcesses.cancelIdentityDeletionProcess(); + const abortResult = await session1.transportServices.identityDeletionProcesses.cancelIdentityDeletionProcess(); if (abortResult.isError) throw abortResult.error; if (session2 && runtime2 && session2.account.deletionDate) { await session2.transportServices.account.syncDatawallet(); - await eventBus.waitForRunningEventHandlers(); + await eventBusRuntime2.waitForRunningEventHandlers(); } } }); afterAll(async function () { - await runtime.stop(); + await runtime1.stop(); await runtime2?.stop(); - await eventBus.close(); + await eventBusRuntime2.close(); }); test("should set the deletionDate of the LocalAccount initiating an IdentityDeletionProcess", async function () { - expect(session.account.deletionDate).toBeUndefined(); + expect(session1.account.deletionDate).toBeUndefined(); - const initiateDeletionResult = await session.transportServices.identityDeletionProcesses.initiateIdentityDeletionProcess(); + const initiateDeletionResult = await session1.transportServices.identityDeletionProcesses.initiateIdentityDeletionProcess(); + await eventBusRuntime1.waitForRunningEventHandlers(); - expect(session.account.deletionDate).toBe(initiateDeletionResult.value.gracePeriodEndsAt); + expect(session1.account.deletionDate).toBe(initiateDeletionResult.value.gracePeriodEndsAt); - const account = await runtime.multiAccountController.getAccount(CoreId.from(session.account.id)); + const account = await runtime1.multiAccountController.getAccount(CoreId.from(session1.account.id)); expect(account.deletionDate!.toString()).toBe(initiateDeletionResult.value.gracePeriodEndsAt!.toString()); }); test("should unset the deletionDate of the LocalAccount cancelling an IdentityDeletionProcess", async function () { - await session.transportServices.identityDeletionProcesses.initiateIdentityDeletionProcess(); - expect(session.account.deletionDate).toBeDefined(); + await session1.transportServices.identityDeletionProcesses.initiateIdentityDeletionProcess(); + await eventBusRuntime1.waitForRunningEventHandlers(); + expect(session1.account.deletionDate).toBeDefined(); - await session.transportServices.identityDeletionProcesses.cancelIdentityDeletionProcess(); - expect(session.account.deletionDate).toBeUndefined(); + await session1.transportServices.identityDeletionProcesses.cancelIdentityDeletionProcess(); + await eventBusRuntime1.waitForRunningEventHandlers(); + expect(session1.account.deletionDate).toBeUndefined(); - const account = await runtime.multiAccountController.getAccount(CoreId.from(session.account.id)); + const account = await runtime1.multiAccountController.getAccount(CoreId.from(session1.account.id)); expect(account.deletionDate).toBeUndefined(); }); describe("multi device", function () { beforeAll(async function () { - runtime2 = await TestUtil.createRuntime(undefined, undefined, eventBus); + runtime2 = await TestUtil.createRuntime(undefined, undefined, eventBusRuntime2); await runtime2.start(); - const createDeviceResult = await session.transportServices.devices.createDevice({ name: "aName", isAdmin: true }); - const onboardingInfoResult = await session.transportServices.devices.getDeviceOnboardingInfo({ id: createDeviceResult.value.id, profileName: "aProfileName" }); + const createDeviceResult = await session1.transportServices.devices.createDevice({ name: "aName", isAdmin: true }); + const onboardingInfoResult = await session1.transportServices.devices.getDeviceOnboardingInfo({ id: createDeviceResult.value.id, profileName: "aProfileName" }); const localAccountDevice2 = await runtime2.accountServices.onboardAccount(onboardingInfoResult.value); session2 = await runtime2.selectAccount(localAccountDevice2.id.toString()); - await session.transportServices.account.syncDatawallet(); + await session1.transportServices.account.syncDatawallet(); await session2.transportServices.account.syncDatawallet(); }); test("should set the deletionDate of the LocalAccount on a second device when an IdentityDeletionProcess is initiated", async function () { - const initiateDeletionResult = await session.transportServices.identityDeletionProcesses.initiateIdentityDeletionProcess(); + const initiateDeletionResult = await session1.transportServices.identityDeletionProcesses.initiateIdentityDeletionProcess(); expect(session2!.account.deletionDate).toBeUndefined(); await session2!.transportServices.account.syncDatawallet(); - await expect(eventBus).toHavePublished(LocalAccountDeletionDateChangedEvent, (e) => e.data.deletionDate! === initiateDeletionResult.value.gracePeriodEndsAt!); + await expect(eventBusRuntime2).toHavePublished(LocalAccountDeletionDateChangedEvent, (e) => e.data.deletionDate! === initiateDeletionResult.value.gracePeriodEndsAt!); expect(session2!.account.deletionDate!.toString()).toStrictEqual(initiateDeletionResult.value.gracePeriodEndsAt); @@ -92,16 +96,51 @@ describe("IdentityDeletionProcessStatusChanged", function () { }); test("should unset the deletionDate of the LocalAccount on a second device when an IdentityDeletionProcess is cancelled", async function () { - await session.transportServices.identityDeletionProcesses.initiateIdentityDeletionProcess(); + await session1.transportServices.identityDeletionProcesses.initiateIdentityDeletionProcess(); await session2!.transportServices.account.syncDatawallet(); - await expect(eventBus).toHavePublished(LocalAccountDeletionDateChangedEvent); + await expect(eventBusRuntime2).toHavePublished(LocalAccountDeletionDateChangedEvent); - await session.transportServices.identityDeletionProcesses.cancelIdentityDeletionProcess(); + await session1.transportServices.identityDeletionProcesses.cancelIdentityDeletionProcess(); expect(session2!.account.deletionDate).toBeDefined(); await session2!.transportServices.account.syncDatawallet(); - await expect(eventBus).toHavePublished(LocalAccountDeletionDateChangedEvent, (e) => e.data.deletionDate === undefined); + await expect(eventBusRuntime2).toHavePublished(LocalAccountDeletionDateChangedEvent, (e) => e.data.deletionDate === undefined); + + expect(session2!.account.deletionDate).toBeUndefined(); + + const account = await runtime2!.multiAccountController.getAccount(CoreId.from(session2!.account.id)); + expect(account.deletionDate).toBeUndefined(); + }); + + test("should handle multiple synced IdentityDeletionProcesses that happend while not syncing with the last one approved", async function () { + await session1.transportServices.identityDeletionProcesses.initiateIdentityDeletionProcess(); + await session1.transportServices.identityDeletionProcesses.cancelIdentityDeletionProcess(); + await session1.transportServices.identityDeletionProcesses.initiateIdentityDeletionProcess(); + await session1.transportServices.identityDeletionProcesses.cancelIdentityDeletionProcess(); + + const initiateDeletionResult = await session1.transportServices.identityDeletionProcesses.initiateIdentityDeletionProcess(); + expect(session2!.account.deletionDate).toBeUndefined(); + + await session2!.transportServices.account.syncDatawallet(); + + await expect(eventBusRuntime2).toHavePublished(LocalAccountDeletionDateChangedEvent, (e) => e.data.deletionDate! === initiateDeletionResult.value.gracePeriodEndsAt!); + + expect(session2!.account.deletionDate!.toString()).toStrictEqual(initiateDeletionResult.value.gracePeriodEndsAt); + + const account = await runtime2!.multiAccountController.getAccount(CoreId.from(session2!.account.id)); + expect(account.deletionDate!.toString()).toBe(initiateDeletionResult.value.gracePeriodEndsAt!.toString()); + }); + + test("should handle multiple synced IdentityDeletionProcesses that happend while not syncing with the last one cancelled", async function () { + await session1.transportServices.identityDeletionProcesses.initiateIdentityDeletionProcess(); + await session1.transportServices.identityDeletionProcesses.cancelIdentityDeletionProcess(); + await session1.transportServices.identityDeletionProcesses.initiateIdentityDeletionProcess(); + await session1.transportServices.identityDeletionProcesses.cancelIdentityDeletionProcess(); + + await session2!.transportServices.account.syncDatawallet(); + + await expect(eventBusRuntime2).toHavePublished(LocalAccountDeletionDateChangedEvent, (e) => e.data.deletionDate === undefined); expect(session2!.account.deletionDate).toBeUndefined(); From 0b431071a3585bc6d3485ac6c3bda306ef2396f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20K=C3=B6nig?= Date: Tue, 3 Dec 2024 13:09:35 +0100 Subject: [PATCH 42/42] fix: tests --- .../transport/identityDeletionProcess.test.ts | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/packages/runtime/test/transport/identityDeletionProcess.test.ts b/packages/runtime/test/transport/identityDeletionProcess.test.ts index f40c0d773..0832aeb3a 100644 --- a/packages/runtime/test/transport/identityDeletionProcess.test.ts +++ b/packages/runtime/test/transport/identityDeletionProcess.test.ts @@ -53,8 +53,8 @@ describe("IdentityDeletionProcess", () => { const initiatedIdentityDeletionProcess = await startIdentityDeletionProcessFromBackboneAdminApi(transportService, accountAddress); expect(initiatedIdentityDeletionProcess.status).toStrictEqual(IdentityDeletionProcessStatus.WaitingForApproval); - await expect(eventBus).toHavePublished(IdentityDeletionProcessStatusChangedEvent, (e) => e.data.id === initiatedIdentityDeletionProcess.id); - await expect(eventBus).toHavePublished(IdentityDeletionProcessStatusChangedEvent, (e) => e.data.status === IdentityDeletionProcessStatus.WaitingForApproval); + await expect(eventBus).toHavePublished(IdentityDeletionProcessStatusChangedEvent, (e) => e.data!.id === initiatedIdentityDeletionProcess.id); + await expect(eventBus).toHavePublished(IdentityDeletionProcessStatusChangedEvent, (e) => e.data!.status === IdentityDeletionProcessStatus.WaitingForApproval); await transportService.identityDeletionProcesses.approveIdentityDeletionProcess(); eventBus.reset(); @@ -64,8 +64,8 @@ describe("IdentityDeletionProcess", () => { const identityDeletionProcess = result.value; expect(identityDeletionProcess.status).toStrictEqual(IdentityDeletionProcessStatus.Cancelled); - await expect(eventBus).toHavePublished(IdentityDeletionProcessStatusChangedEvent, (e) => e.data.id === initiatedIdentityDeletionProcess.id); - await expect(eventBus).toHavePublished(IdentityDeletionProcessStatusChangedEvent, (e) => e.data.status === IdentityDeletionProcessStatus.Cancelled); + await expect(eventBus).toHavePublished(IdentityDeletionProcessStatusChangedEvent, (e) => e.data!.id === initiatedIdentityDeletionProcess.id); + await expect(eventBus).toHavePublished(IdentityDeletionProcessStatusChangedEvent, (e) => e.data!.status === IdentityDeletionProcessStatus.Cancelled); }); describe(InitiateIdentityDeletionProcessUseCase.name, () => { @@ -76,8 +76,8 @@ describe("IdentityDeletionProcess", () => { const identityDeletionProcess = result.value; expect(identityDeletionProcess.status).toBe(IdentityDeletionProcessStatus.Approved); - await expect(eventBus).toHavePublished(IdentityDeletionProcessStatusChangedEvent, (e) => e.data.id === identityDeletionProcess.id); - await expect(eventBus).toHavePublished(IdentityDeletionProcessStatusChangedEvent, (e) => e.data.status === IdentityDeletionProcessStatus.Approved); + await expect(eventBus).toHavePublished(IdentityDeletionProcessStatusChangedEvent, (e) => e.data!.id === identityDeletionProcess.id); + await expect(eventBus).toHavePublished(IdentityDeletionProcessStatusChangedEvent, (e) => e.data!.status === IdentityDeletionProcessStatus.Approved); }); test("should return an error trying to initiate an IdentityDeletionProcess if there already is one approved", async function () { @@ -186,8 +186,8 @@ describe("IdentityDeletionProcess", () => { const cancelledIdentityDeletionProcess = result.value; expect(cancelledIdentityDeletionProcess.status).toBe(IdentityDeletionProcessStatus.Cancelled); - await expect(eventBus).toHavePublished(IdentityDeletionProcessStatusChangedEvent, (e) => e.data.id === cancelledIdentityDeletionProcess.id); - await expect(eventBus).toHavePublished(IdentityDeletionProcessStatusChangedEvent, (e) => e.data.status === IdentityDeletionProcessStatus.Cancelled); + await expect(eventBus).toHavePublished(IdentityDeletionProcessStatusChangedEvent, (e) => e.data!.id === cancelledIdentityDeletionProcess.id); + await expect(eventBus).toHavePublished(IdentityDeletionProcessStatusChangedEvent, (e) => e.data!.status === IdentityDeletionProcessStatus.Cancelled); }); test("should return an error trying to cancel an IdentityDeletionProcess if there is none active", async function () { @@ -205,8 +205,8 @@ describe("IdentityDeletionProcess", () => { const approvedIdentityDeletionProcess = result.value; expect(approvedIdentityDeletionProcess.status).toBe(IdentityDeletionProcessStatus.Approved); - await expect(eventBus).toHavePublished(IdentityDeletionProcessStatusChangedEvent, (e) => e.data.id === approvedIdentityDeletionProcess.id); - await expect(eventBus).toHavePublished(IdentityDeletionProcessStatusChangedEvent, (e) => e.data.status === IdentityDeletionProcessStatus.Approved); + await expect(eventBus).toHavePublished(IdentityDeletionProcessStatusChangedEvent, (e) => e.data!.id === approvedIdentityDeletionProcess.id); + await expect(eventBus).toHavePublished(IdentityDeletionProcessStatusChangedEvent, (e) => e.data!.status === IdentityDeletionProcessStatus.Approved); }); test("should return an error trying to approve an IdentityDeletionProcess if there is none active", async function () { @@ -227,8 +227,8 @@ describe("IdentityDeletionProcess", () => { const rejectedIdentityDeletionProcess = result.value; expect(rejectedIdentityDeletionProcess.status).toBe(IdentityDeletionProcessStatus.Rejected); - await expect(eventBus).toHavePublished(IdentityDeletionProcessStatusChangedEvent, (e) => e.data.id === rejectedIdentityDeletionProcess.id); - await expect(eventBus).toHavePublished(IdentityDeletionProcessStatusChangedEvent, (e) => e.data.status === IdentityDeletionProcessStatus.Rejected); + await expect(eventBus).toHavePublished(IdentityDeletionProcessStatusChangedEvent, (e) => e.data!.id === rejectedIdentityDeletionProcess.id); + await expect(eventBus).toHavePublished(IdentityDeletionProcessStatusChangedEvent, (e) => e.data!.status === IdentityDeletionProcessStatus.Rejected); }); test("should return an error trying to reject an IdentityDeletionProcess if there is none active", async function () {