diff --git a/package-lock.json b/package-lock.json index 95d4a6477..2ef9bf8bf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,7 +15,7 @@ "packages/app-runtime" ], "devDependencies": { - "@js-soft/eslint-config-ts": "^1.6.9", + "@js-soft/eslint-config-ts": "^1.6.10", "@js-soft/license-check": "^1.0.9", "@types/jest": "^29.5.13", "@types/node": "^22.7.4", @@ -1270,10 +1270,11 @@ "integrity": "sha512-VfuAWmGF3fJ/hrbvk+2CYh3p6kdqlcdUtHrOM6LK9q7lnZrVHmlnaE242fhGoUiAiKF0w5PWhUtd5/lggEb0EA==" }, "node_modules/@js-soft/eslint-config-ts": { - "version": "1.6.9", - "resolved": "https://registry.npmjs.org/@js-soft/eslint-config-ts/-/eslint-config-ts-1.6.9.tgz", - "integrity": "sha512-PRseBdHg9o/bIs1VHUrYkfo7sg6v1t8qqepM9kHQd86bBO5EfEgHWQE5CrAvG/R3KMN+yGQfbELrWYxzUPXlgQ==", + "version": "1.6.10", + "resolved": "https://registry.npmjs.org/@js-soft/eslint-config-ts/-/eslint-config-ts-1.6.10.tgz", + "integrity": "sha512-GkalyUbjUK9NMIX6J1+IIyFvKaITTLc2qNYleR27SVNetGtqWAYLG10JJC+m783p9y97IufbxxwcE7kdZhVIWw==", "dev": true, + "license": "MIT", "dependencies": { "@typescript-eslint/eslint-plugin": "^7.14.1", "@typescript-eslint/parser": "^7.14.1", diff --git a/package.json b/package.json index 00117cca8..9c465c951 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ "test:teardown": "docker compose -f .dev/compose.yml down -fsv" }, "devDependencies": { - "@js-soft/eslint-config-ts": "^1.6.9", + "@js-soft/eslint-config-ts": "^1.6.10", "@js-soft/license-check": "^1.0.9", "@types/jest": "^29.5.13", "@types/node": "^22.7.4", diff --git a/packages/runtime/src/dataViews/DataViewExpander.ts b/packages/runtime/src/dataViews/DataViewExpander.ts index ded498aae..417f5c2d5 100644 --- a/packages/runtime/src/dataViews/DataViewExpander.ts +++ b/packages/runtime/src/dataViews/DataViewExpander.ts @@ -1782,6 +1782,7 @@ export class DataViewExpander { type: "RelationshipDVO", status: relationship.status, peerDeletionStatus: relationship.peerDeletionInfo?.deletionStatus, + peerDeletionDate: relationship.peerDeletionInfo?.deletionDate, statusText: statusText, direction: direction, isPinned: relationshipSetting.isPinned, diff --git a/packages/runtime/src/dataViews/transport/RelationshipDVO.ts b/packages/runtime/src/dataViews/transport/RelationshipDVO.ts index a744bfc95..c17b018e8 100644 --- a/packages/runtime/src/dataViews/transport/RelationshipDVO.ts +++ b/packages/runtime/src/dataViews/transport/RelationshipDVO.ts @@ -11,6 +11,7 @@ export interface RelationshipDVO extends DataViewObject { type: "RelationshipDVO"; status: string; peerDeletionStatus?: PeerDeletionStatus; + peerDeletionDate?: string; direction: RelationshipDirection; statusText: string; isPinned: boolean; diff --git a/packages/runtime/src/types/transport/RelationshipDTO.ts b/packages/runtime/src/types/transport/RelationshipDTO.ts index 1c0c6611f..e17040b0d 100644 --- a/packages/runtime/src/types/transport/RelationshipDTO.ts +++ b/packages/runtime/src/types/transport/RelationshipDTO.ts @@ -44,6 +44,7 @@ export enum PeerDeletionStatus { export interface PeerDeletionInfoDTO { deletionStatus: PeerDeletionStatus; + deletionDate: string; } export interface RelationshipDTO { diff --git a/packages/runtime/test/transport/identityDeletionPeerProcessing.test.ts b/packages/runtime/test/transport/identityDeletionPeerProcessing.test.ts index 9e2784dcc..c91f6b8b3 100644 --- a/packages/runtime/test/transport/identityDeletionPeerProcessing.test.ts +++ b/packages/runtime/test/transport/identityDeletionPeerProcessing.test.ts @@ -14,15 +14,15 @@ beforeAll(async () => { relationshipId = (await establishRelationship(services1.transport, services2.transport)).id; }, 30000); +afterAll(async () => { + return await serviceProvider.stop(); +}); + beforeEach(() => { services1.eventBus.reset(); services2.eventBus.reset(); }); -afterAll(async () => { - return await serviceProvider.stop(); -}); - afterEach(async () => { const activeIdentityDeletionProcess = await services1.transport.identityDeletionProcesses.getActiveIdentityDeletionProcess(); if (!activeIdentityDeletionProcess.isSuccess) { @@ -34,6 +34,7 @@ afterEach(async () => { } else if (activeIdentityDeletionProcess.value.status === IdentityDeletionProcessStatus.WaitingForApproval) { abortResult = await services1.transport.identityDeletionProcesses.rejectIdentityDeletionProcess(); } + await syncUntilHasEvent(services2, PeerDeletionCancelledEvent, (e) => e.data.id === relationshipId); if (abortResult?.isError) throw abortResult.error; }); @@ -47,6 +48,7 @@ describe("IdentityDeletionProcess", () => { const updatedRelationship = (await services2.transport.relationships.getRelationship({ id: relationshipId })).value; expect(updatedRelationship.peerDeletionInfo!.deletionStatus).toBe(PeerDeletionStatus.ToBeDeleted); + expect(updatedRelationship.peerDeletionInfo!.deletionDate).toBeDefined(); }); test("peer should be notified about cancelled deletion process", async function () { diff --git a/packages/transport/src/modules/relationships/local/PeerDeletionInfo.ts b/packages/transport/src/modules/relationships/local/PeerDeletionInfo.ts index 0213c5861..ff22d4135 100644 --- a/packages/transport/src/modules/relationships/local/PeerDeletionInfo.ts +++ b/packages/transport/src/modules/relationships/local/PeerDeletionInfo.ts @@ -1,4 +1,5 @@ import { ISerializable, Serializable, serialize, validate } from "@js-soft/ts-serval"; +import { CoreDate } from "@nmshd/core-types"; export enum PeerDeletionStatus { ToBeDeleted = "ToBeDeleted", @@ -7,10 +8,12 @@ export enum PeerDeletionStatus { export interface PeerDeletionInfoJSON { deletionStatus: PeerDeletionStatus; + deletionDate: string; } export interface IPeerDeletionInfo extends ISerializable { deletionStatus: PeerDeletionStatus; + deletionDate: CoreDate; } export class PeerDeletionInfo extends Serializable implements IPeerDeletionInfo { @@ -20,6 +23,25 @@ export class PeerDeletionInfo extends Serializable implements IPeerDeletionInfo }) public deletionStatus: PeerDeletionStatus; + @serialize() + @validate() + public deletionDate: CoreDate; + + public static override preFrom(value: any): any { + if (!value.deletionDate) { + switch (value.deletionStatus as PeerDeletionStatus) { + case PeerDeletionStatus.ToBeDeleted: + value.deletionDate = CoreDate.local().add({ days: 14 }).toString(); + break; + case PeerDeletionStatus.Deleted: + value.deletionDate = CoreDate.local().toString(); + break; + } + } + + return value; + } + public static from(value: IPeerDeletionInfo | PeerDeletionInfoJSON): PeerDeletionInfo { return this.fromAny(value); } diff --git a/packages/transport/src/modules/sync/externalEventProcessors/PeerDeletedExternalEventProcessor.ts b/packages/transport/src/modules/sync/externalEventProcessors/PeerDeletedExternalEventProcessor.ts index 02d7399b1..459872852 100644 --- a/packages/transport/src/modules/sync/externalEventProcessors/PeerDeletedExternalEventProcessor.ts +++ b/packages/transport/src/modules/sync/externalEventProcessors/PeerDeletedExternalEventProcessor.ts @@ -1,5 +1,5 @@ import { Serializable, serialize, validate } from "@js-soft/ts-serval"; -import { CoreId } from "@nmshd/core-types"; +import { CoreDate, CoreId } from "@nmshd/core-types"; import { PeerDeletedEvent } from "../../../events"; import { PeerDeletionInfo, PeerDeletionStatus } from "../../relationships/local/PeerDeletionInfo"; import { Relationship } from "../../relationships/local/Relationship"; @@ -10,13 +10,17 @@ class PeerDeletedExternalEventData extends Serializable { @serialize() @validate() public relationshipId: string; + + @serialize() + @validate() + public deletionDate: CoreDate; } export class PeerDeletedExternalEventProcessor extends ExternalEventProcessor { public override async execute(externalEvent: BackboneExternalEvent): Promise { const payload = PeerDeletedExternalEventData.fromAny(externalEvent.payload); - const peerDeletionInfo = PeerDeletionInfo.from({ deletionStatus: PeerDeletionStatus.Deleted }); + const peerDeletionInfo = PeerDeletionInfo.from({ deletionStatus: PeerDeletionStatus.Deleted, deletionDate: payload.deletionDate }); const relationship = await this.accountController.relationships.setPeerDeletionInfo(CoreId.from(payload.relationshipId), peerDeletionInfo); this.eventBus.publish(new PeerDeletedEvent(this.ownAddress, relationship)); diff --git a/packages/transport/src/modules/sync/externalEventProcessors/PeerToBeDeletedExternalEventProcessor.ts b/packages/transport/src/modules/sync/externalEventProcessors/PeerToBeDeletedExternalEventProcessor.ts index 9d4bc9267..f91e9184a 100644 --- a/packages/transport/src/modules/sync/externalEventProcessors/PeerToBeDeletedExternalEventProcessor.ts +++ b/packages/transport/src/modules/sync/externalEventProcessors/PeerToBeDeletedExternalEventProcessor.ts @@ -1,5 +1,5 @@ import { Serializable, serialize, validate } from "@js-soft/ts-serval"; -import { CoreId } from "@nmshd/core-types"; +import { CoreDate, CoreId } from "@nmshd/core-types"; import { PeerToBeDeletedEvent } from "../../../events"; import { PeerDeletionInfo, PeerDeletionStatus } from "../../relationships/local/PeerDeletionInfo"; import { Relationship } from "../../relationships/local/Relationship"; @@ -10,13 +10,17 @@ class PeerToBeDeletedExternalEventData extends Serializable { @serialize() @validate() public relationshipId: string; + + @serialize() + @validate() + public deletionDate: CoreDate; } export class PeerToBeDeletedExternalEventProcessor extends ExternalEventProcessor { public override async execute(externalEvent: BackboneExternalEvent): Promise { const payload = PeerToBeDeletedExternalEventData.fromAny(externalEvent.payload); - const peerDeletionInfo = PeerDeletionInfo.from({ deletionStatus: PeerDeletionStatus.ToBeDeleted }); + const peerDeletionInfo = PeerDeletionInfo.from({ deletionStatus: PeerDeletionStatus.ToBeDeleted, deletionDate: payload.deletionDate }); const relationship = await this.accountController.relationships.setPeerDeletionInfo(CoreId.from(payload.relationshipId), peerDeletionInfo); this.eventBus.publish(new PeerToBeDeletedEvent(this.ownAddress, relationship)); diff --git a/packages/transport/test/modules/relationships/PeerDeletionInfo.test.ts b/packages/transport/test/modules/relationships/PeerDeletionInfo.test.ts new file mode 100644 index 000000000..f24fcf58a --- /dev/null +++ b/packages/transport/test/modules/relationships/PeerDeletionInfo.test.ts @@ -0,0 +1,20 @@ +import { CoreDate } from "@nmshd/core-types"; +import { PeerDeletionInfo, PeerDeletionStatus } from "../../../src"; + +describe("PeerDeletionInfo", () => { + test("PeerDeletionInfo in status ToBeDeleted should have a default deletionDate in the Future", () => { + const peerDeletionInfoToBeDeleted = PeerDeletionInfo.fromAny({ + deletionStatus: PeerDeletionStatus.ToBeDeleted + }); + + expect(peerDeletionInfoToBeDeleted.deletionDate.isAfter(CoreDate.local())).toBeTruthy(); + }); + + test("PeerDeletionInfo in status Deleted should have a default deletionDate now or in the past", () => { + const peerDeletionInfoDeleted = PeerDeletionInfo.fromAny({ + deletionStatus: PeerDeletionStatus.Deleted + }); + + expect(peerDeletionInfoDeleted.deletionDate.isSameOrBefore(CoreDate.local())).toBeTruthy(); + }); +}); diff --git a/packages/transport/test/modules/sync/externalEventProcessors/PeerDeletedExternalEventProcessor.test.ts b/packages/transport/test/modules/sync/externalEventProcessors/PeerDeletedExternalEventProcessor.test.ts index 280bbfdbd..4bc209eb4 100644 --- a/packages/transport/test/modules/sync/externalEventProcessors/PeerDeletedExternalEventProcessor.test.ts +++ b/packages/transport/test/modules/sync/externalEventProcessors/PeerDeletedExternalEventProcessor.test.ts @@ -1,5 +1,5 @@ import { IDatabaseConnection } from "@js-soft/docdb-access-abstractions"; -import { CoreId } from "@nmshd/core-types"; +import { CoreDate, CoreId } from "@nmshd/core-types"; import { AccountController, PeerDeletionStatus, Transport } from "../../../../src"; import { PeerDeletedExternalEventProcessor } from "../../../../src/modules/sync/externalEventProcessors/PeerDeletedExternalEventProcessor"; import { TestUtil } from "../../../testHelpers/TestUtil"; @@ -35,8 +35,17 @@ describe("PeerDeletedExternalEventProcessor", function () { test("PeerDeletedExternalEventProcessor should mark peer as deleted", async function () { const eventProcessor = new PeerDeletedExternalEventProcessor(recipient.identityDeletionProcess.eventBus, recipient); - await eventProcessor.execute({ id: "anId", createdAt: "aDate", index: 1, syncErrorCount: 0, type: "PeerDeleted", payload: { relationshipId: relationshipId.toString() } }); + const deletionDate = CoreDate.local(); + await eventProcessor.execute({ + id: "anId", + createdAt: "aDate", + index: 1, + syncErrorCount: 0, + type: "PeerDeleted", + payload: { relationshipId: relationshipId.toString(), deletionDate } + }); const relationship = await recipient.relationships.getRelationship(relationshipId); expect(relationship!.peerDeletionInfo!.deletionStatus).toBe(PeerDeletionStatus.Deleted); + expect(relationship!.peerDeletionInfo!.deletionDate.isSame(deletionDate)).toBeTruthy(); }); }); diff --git a/packages/transport/test/modules/sync/externalEventProcessors/PeerToBeDeletedExternalEventProcessor.test.ts b/packages/transport/test/modules/sync/externalEventProcessors/PeerToBeDeletedExternalEventProcessor.test.ts index 75d613417..0cb443a40 100644 --- a/packages/transport/test/modules/sync/externalEventProcessors/PeerToBeDeletedExternalEventProcessor.test.ts +++ b/packages/transport/test/modules/sync/externalEventProcessors/PeerToBeDeletedExternalEventProcessor.test.ts @@ -1,5 +1,5 @@ import { IDatabaseConnection } from "@js-soft/docdb-access-abstractions"; -import { CoreId } from "@nmshd/core-types"; +import { CoreDate, CoreId } from "@nmshd/core-types"; import { AccountController, PeerDeletionStatus, Transport } from "../../../../src"; import { PeerToBeDeletedExternalEventProcessor } from "../../../../src/modules/sync/externalEventProcessors/PeerToBeDeletedExternalEventProcessor"; import { TestUtil } from "../../../testHelpers/TestUtil"; @@ -35,15 +35,17 @@ describe("PeerToBeDeletedExternalEventProcessor", function () { test("PeerToBeDeletedExternalEventProcessor should mark peer as deleted", async function () { const eventProcessor = new PeerToBeDeletedExternalEventProcessor(recipient.identityDeletionProcess.eventBus, recipient); + const deletionDate = CoreDate.local().add({ days: 14 }); await eventProcessor.execute({ id: "anId", createdAt: "aDate", index: 1, syncErrorCount: 0, type: "PeerToBeDeleted", - payload: { relationshipId: relationshipId.toString() } + payload: { relationshipId: relationshipId.toString(), deletionDate } }); const relationship = await recipient.relationships.getRelationship(relationshipId); expect(relationship!.peerDeletionInfo!.deletionStatus).toBe(PeerDeletionStatus.ToBeDeleted); + expect(relationship!.peerDeletionInfo!.deletionDate.isSame(deletionDate)).toBeTruthy(); }); });