diff --git a/.dev/compose.backbone.env b/.dev/compose.backbone.env index b1bc18bf6..6a5adf787 100644 --- a/.dev/compose.backbone.env +++ b/.dev/compose.backbone.env @@ -1 +1 @@ -BACKBONE_VERSION=5.0.0 +BACKBONE_VERSION=6.0.0-alpha.4 diff --git a/.dev/compose.backbone.yml b/.dev/compose.backbone.yml index ea4ea6f49..10a49e131 100644 --- a/.dev/compose.backbone.yml +++ b/.dev/compose.backbone.yml @@ -32,6 +32,24 @@ services: - source: Config target: app/appsettings.override.json + admin-ui: + image: ghcr.io/nmshd/backbone-admin-ui:${BACKBONE_VERSION} + container_name: admin-ui + hostname: admin-ui + ports: + - "8091:8080" + depends_on: + database: + condition: service_started + rabbitmq: + condition: service_started + consumer-api: + condition: service_healthy + configs: + - source: Config + target: app/appsettings.override.json + profiles: [debug] + ### infrastructure ### database: diff --git a/packages/app-runtime/src/events/OnboardingChangeReceivedEvent.ts b/packages/app-runtime/src/events/OnboardingChangeReceivedEvent.ts index abc6ba1da..04cff56b2 100644 --- a/packages/app-runtime/src/events/OnboardingChangeReceivedEvent.ts +++ b/packages/app-runtime/src/events/OnboardingChangeReceivedEvent.ts @@ -1,16 +1,16 @@ -import { DataEvent, IdentityDVO, RelationshipChangeDTO, RelationshipDTO } from "@nmshd/runtime"; +import { DataEvent, IdentityDVO, RelationshipAuditLogEntryDTO, RelationshipDTO } from "@nmshd/runtime"; export class OnboardingChangeReceivedEvent extends DataEvent<{ - change: RelationshipChangeDTO; relationship: RelationshipDTO; + auditLogEntry: RelationshipAuditLogEntryDTO; identity: IdentityDVO; }> { public static readonly namespace: string = "app.onboardingChangeReceived"; - public constructor(address: string, change: RelationshipChangeDTO, relationship: RelationshipDTO, identity: IdentityDVO) { + public constructor(address: string, relationship: RelationshipDTO, auditLogEntry: RelationshipAuditLogEntryDTO, identity: IdentityDVO) { super(OnboardingChangeReceivedEvent.namespace, address, { - change, relationship, + auditLogEntry, identity }); } diff --git a/packages/app-runtime/src/extensibility/facades/AppRelationshipFacade.ts b/packages/app-runtime/src/extensibility/facades/AppRelationshipFacade.ts index fc0511833..d5393d0a8 100644 --- a/packages/app-runtime/src/extensibility/facades/AppRelationshipFacade.ts +++ b/packages/app-runtime/src/extensibility/facades/AppRelationshipFacade.ts @@ -1,12 +1,12 @@ import { - AcceptRelationshipChangeRequest, + AcceptRelationshipRequest, CreateRelationshipRequest, GetRelationshipByAddressRequest, GetRelationshipRequest, GetRelationshipsRequest, IdentityDVO, - RejectRelationshipChangeRequest, - RevokeRelationshipChangeRequest + RejectRelationshipRequest, + RevokeRelationshipRequest } from "@nmshd/runtime"; import { UserfriendlyApplicationError } from "../../UserfriendlyApplicationError"; import { UserfriendlyResult } from "../../UserfriendlyResult"; @@ -43,34 +43,24 @@ export class AppRelationshipFacade extends AppRuntimeFacade { return UserfriendlyResult.ok(dvo); } - public async acceptRelationshipCreationChange(relationshipId: string, content: any): Promise> { - const result = await this.transportServices.relationships.getRelationship({ id: relationshipId }); - if (result.isError) { - return await this.parseErrorResult(result); - } - - const changeId = result.value.changes[0].id; - return await this.acceptRelationshipChange({ relationshipId, changeId, content }); + public async createRelationship(request: CreateRelationshipRequest): Promise> { + const result = await this.transportServices.relationships.createRelationship(request); + return await this.handleResult(result, (v) => this.expander.expandRelationshipDTO(v)); } - public async rejectRelationshipCreationChange(relationshipId: string, content: any): Promise> { - const result = await this.transportServices.relationships.getRelationship({ id: relationshipId }); - if (result.isError) { - return await this.parseErrorResult(result); - } - - const changeId = result.value.changes[0].id; - return await this.rejectRelationshipChange({ relationshipId, changeId, content }); + public async acceptRelationship(request: AcceptRelationshipRequest): Promise> { + const result = await this.transportServices.relationships.acceptRelationship(request); + return await this.handleResult(result, (v) => this.expander.expandRelationshipDTO(v)); } - public async revokeRelationshipCreationChange(relationshipId: string, content: any): Promise> { - const result = await this.transportServices.relationships.getRelationship({ id: relationshipId }); - if (result.isError) { - return await this.parseErrorResult(result); - } + public async rejectRelationship(request: RejectRelationshipRequest): Promise> { + const result = await this.transportServices.relationships.rejectRelationship(request); + return await this.handleResult(result, (v) => this.expander.expandRelationshipDTO(v)); + } - const changeId = result.value.changes[0].id; - return await this.revokeRelationshipChange({ relationshipId, changeId, content }); + public async revokeRelationship(request: RevokeRelationshipRequest): Promise> { + const result = await this.transportServices.relationships.revokeRelationship(request); + return await this.handleResult(result, (v) => this.expander.expandRelationshipDTO(v)); } public async getRelationships(request: GetRelationshipsRequest): Promise> { @@ -87,24 +77,4 @@ export class AppRelationshipFacade extends AppRuntimeFacade { const result = await this.transportServices.relationships.getRelationshipByAddress(request); return await this.handleResult(result, (v) => this.expander.expandRelationshipDTO(v)); } - - public async createRelationship(request: CreateRelationshipRequest): Promise> { - const result = await this.transportServices.relationships.createRelationship(request); - return await this.handleResult(result, (v) => this.expander.expandRelationshipDTO(v)); - } - - public async acceptRelationshipChange(request: AcceptRelationshipChangeRequest): Promise> { - const result = await this.transportServices.relationships.acceptRelationshipChange(request); - return await this.handleResult(result, (v) => this.expander.expandRelationshipDTO(v)); - } - - public async rejectRelationshipChange(request: RejectRelationshipChangeRequest): Promise> { - const result = await this.transportServices.relationships.rejectRelationshipChange(request); - return await this.handleResult(result, (v) => this.expander.expandRelationshipDTO(v)); - } - - public async revokeRelationshipChange(request: RevokeRelationshipChangeRequest): Promise> { - const result = await this.transportServices.relationships.revokeRelationshipChange(request); - return await this.handleResult(result, (v) => this.expander.expandRelationshipDTO(v)); - } } diff --git a/packages/app-runtime/src/modules/appEvents/OnboardingChangeReceivedModule.ts b/packages/app-runtime/src/modules/appEvents/OnboardingChangeReceivedModule.ts index 3545ae476..7408b7c19 100644 --- a/packages/app-runtime/src/modules/appEvents/OnboardingChangeReceivedModule.ts +++ b/packages/app-runtime/src/modules/appEvents/OnboardingChangeReceivedModule.ts @@ -1,4 +1,4 @@ -import { RelationshipChangeStatus } from "@nmshd/runtime"; +import { RelationshipAuditLogEntryReason } from "@nmshd/runtime"; import { AppRuntimeError } from "../../AppRuntimeError"; import { OnboardingChangeReceivedEvent } from "../../events"; import { AppRuntimeModule, AppRuntimeModuleConfiguration } from "../AppRuntimeModule"; @@ -17,32 +17,35 @@ export class OnboardingChangeReceivedModule extends AppRuntimeModule { diff --git a/packages/app-runtime/src/modules/runtimeEvents/RelationshipChangedModule.ts b/packages/app-runtime/src/modules/runtimeEvents/RelationshipChangedModule.ts index b650b57ae..f366a5b5f 100644 --- a/packages/app-runtime/src/modules/runtimeEvents/RelationshipChangedModule.ts +++ b/packages/app-runtime/src/modules/runtimeEvents/RelationshipChangedModule.ts @@ -1,4 +1,4 @@ -import { RelationshipChangedEvent } from "@nmshd/runtime"; +import { RelationshipAuditLogEntryReason, RelationshipChangedEvent } from "@nmshd/runtime"; import { AppRuntimeError } from "../../AppRuntimeError"; import { OnboardingChangeReceivedEvent } from "../../events"; import { AppRuntimeModule, AppRuntimeModuleConfiguration } from "../AppRuntimeModule"; @@ -18,20 +18,28 @@ export class RelationshipChangedModule extends AppRuntimeModule { - const relRequest = await from.transportServices.relationships.createRelationship({ templateId, content }); + const relRequest = await from.transportServices.relationships.createRelationship({ templateId, creationContent: content }); return relRequest.value; } - public static async acceptRelationship( - session: LocalAccountSession, - relationshipId: string, - content: any = { - mycontent: "response" - } - ): Promise { - const relationship = ( - await session.transportServices.relationships.getRelationship({ - id: relationshipId - }) - ).value; - - const acceptedRelationship = ( - await session.transportServices.relationships.acceptRelationshipChange({ - changeId: relationship.changes[0].id, - content, - relationshipId - }) - ).value; + public static async acceptRelationship(session: LocalAccountSession, relationshipId: string): Promise { + const acceptedRelationship = (await session.transportServices.relationships.acceptRelationship({ relationshipId })).value; return acceptedRelationship; } - public static async rejectRelationship( - session: LocalAccountSession, - relationshipId: string, - content: any = { - mycontent: "rejection" - } - ): Promise { - const relationship = ( - await session.transportServices.relationships.getRelationship({ - id: relationshipId - }) - ).value; - - const rejectedRelationship = ( - await session.transportServices.relationships.rejectRelationshipChange({ - changeId: relationship.changes[0].id, - content, - relationshipId - }) - ).value; + public static async rejectRelationship(session: LocalAccountSession, relationshipId: string): Promise { + const rejectedRelationship = (await session.transportServices.relationships.rejectRelationship({ relationshipId })).value; return rejectedRelationship; } - public static async revokeRelationship( - session: LocalAccountSession, - relationshipId: string, - content: any = { - mycontent: "revokation" - } - ): Promise { - const relationship = ( - await session.transportServices.relationships.getRelationship({ - id: relationshipId - }) - ).value; - - const rejectedRelationship = ( - await session.transportServices.relationships.revokeRelationshipChange({ - changeId: relationship.changes[0].id, - content, - relationshipId - }) - ).value; + public static async revokeRelationship(session: LocalAccountSession, relationshipId: string): Promise { + const rejectedRelationship = (await session.transportServices.relationships.revokeRelationship({ relationshipId })).value; return rejectedRelationship; } diff --git a/packages/app-runtime/test/modules/RelationshipEventingAccept.test.ts b/packages/app-runtime/test/modules/RelationshipEventingAccept.test.ts index e93c52342..2435794e0 100644 --- a/packages/app-runtime/test/modules/RelationshipEventingAccept.test.ts +++ b/packages/app-runtime/test/modules/RelationshipEventingAccept.test.ts @@ -1,4 +1,4 @@ -import { RelationshipChangedEvent, RelationshipChangeStatus, RelationshipStatus } from "@nmshd/runtime"; +import { RelationshipChangedEvent, RelationshipStatus } from "@nmshd/runtime"; import { AppRuntime, LocalAccountSession, OnboardingChangeReceivedEvent } from "../../src"; import { EventListener, TestUtil } from "../lib"; @@ -42,8 +42,7 @@ describe("RelationshipEventingAcceptTest", function () { const onboardingChangeReceivedEvent = events[1].instance as OnboardingChangeReceivedEvent; expect(onboardingChangeReceivedEvent).toBeInstanceOf(OnboardingChangeReceivedEvent); expect(onboardingChangeReceivedEvent.data).toBeDefined(); - expect(onboardingChangeReceivedEvent.data.change.status).toBe(RelationshipChangeStatus.Pending); - expect(onboardingChangeReceivedEvent.data.change).toBe(relationshipChangedEvent.data.changes[0]); + expect(onboardingChangeReceivedEvent.data.auditLogEntry.newStatus).toBe(RelationshipStatus.Pending); expect(onboardingChangeReceivedEvent.data.identity).toBeDefined(); expect(onboardingChangeReceivedEvent.data.identity.id).toBe(sessionB.address.toString()); expect(onboardingChangeReceivedEvent.data.identity.name).toBe(sessionB.address.toString().substring(3, 9)); @@ -85,8 +84,7 @@ describe("RelationshipEventingAcceptTest", function () { const onboardingChangeReceivedEvent = events[1].instance as OnboardingChangeReceivedEvent; expect(onboardingChangeReceivedEvent).toBeInstanceOf(OnboardingChangeReceivedEvent); expect(onboardingChangeReceivedEvent.data).toBeDefined(); - expect(onboardingChangeReceivedEvent.data.change.status).toBe(RelationshipChangeStatus.Accepted); - expect(onboardingChangeReceivedEvent.data.change).toBe(relationshipChangedEvent.data.changes[0]); + expect(onboardingChangeReceivedEvent.data.auditLogEntry.newStatus).toBe(RelationshipStatus.Active); expect(onboardingChangeReceivedEvent.data.identity).toBeDefined(); expect(onboardingChangeReceivedEvent.data.identity.name).toBe(sessionA.accountController.identity.address.toString().substring(3, 9)); expect(onboardingChangeReceivedEvent.data.identity.id).toBe(sessionA.accountController.identity.address.toString()); diff --git a/packages/app-runtime/test/modules/RelationshipEventingReject.test.ts b/packages/app-runtime/test/modules/RelationshipEventingReject.test.ts index 152177267..d3b7ecc22 100644 --- a/packages/app-runtime/test/modules/RelationshipEventingReject.test.ts +++ b/packages/app-runtime/test/modules/RelationshipEventingReject.test.ts @@ -1,4 +1,4 @@ -import { RelationshipChangedEvent, RelationshipChangeStatus, RelationshipStatus } from "@nmshd/runtime"; +import { RelationshipChangedEvent, RelationshipStatus } from "@nmshd/runtime"; import { AppRuntime, LocalAccountSession, OnboardingChangeReceivedEvent } from "../../src"; import { EventListener, TestUtil } from "../lib"; @@ -42,8 +42,7 @@ describe("RelationshipEventingRejectTest", function () { const onboardingChangeReceivedEvent = events[1].instance as OnboardingChangeReceivedEvent; expect(onboardingChangeReceivedEvent).toBeInstanceOf(OnboardingChangeReceivedEvent); expect(onboardingChangeReceivedEvent.data).toBeDefined(); - expect(onboardingChangeReceivedEvent.data.change.status).toBe(RelationshipChangeStatus.Pending); - expect(onboardingChangeReceivedEvent.data.change).toBe(relationshipChangedEvent.data.changes[0]); + expect(onboardingChangeReceivedEvent.data.auditLogEntry.newStatus).toBe(RelationshipStatus.Pending); expect(onboardingChangeReceivedEvent.data.identity).toBeDefined(); expect(onboardingChangeReceivedEvent.data.identity.name).toBe(sessionB.accountController.identity.address.toString().substring(3, 9)); expect(onboardingChangeReceivedEvent.data.identity.id).toBe(sessionB.accountController.identity.address.toString()); @@ -84,8 +83,7 @@ describe("RelationshipEventingRejectTest", function () { const onboardingChangeReceivedEvent = events[1].instance as OnboardingChangeReceivedEvent; expect(onboardingChangeReceivedEvent).toBeInstanceOf(OnboardingChangeReceivedEvent); expect(onboardingChangeReceivedEvent.data).toBeDefined(); - expect(onboardingChangeReceivedEvent.data.change.status).toBe(RelationshipChangeStatus.Rejected); - expect(onboardingChangeReceivedEvent.data.change).toBe(relationshipChangedEvent.data.changes[0]); + expect(onboardingChangeReceivedEvent.data.auditLogEntry.newStatus).toBe(RelationshipStatus.Rejected); expect(onboardingChangeReceivedEvent.data.identity).toBeDefined(); expect(onboardingChangeReceivedEvent.data.identity.name).toBe(sessionA.accountController.identity.address.toString().substring(3, 9)); diff --git a/packages/app-runtime/test/modules/RelationshipEventingRevoke.test.ts b/packages/app-runtime/test/modules/RelationshipEventingRevoke.test.ts index fd2a0822a..92801ecb1 100644 --- a/packages/app-runtime/test/modules/RelationshipEventingRevoke.test.ts +++ b/packages/app-runtime/test/modules/RelationshipEventingRevoke.test.ts @@ -1,4 +1,4 @@ -import { RelationshipChangedEvent, RelationshipChangeStatus, RelationshipStatus } from "@nmshd/runtime"; +import { RelationshipChangedEvent, RelationshipStatus } from "@nmshd/runtime"; import { AppRuntime, LocalAccountSession, OnboardingChangeReceivedEvent } from "../../src"; import { EventListener, TestUtil } from "../lib"; @@ -43,8 +43,7 @@ describe("RelationshipEventingRevokeTest", function () { const onboardingChangeReceivedEvent = events[1].instance as OnboardingChangeReceivedEvent; expect(onboardingChangeReceivedEvent).toBeInstanceOf(OnboardingChangeReceivedEvent); expect(onboardingChangeReceivedEvent.data).toBeDefined(); - expect(onboardingChangeReceivedEvent.data.change.status).toBe(RelationshipChangeStatus.Pending); - expect(onboardingChangeReceivedEvent.data.change).toBe(relationshipChangedEvent.data.changes[0]); + expect(onboardingChangeReceivedEvent.data.auditLogEntry.newStatus).toBe(RelationshipStatus.Pending); expect(onboardingChangeReceivedEvent.data.identity).toBeDefined(); expect(onboardingChangeReceivedEvent.data.identity.name).toBe(sessionB.accountController.identity.address.toString().substring(3, 9)); @@ -86,8 +85,7 @@ describe("RelationshipEventingRevokeTest", function () { const onboardingChangeReceivedEvent = events[1].instance as OnboardingChangeReceivedEvent; expect(onboardingChangeReceivedEvent).toBeInstanceOf(OnboardingChangeReceivedEvent); expect(onboardingChangeReceivedEvent.data).toBeDefined(); - expect(onboardingChangeReceivedEvent.data.change.status).toBe(RelationshipChangeStatus.Revoked); - expect(onboardingChangeReceivedEvent.data.change).toBe(relationshipChangedEvent.data.changes[0]); + expect(onboardingChangeReceivedEvent.data.auditLogEntry.newStatus).toBe(RelationshipStatus.Revoked); expect(onboardingChangeReceivedEvent.data.identity).toBeDefined(); expect(onboardingChangeReceivedEvent.data.identity.name).toBe(sessionB.accountController.identity.address.toString().substring(3, 9)); diff --git a/packages/consumption/src/modules/requests/incoming/IncomingRequestsController.ts b/packages/consumption/src/modules/requests/incoming/IncomingRequestsController.ts index 5da529aff..6280af606 100644 --- a/packages/consumption/src/modules/requests/incoming/IncomingRequestsController.ts +++ b/packages/consumption/src/modules/requests/incoming/IncomingRequestsController.ts @@ -14,13 +14,13 @@ import { RequestItemProcessorRegistry } from "../itemProcessors/RequestItemProce import { ILocalRequestSource, LocalRequest } from "../local/LocalRequest"; import { LocalRequestStatus } from "../local/LocalRequestStatus"; import { LocalResponse, LocalResponseSource } from "../local/LocalResponse"; -import { DecideRequestParametersValidator } from "./DecideRequestParametersValidator"; import { CheckPrerequisitesOfIncomingRequestParameters, ICheckPrerequisitesOfIncomingRequestParameters } from "./checkPrerequisites/CheckPrerequisitesOfIncomingRequestParameters"; import { CompleteIncomingRequestParameters, ICompleteIncomingRequestParameters } from "./complete/CompleteIncomingRequestParameters"; import { DecideRequestItemGroupParametersJSON } from "./decide/DecideRequestItemGroupParameters"; import { DecideRequestItemParametersJSON } from "./decide/DecideRequestItemParameters"; import { DecideRequestParametersJSON } from "./decide/DecideRequestParameters"; import { InternalDecideRequestParameters, InternalDecideRequestParametersJSON } from "./decide/InternalDecideRequestParameters"; +import { DecideRequestParametersValidator } from "./DecideRequestParametersValidator"; import { IReceivedIncomingRequestParameters, ReceivedIncomingRequestParameters } from "./received/ReceivedIncomingRequestParameters"; import { IRequireManualDecisionOfIncomingRequestParameters, @@ -339,7 +339,7 @@ export class IncomingRequestsController extends ConsumptionBaseController { if (parsedParams.responseSourceObject) { request.response!.source = LocalResponseSource.from({ - type: parsedParams.responseSourceObject instanceof Message ? "Message" : "RelationshipChange", + type: parsedParams.responseSourceObject instanceof Message ? "Message" : "Relationship", reference: parsedParams.responseSourceObject.id }); } else if (!requestIsRejected || !requestIsFromTemplate) { diff --git a/packages/consumption/src/modules/requests/incoming/complete/CompleteIncomingRequestParameters.ts b/packages/consumption/src/modules/requests/incoming/complete/CompleteIncomingRequestParameters.ts index 56da95c66..c71cfdc72 100644 --- a/packages/consumption/src/modules/requests/incoming/complete/CompleteIncomingRequestParameters.ts +++ b/packages/consumption/src/modules/requests/incoming/complete/CompleteIncomingRequestParameters.ts @@ -1,9 +1,9 @@ import { ISerializable, Serializable, serialize, validate } from "@js-soft/ts-serval"; -import { CoreId, ICoreId, IMessage, IRelationshipChange, Message, RelationshipChange } from "@nmshd/transport"; +import { CoreId, ICoreId, IMessage, IRelationship, Message, Relationship } from "@nmshd/transport"; export interface ICompleteIncomingRequestParameters extends ISerializable { requestId: ICoreId; - responseSourceObject?: IMessage | IRelationshipChange; + responseSourceObject?: IMessage | IRelationship; } export class CompleteIncomingRequestParameters extends Serializable implements ICompleteIncomingRequestParameters { @@ -11,9 +11,9 @@ export class CompleteIncomingRequestParameters extends Serializable implements I @validate() public requestId: CoreId; - @serialize({ unionTypes: [Message, RelationshipChange] }) + @serialize({ unionTypes: [Message, Relationship] }) @validate({ nullable: true }) - public responseSourceObject?: Message | RelationshipChange; + public responseSourceObject?: Message | Relationship; public static from(value: ICompleteIncomingRequestParameters): CompleteIncomingRequestParameters { return this.fromAny(value); diff --git a/packages/consumption/src/modules/requests/local/LocalResponse.ts b/packages/consumption/src/modules/requests/local/LocalResponse.ts index d4943c0b4..6b28933c3 100644 --- a/packages/consumption/src/modules/requests/local/LocalResponse.ts +++ b/packages/consumption/src/modules/requests/local/LocalResponse.ts @@ -3,7 +3,7 @@ import { IResponse, Response } from "@nmshd/content"; import { CoreDate, CoreId, CoreSerializable, ICoreDate, ICoreId, ICoreSerializable } from "@nmshd/transport"; export interface ILocalResponseSource extends ICoreSerializable { - type: "Message" | "RelationshipChange"; + type: "Message" | "Relationship"; reference: ICoreId; } @@ -11,7 +11,7 @@ export interface ILocalResponseSource extends ICoreSerializable { export class LocalResponseSource extends CoreSerializable implements ILocalResponseSource { @serialize() @validate() - public type: "Message" | "RelationshipChange"; + public type: "Message" | "Relationship"; @serialize() @validate() diff --git a/packages/consumption/src/modules/requests/outgoing/OutgoingRequestsController.ts b/packages/consumption/src/modules/requests/outgoing/OutgoingRequestsController.ts index 6b2471405..e974e02e7 100644 --- a/packages/consumption/src/modules/requests/outgoing/OutgoingRequestsController.ts +++ b/packages/consumption/src/modules/requests/outgoing/OutgoingRequestsController.ts @@ -8,7 +8,6 @@ import { ICoreId, Message, Relationship, - RelationshipChange, RelationshipTemplate, SynchronizedCollection, CoreErrors as TransportCoreErrors @@ -127,7 +126,7 @@ export class OutgoingRequestsController extends ConsumptionBaseController { public async createAndCompleteFromRelationshipTemplateResponse(params: ICreateAndCompleteOutgoingRequestFromRelationshipTemplateResponseParameters): Promise { const parsedParams = CreateAndCompleteOutgoingRequestFromRelationshipTemplateResponseParameters.from(params); - const peer = parsedParams.responseSource instanceof RelationshipChange ? parsedParams.responseSource.request.createdBy : parsedParams.responseSource.cache!.createdBy; + const peer = parsedParams.responseSource instanceof Relationship ? parsedParams.responseSource.peer.address : parsedParams.responseSource.cache!.createdBy; const response = parsedParams.response; const requestId = response.requestId; @@ -137,7 +136,7 @@ export class OutgoingRequestsController extends ConsumptionBaseController { } // checking for an active relationship is not secure as in the meantime the relationship could have been accepted - const isFromNewRelationship = parsedParams.responseSource instanceof RelationshipChange && parsedParams.responseSource.type === "Creation"; + const isFromNewRelationship = parsedParams.responseSource instanceof Relationship && parsedParams.responseSource.cache!.auditLog.length === 1; const requestContent = isFromNewRelationship ? templateContent.onNewRelationship : templateContent.onExistingRelationship; @@ -220,12 +219,13 @@ export class OutgoingRequestsController extends ConsumptionBaseController { return request; } - private async _complete(requestId: CoreId, responseSourceObject: Message | RelationshipChange, receivedResponse: Response): Promise { + private async _complete(requestId: CoreId, responseSourceObject: Message | Relationship, receivedResponse: Response): Promise { const request = await this.getOrThrow(requestId); this.assertRequestStatus(request, LocalRequestStatus.Open, LocalRequestStatus.Expired); - const responseSourceObjectCreationDate = responseSourceObject instanceof Message ? responseSourceObject.cache!.createdAt : responseSourceObject.request.createdAt; + const responseSourceObjectCreationDate = + responseSourceObject instanceof Message ? responseSourceObject.cache!.createdAt : responseSourceObject.cache!.auditLog[0].createdAt; if (request.status === LocalRequestStatus.Expired && request.isExpired(responseSourceObjectCreationDate)) { throw new ConsumptionError("Cannot complete an expired request with a response that was created before the expiration date"); } @@ -238,12 +238,12 @@ export class OutgoingRequestsController extends ConsumptionBaseController { await this.applyItems(request.content.items, receivedResponse.items, request); - let responseSource: "Message" | "RelationshipChange"; + let responseSource: "Message" | "Relationship"; if (responseSourceObject instanceof Message) { responseSource = "Message"; - } else if (responseSourceObject instanceof RelationshipChange) { - responseSource = "RelationshipChange"; + } else if (responseSourceObject instanceof Relationship) { + responseSource = "Relationship"; } else { throw new ConsumptionError("Invalid responseSourceObject"); } diff --git a/packages/consumption/src/modules/requests/outgoing/createAndCompleteFromRelationshipTemplateResponse/CreateAndCompleteOutgoingRequestFromRelationshipTemplateResponseParameters.ts b/packages/consumption/src/modules/requests/outgoing/createAndCompleteFromRelationshipTemplateResponse/CreateAndCompleteOutgoingRequestFromRelationshipTemplateResponseParameters.ts index 25b70f160..aee050f58 100644 --- a/packages/consumption/src/modules/requests/outgoing/createAndCompleteFromRelationshipTemplateResponse/CreateAndCompleteOutgoingRequestFromRelationshipTemplateResponseParameters.ts +++ b/packages/consumption/src/modules/requests/outgoing/createAndCompleteFromRelationshipTemplateResponse/CreateAndCompleteOutgoingRequestFromRelationshipTemplateResponseParameters.ts @@ -1,14 +1,14 @@ import { ISerializable, Serializable, serialize, type, validate } from "@js-soft/ts-serval"; import { IResponse, Response } from "@nmshd/content"; -import { IMessage, IRelationshipChange, IRelationshipTemplate, Message, RelationshipChange, RelationshipTemplate } from "@nmshd/transport"; +import { CoreDate, IMessage, IRelationship, IRelationshipTemplate, Message, Relationship, RelationshipTemplate } from "@nmshd/transport"; export interface ICreateAndCompleteOutgoingRequestFromRelationshipTemplateResponseParameters extends ISerializable { template: IRelationshipTemplate; - responseSource: IRelationshipChange | IMessage; + responseSource: IRelationship | IMessage; response: IResponse; } -@type("CreateAndCompleteOutgoingRequestFromRelationshipCreationChangeParameters") +@type("CreateAndCompleteOutgoingRequestFromRelationshipTemplateResponseParameters") export class CreateAndCompleteOutgoingRequestFromRelationshipTemplateResponseParameters extends Serializable implements ICreateAndCompleteOutgoingRequestFromRelationshipTemplateResponseParameters @@ -17,14 +17,18 @@ export class CreateAndCompleteOutgoingRequestFromRelationshipTemplateResponsePar @validate() public template: RelationshipTemplate; - @serialize({ unionTypes: [RelationshipChange, Message] }) + @serialize({ unionTypes: [Relationship, Message] }) @validate() - public responseSource: RelationshipChange | Message; + public responseSource: Relationship | Message; @serialize() @validate() public response: Response; + @serialize() + @validate({ nullable: true }) + public responseCreationDate?: CoreDate; + public static from( value: ICreateAndCompleteOutgoingRequestFromRelationshipTemplateResponseParameters ): CreateAndCompleteOutgoingRequestFromRelationshipTemplateResponseParameters { diff --git a/packages/consumption/test/core/TestUtil.ts b/packages/consumption/test/core/TestUtil.ts index 96f843e9c..f8350c09b 100644 --- a/packages/consumption/test/core/TestUtil.ts +++ b/packages/consumption/test/core/TestUtil.ts @@ -244,7 +244,7 @@ export class TestUtil { const relRequest = await to.relationships.sendRelationship({ template: templateTo, - content: requestContent ?? { + creationContent: requestContent ?? { metadata: { mycontent: "request" } } }); @@ -255,7 +255,7 @@ export class TestUtil { const pendingRelationship = syncedRelationships[0]; expect(pendingRelationship.status).toStrictEqual(RelationshipStatus.Pending); - const acceptedRelationshipFromSelf = await from.relationships.acceptChange(pendingRelationship.cache!.creationChange, {}); + const acceptedRelationshipFromSelf = await from.relationships.accept(pendingRelationship.id); expect(acceptedRelationshipFromSelf.status).toStrictEqual(RelationshipStatus.Active); // Get accepted relationship @@ -350,7 +350,7 @@ export class TestUtil { content: "request" }; - return await account.relationships.sendRelationship({ template, content }); + return await account.relationships.sendRelationship({ template, creationContent: content }); } public static async fetchRelationshipTemplateFromTokenReference(account: AccountController, tokenReference: string): Promise { diff --git a/packages/consumption/test/modules/requests/IncomingRequestsController.test.ts b/packages/consumption/test/modules/requests/IncomingRequestsController.test.ts index 3abc95467..e275cbdb2 100644 --- a/packages/consumption/test/modules/requests/IncomingRequestsController.test.ts +++ b/packages/consumption/test/modules/requests/IncomingRequestsController.test.ts @@ -1,6 +1,6 @@ import { IDatabaseConnection } from "@js-soft/docdb-access-abstractions"; import { IRequest, IRequestItemGroup, Request, RequestItemGroup, ResponseItem, ResponseItemGroup, ResponseItemResult } from "@nmshd/content"; -import { CoreDate, CoreId, RelationshipChangeType, TransportLoggerFactory } from "@nmshd/transport"; +import { CoreDate, CoreId, TransportLoggerFactory } from "@nmshd/transport"; import { ConsumptionIds, DecideRequestItemGroupParametersJSON, @@ -794,14 +794,14 @@ describe("IncomingRequestsController", function () { }); }); - test("can handle valid input with a RelationshipChange as responseSource", async function () { + test("can handle valid input with a Relationship as responseSource", async function () { await Given.anIncomingRequestInStatus(LocalRequestStatus.Decided); - const outgoingRelationshipCreationChange = TestObjectFactory.createOutgoingIRelationshipChange(RelationshipChangeType.Creation, context.currentIdentity); + const outgoingRelationship = TestObjectFactory.createIRelationship(); await When.iCompleteTheIncomingRequestWith({ - responseSourceObject: outgoingRelationshipCreationChange + responseSourceObject: outgoingRelationship }); await Then.theRequestMovesToStatus(LocalRequestStatus.Completed); - await Then.theResponseHasItsSourcePropertySetCorrectly({ responseSourceType: "RelationshipChange" }); + await Then.theResponseHasItsSourcePropertySetCorrectly({ responseSourceType: "Relationship" }); await Then.theChangesArePersistedInTheDatabase(); await Then.eventHasBeenPublished(IncomingRequestStatusChangedEvent, { newStatus: LocalRequestStatus.Completed @@ -981,11 +981,11 @@ describe("IncomingRequestsController", function () { ] }); - const relationshipChange = TestObjectFactory.createOutgoingIRelationshipChange(RelationshipChangeType.Creation, context.currentIdentity); + const relationship = TestObjectFactory.createIRelationship(); cnsRequest = await context.incomingRequestsController.complete({ requestId: cnsRequest.id, - responseSourceObject: relationshipChange + responseSourceObject: relationship }); expect(cnsRequest).toBeDefined(); diff --git a/packages/consumption/test/modules/requests/OutgoingRequestsController.test.ts b/packages/consumption/test/modules/requests/OutgoingRequestsController.test.ts index c75dc430c..b89d5aa8a 100644 --- a/packages/consumption/test/modules/requests/OutgoingRequestsController.test.ts +++ b/packages/consumption/test/modules/requests/OutgoingRequestsController.test.ts @@ -11,7 +11,7 @@ import { ResponseItemResult, ResponseResult } from "@nmshd/content"; -import { CoreAddress, CoreDate, CoreId, RelationshipChangeType, TransportLoggerFactory } from "@nmshd/transport"; +import { CoreAddress, CoreDate, CoreId, TransportLoggerFactory } from "@nmshd/transport"; import { ConsumptionIds, ErrorValidationResult, @@ -23,7 +23,7 @@ import { OutgoingRequestStatusChangedEvent, ValidationResult } from "../../../src"; -import { TestUtil, loggerFactory } from "../../core/TestUtil"; +import { loggerFactory, TestUtil } from "../../core/TestUtil"; import { RequestsGiven, RequestsTestsContext, RequestsThen, RequestsWhen } from "./RequestsIntegrationTest"; import { TestObjectFactory } from "./testHelpers/TestObjectFactory"; import { ITestRequestItem, TestRequestItem } from "./testHelpers/TestRequestItem"; @@ -232,23 +232,23 @@ describe("OutgoingRequestsController", function () { }); describe("CreateFromRelationshipTemplateResponse", function () { - describe("with a RelationshipCreationChange", function () { + describe("with a RelationshipCreation", function () { test("combines calls to create, sent and complete", async function () { - await When.iCreateAnOutgoingRequestFromRelationshipCreationChange(); + await When.iCreateAnOutgoingRequestFromRelationshipCreation(); await Then.theCreatedOutgoingRequestHasAllProperties(); await Then.theRequestIsInStatus(LocalRequestStatus.Completed); await Then.theRequestHasItsSourcePropertySet(); await Then.theRequestHasItsResponsePropertySetCorrectly(ResponseItemResult.Accepted); await Then.theResponseHasItsSourcePropertySetCorrectly({ - responseSourceType: "RelationshipChange" + responseSourceType: "Relationship" }); await Then.theNewRequestIsPersistedInTheDatabase(); await Then.eventsHaveBeenPublished(OutgoingRequestCreatedAndCompletedEvent); }); test("uses the id from the response for the created Local Request", async function () { - await When.iCreateAnOutgoingRequestFromRelationshipCreationChangeWith({ - responseSource: TestObjectFactory.createIncomingIRelationshipChange(RelationshipChangeType.Creation, "requestIdReceivedFromPeer"), + await When.iCreateAnOutgoingRequestFromRelationshipCreationWith({ + responseSource: TestObjectFactory.createIRelationship(), response: TestObjectFactory.createResponse("requestIdReceivedFromPeer") }); @@ -260,14 +260,14 @@ describe("OutgoingRequestsController", function () { test("create an outgoing request from relationship creation with an active relationship", async function () { await Given.anActiveRelationshipToIdentity(); - await When.iCreateAnOutgoingRequestFromRelationshipCreationChangeWith({ + await When.iCreateAnOutgoingRequestFromRelationshipCreationWith({ template: TestObjectFactory.createOutgoingIRelationshipTemplate( context.currentIdentity, RelationshipTemplateContent.from({ onNewRelationship: TestObjectFactory.createRequestWithOneItem() }) ), - responseSource: TestObjectFactory.createIncomingIRelationshipChange(RelationshipChangeType.Creation, "requestIdReceivedFromPeer"), + responseSource: TestObjectFactory.createIRelationship(), response: TestObjectFactory.createResponse("requestIdReceivedFromPeer") }); @@ -282,7 +282,7 @@ describe("OutgoingRequestsController", function () { }); test("uses the content from onExistingRelationship when the relationship exists", async function () { - await When.iCreateAnOutgoingRequestFromRelationshipCreationChangeWhenRelationshipExistsWith({ + await When.iCreateAnOutgoingRequestFromRelationshipCreationWhenRelationshipExistsWith({ responseSource: TestObjectFactory.createIncomingIMessageWithResponse(CoreAddress.from("id1"), "requestIdReceivedFromPeer"), response: TestObjectFactory.createResponse("requestIdReceivedFromPeer") }); diff --git a/packages/consumption/test/modules/requests/RequestEnd2End.test.ts b/packages/consumption/test/modules/requests/RequestEnd2End.test.ts index 5c178ba52..9b9cb89ad 100644 --- a/packages/consumption/test/modules/requests/RequestEnd2End.test.ts +++ b/packages/consumption/test/modules/requests/RequestEnd2End.test.ts @@ -1,6 +1,6 @@ /* eslint-disable jest/expect-expect */ import { IDatabaseConnection } from "@js-soft/docdb-access-abstractions"; -import { AcceptResponseItem, RelationshipCreationChangeRequestContent, RelationshipTemplateContent, Request, Response, ResponseWrapper } from "@nmshd/content"; +import { AcceptResponseItem, RelationshipCreationContent, RelationshipTemplateContent, Request, Response, ResponseWrapper } from "@nmshd/content"; import { AccountController, CoreDate, Message, Relationship, RelationshipTemplate, Transport } from "@nmshd/transport"; import { ConsumptionController, LocalRequest, LocalRequestStatus } from "../../../src"; import { TestUtil } from "../../core/TestUtil"; @@ -10,7 +10,7 @@ import { TestRequestItemProcessor } from "./testHelpers/TestRequestItemProcessor let connection: IDatabaseConnection; let transport: Transport; -describe("End2End Request/Response via Relationship Template/ChangeRequest", function () { +describe("End2End Request/Response via Relationship Template", function () { let sAccountController: AccountController; let sConsumptionController: ConsumptionController; let rAccountController: AccountController; @@ -23,6 +23,9 @@ describe("End2End Request/Response via Relationship Template/ChangeRequest", fun let sRelationship: Relationship; let sLocalRequest: LocalRequest; + let sCreationContent: RelationshipCreationContent; + let rCreationContent: RelationshipCreationContent; + beforeAll(async function () { connection = await TestUtil.createConnection(); transport = TestUtil.createTransport(connection); @@ -85,19 +88,18 @@ describe("End2End Request/Response via Relationship Template/ChangeRequest", fun }); }); - test("recipient: create Relationship with Response in Relationship Change", async function () { + test("recipient: create Relationship with Response in Relationship Creation Content", async function () { rRelationship = await rAccountController.relationships.sendRelationship({ template: rTemplate, - content: RelationshipCreationChangeRequestContent.from({ - response: rLocalRequest.response!.content - }) + creationContent: RelationshipCreationContent.from({ response: rLocalRequest.response!.content }) }); + rCreationContent = rRelationship.cache?.creationContent as RelationshipCreationContent; }); test("recipient: complete Local Request", async function () { rLocalRequest = await rConsumptionController.incomingRequests.complete({ requestId: rLocalRequest.id, - responseSourceObject: rRelationship.cache!.changes[0] + responseSourceObject: rRelationship }); }); @@ -107,11 +109,12 @@ describe("End2End Request/Response via Relationship Template/ChangeRequest", fun }); test("sender: create Local Request and Response from Relationship Change", async function () { - const response = (sRelationship.cache!.changes[0].request.content as RelationshipCreationChangeRequestContent).response; + sCreationContent = sRelationship.cache!.creationContent! as RelationshipCreationContent; + const response = sCreationContent.response; sLocalRequest = await sConsumptionController.outgoingRequests.createAndCompleteFromRelationshipTemplateResponse({ template: sTemplate, - responseSource: sRelationship.cache!.changes[0], + responseSource: sRelationship, response }); }); @@ -134,8 +137,8 @@ describe("End2End Request/Response via Relationship Template/ChangeRequest", fun expect(sLocalRequest.content.items[0]).toBeInstanceOf(TestRequestItem); expect(rLocalRequest.content.items[0]).toBeInstanceOf(TestRequestItem); - expect((sRelationship.cache!.changes[0].request.content as RelationshipCreationChangeRequestContent).response).toBeInstanceOf(Response); - expect((rRelationship.cache!.changes[0].request.content as RelationshipCreationChangeRequestContent).response).toBeInstanceOf(Response); + expect(sCreationContent.response).toBeInstanceOf(Response); + expect(rCreationContent.response).toBeInstanceOf(Response); expect(sLocalRequest.response!.content.items[0]).toBeInstanceOf(AcceptResponseItem); expect(rLocalRequest.response!.content.items[0]).toBeInstanceOf(AcceptResponseItem); diff --git a/packages/consumption/test/modules/requests/RequestsIntegrationTest.ts b/packages/consumption/test/modules/requests/RequestsIntegrationTest.ts index 635ea40d5..58b0953ed 100644 --- a/packages/consumption/test/modules/requests/RequestsIntegrationTest.ts +++ b/packages/consumption/test/modules/requests/RequestsIntegrationTest.ts @@ -8,12 +8,11 @@ import { CoreId, IConfigOverwrite, ICoreId, + IdentityController, IMessage, IRelationshipTemplate, - IdentityController, Message, Relationship, - RelationshipChangeType, RelationshipTemplate, SynchronizedCollection, Transport @@ -28,10 +27,10 @@ import { ICreateAndCompleteOutgoingRequestFromRelationshipTemplateResponseParameters, ICreateOutgoingRequestParameters, ILocalRequestSource, + IncomingRequestsController, IReceivedIncomingRequestParameters, IRequireManualDecisionOfIncomingRequestParameters, ISentOutgoingRequestParameters, - IncomingRequestsController, LocalRequest, LocalRequestSource, LocalRequestStatus, @@ -580,20 +579,18 @@ export class RequestsWhen { this.context.localRequestAfterAction = await this.context.outgoingRequestsController.create(params); } - public async iCreateAnOutgoingRequestFromRelationshipCreationChange(): Promise { - await this.iCreateAnOutgoingRequestFromRelationshipCreationChangeWith({}); + public async iCreateAnOutgoingRequestFromRelationshipCreation(): Promise { + await this.iCreateAnOutgoingRequestFromRelationshipCreationWith({}); } - public async iCreateAnOutgoingRequestFromRelationshipCreationChangeWhenRelationshipExistsWith( + public async iCreateAnOutgoingRequestFromRelationshipCreationWhenRelationshipExistsWith( params: Partial ): Promise { this.context.relationshipToReturnFromGetActiveRelationshipToIdentity = TestObjectFactory.createRelationship(); - await this.iCreateAnOutgoingRequestFromRelationshipCreationChangeWith(params); + await this.iCreateAnOutgoingRequestFromRelationshipCreationWith(params); } - public async iCreateAnOutgoingRequestFromRelationshipCreationChangeWith( - params: Partial - ): Promise { + public async iCreateAnOutgoingRequestFromRelationshipCreationWith(params: Partial): Promise { params.template ??= TestObjectFactory.createOutgoingIRelationshipTemplate( this.context.currentIdentity, RelationshipTemplateContent.from({ @@ -601,7 +598,7 @@ export class RequestsWhen { onExistingRelationship: TestObjectFactory.createRequestWithTwoItems() }) ); - params.responseSource ??= TestObjectFactory.createIncomingIRelationshipChange(RelationshipChangeType.Creation); + params.responseSource ??= TestObjectFactory.createIRelationship(); params.response ??= TestObjectFactory.createResponse(); this.context.localRequestAfterAction = await this.context.outgoingRequestsController.createAndCompleteFromRelationshipTemplateResponse( diff --git a/packages/consumption/test/modules/requests/testHelpers/TestObjectFactory.ts b/packages/consumption/test/modules/requests/testHelpers/TestObjectFactory.ts index fa0eb0e8e..91ef1a7e9 100644 --- a/packages/consumption/test/modules/requests/testHelpers/TestObjectFactory.ts +++ b/packages/consumption/test/modules/requests/testHelpers/TestObjectFactory.ts @@ -5,7 +5,6 @@ import { IdentityAttribute, IIdentityAttribute, IRelationshipAttribute, - IRelationshipCreationChangeRequestContent, IRelationshipTemplateContent, IRequest, IResponse, @@ -28,13 +27,11 @@ import { Identity, IMessage, IRelationship, - IRelationshipChange, IRelationshipTemplate, Message, Realm, Relationship, - RelationshipChangeStatus, - RelationshipChangeType, + RelationshipAuditLogEntryReason, RelationshipStatus, RelationshipTemplate, RelationshipTemplatePublicKey @@ -58,21 +55,26 @@ export class TestObjectFactory { }), status: properties?.status ?? RelationshipStatus.Active, relationshipSecretId: properties?.relationshipSecretId ?? CoreId.from("RELSEC1"), + cachedAt: properties?.cachedAt ?? CoreDate.from("2020-01-02T00:00:00.000Z"), cache: properties?.cache ?? CachedRelationship.from({ - changes: [ + auditLog: [ { - id: CoreId.from("RELCH1"), - type: RelationshipChangeType.Creation, - status: RelationshipChangeStatus.Accepted, - relationshipId: CoreId.from("REL1"), - request: { - createdAt: CoreDate.from("2020-01-01T00:00:00.000Z"), - content: {}, - createdBy: CoreAddress.from("id1"), - createdByDevice: CoreId.from("DEV1") - } + createdAt: CoreDate.from("2020-01-01T00:00:00.000Z"), + createdBy: CoreAddress.from("id2"), + createdByDevice: CoreId.from("DVC1"), + reason: RelationshipAuditLogEntryReason.Creation, + newStatus: RelationshipStatus.Pending + }, + + { + createdAt: CoreDate.from("2020-01-02T00:00:00.000Z"), + createdBy: CoreAddress.from("id1"), + createdByDevice: CoreId.from("DVC1"), + reason: RelationshipAuditLogEntryReason.AcceptanceOfCreation, + oldStatus: RelationshipStatus.Pending, + newStatus: RelationshipStatus.Active } ], template: this.createIncomingRelationshipTemplate() @@ -277,49 +279,32 @@ export class TestObjectFactory { return RelationshipTemplate.from(this.createIncomingIRelationshipTemplate()); } - public static createIncomingIRelationshipChange(type: RelationshipChangeType, requestId?: string): IRelationshipChange { - return { - // @ts-expect-error - "@type": "RelationshipChange", - id: CoreId.from("RCH1"), - relationshipId: CoreId.from("REL1"), - type: type, - status: RelationshipChangeStatus.Pending, - request: { - createdAt: CoreDate.utc(), - createdBy: CoreAddress.from("id1"), - createdByDevice: CoreId.from("DVC1"), - content: { - "@type": "RelationshipCreationChangeRequestContent", - response: { - "@type": "Response", - result: ResponseResult.Accepted, - items: [ - { - // @ts-expect-error - "@type": "AcceptResponseItem", - result: ResponseItemResult.Accepted - } - ], - requestId: CoreId.from(requestId ?? "REQ1") - } as IResponse - } as IRelationshipCreationChangeRequestContent - } - }; - } - - public static createOutgoingIRelationshipChange(type: RelationshipChangeType, sender: CoreAddress): IRelationshipChange { + public static createIRelationship(): IRelationship { return { // @ts-expect-error - "@type": "RelationshipChange", - id: CoreId.from("RCH1"), - relationshipId: CoreId.from("REL1"), - type: type, - status: RelationshipChangeStatus.Pending, - request: { - createdAt: CoreDate.utc(), - createdBy: sender, - createdByDevice: CoreId.from("DVC1") + "@type": "Relationship", + id: CoreId.from("REL1"), + status: RelationshipStatus.Pending, + relationshipSecretId: CoreId.from("REL1"), + peer: { + address: CoreAddress.from("id2"), + publicKey: CryptoSignaturePublicKey.from({ + algorithm: CryptoSignatureAlgorithm.ECDSA_ED25519, + publicKey: CoreBuffer.fromBase64URL("aS-A8ywidL00DfBlZySOG_1-NdSBW38uGD1il_Ymk5g") + }), + realm: Realm.Prod + }, + cache: { + template: this.createIncomingIRelationshipTemplate(), + auditLog: [ + { + createdAt: CoreDate.from("2020-01-01T00:00:00.000Z"), + createdBy: CoreAddress.from("id2"), + createdByDevice: CoreId.from("DVC1"), + reason: RelationshipAuditLogEntryReason.Creation, + newStatus: RelationshipStatus.Active + } + ] } }; } diff --git a/packages/content/src/relationships/RelationshipCreationContent.ts b/packages/content/src/relationships/RelationshipCreationContent.ts new file mode 100644 index 000000000..a1872ae5f --- /dev/null +++ b/packages/content/src/relationships/RelationshipCreationContent.ts @@ -0,0 +1,27 @@ +import { ISerializable, Serializable, serialize, type, validate } from "@js-soft/ts-serval"; +import { ContentJSON } from "../ContentJSON"; +import { IResponse, Response, ResponseJSON } from "../requests/response/Response"; + +export interface RelationshipCreationContentJSON extends ContentJSON { + "@type": "RelationshipCreationContent"; + response: ResponseJSON; +} + +export interface IRelationshipCreationContent extends ISerializable { + response: IResponse; +} + +@type("RelationshipCreationContent") +export class RelationshipCreationContent extends Serializable implements IRelationshipCreationContent { + @serialize() + @validate() + public response: Response; + + public static from(value: IRelationshipCreationContent | Omit): RelationshipCreationContent { + return this.fromAny(value); + } + + public override toJSON(verbose?: boolean | undefined, serializeAsString?: boolean | undefined): RelationshipCreationContentJSON { + return super.toJSON(verbose, serializeAsString) as RelationshipCreationContentJSON; + } +} diff --git a/packages/content/src/relationships/index.ts b/packages/content/src/relationships/index.ts index dcfeaed74..dba4e47a8 100644 --- a/packages/content/src/relationships/index.ts +++ b/packages/content/src/relationships/index.ts @@ -1,2 +1,3 @@ export * from "./RelationshipCreationChangeRequestContent"; +export * from "./RelationshipCreationContent"; export * from "./RelationshipTemplateContent"; diff --git a/packages/runtime/src/dataViews/DataViewExpander.ts b/packages/runtime/src/dataViews/DataViewExpander.ts index d90dc417b..3674a7a0a 100644 --- a/packages/runtime/src/dataViews/DataViewExpander.ts +++ b/packages/runtime/src/dataViews/DataViewExpander.ts @@ -79,7 +79,6 @@ import { MessageDTO, MessageWithAttachmentsDTO, RecipientDTO, - RelationshipChangeDTO, RelationshipDTO, RelationshipTemplateDTO } from "../types"; @@ -139,7 +138,7 @@ import { import { DataViewObject } from "./DataViewObject"; import { DataViewTranslateable } from "./DataViewTranslateable"; import { MessageDVO, MessageStatus, RecipientDVO } from "./transport/MessageDVO"; -import { RelationshipChangeDVO, RelationshipChangeResponseDVO, RelationshipDirection, RelationshipDVO } from "./transport/RelationshipDVO"; +import { RelationshipDirection, RelationshipDVO } from "./transport/RelationshipDVO"; export class DataViewExpander { public constructor( @@ -1563,48 +1562,6 @@ export class DataViewExpander { return await Promise.all(relationshipPromises); } - public expandRelationshipChangeDTO(relationship: RelationshipDTO, change: RelationshipChangeDTO): Promise { - const date = change.response ? change.response.createdAt : change.request.createdAt; - let isOwn = false; - if (this.identityController.isMe(CoreAddress.from(change.request.createdBy))) { - isOwn = true; - } - - let response: RelationshipChangeResponseDVO | undefined; - if (change.response) { - response = { - ...change.response, - id: `${change.id}_response`, - name: "i18n://dvo.relationshipChange.response.name", - type: "RelationshipChangeResponseDVO" - }; - } - - return Promise.resolve({ - type: "RelationshipChangeDVO", - id: change.id, - name: "", - date: date, - status: change.status, - statusText: `i18n://dvo.relationshipChange.${change.status}`, - changeType: change.type, - changeTypeText: `i18n://dvo.relationshipChange.${change.type}`, - isOwn: isOwn, - request: { - ...change.request, - id: `${change.id}_request`, - name: "i18n://dvo.relationshipChange.request.name", - type: "RelationshipChangeRequestDVO" - }, - response: response - }); - } - - public async expandRelationshipChangeDTOs(relationship: RelationshipDTO): Promise { - const changePromises = relationship.changes.map((change) => this.expandRelationshipChangeDTO(relationship, change)); - return await Promise.all(changePromises); - } - private async createRelationshipDVO(relationship: RelationshipDTO): Promise { let relationshipSetting: RelationshipSettingDVO; const settingResult = await this.consumption.settings.getSettings({ query: { reference: relationship.id } }); @@ -1642,7 +1599,7 @@ export class DataViewExpander { } let direction = RelationshipDirection.Incoming; - if (this.identityController.isMe(CoreAddress.from(relationship.changes[0].request.createdBy))) { + if (!relationship.template.isOwn) { direction = RelationshipDirection.Outgoing; } @@ -1659,7 +1616,7 @@ export class DataViewExpander { statusText = DataViewTranslateable.transport.relationshipActive; } - const changes = await this.expandRelationshipChangeDTOs(relationship); + const creationDate = relationship.auditLog[0].createdAt; let name; if (stringByType["DisplayName"]) { @@ -1680,7 +1637,7 @@ export class DataViewExpander { id: relationship.id, name: relationshipSetting.userTitle ?? name, description: relationshipSetting.userDescription ?? statusText, - date: relationship.changes[0].request.createdAt, + date: creationDate, image: "", type: "RelationshipDVO", status: relationship.status, @@ -1690,9 +1647,9 @@ export class DataViewExpander { attributeMap: attributesByType, items: expandedAttributes, nameMap: stringByType, - changes: changes, - changeCount: changes.length, - templateId: relationship.template.id + templateId: relationship.template.id, + auditLog: relationship.auditLog, + creationContent: relationship.creationContent }; } diff --git a/packages/runtime/src/dataViews/consumption/LocalRequestDVO.ts b/packages/runtime/src/dataViews/consumption/LocalRequestDVO.ts index 2d0893632..25a4c990c 100644 --- a/packages/runtime/src/dataViews/consumption/LocalRequestDVO.ts +++ b/packages/runtime/src/dataViews/consumption/LocalRequestDVO.ts @@ -32,6 +32,6 @@ export interface LocalResponseDVO extends DataViewObject { } export interface LocalResponseSourceDVO { - type: "Message" | "RelationshipChange"; + type: "Message" | "Relationship"; reference: string; } diff --git a/packages/runtime/src/dataViews/transport/RelationshipDVO.ts b/packages/runtime/src/dataViews/transport/RelationshipDVO.ts index cbbd6a36e..012c82679 100644 --- a/packages/runtime/src/dataViews/transport/RelationshipDVO.ts +++ b/packages/runtime/src/dataViews/transport/RelationshipDVO.ts @@ -1,4 +1,4 @@ -import { RelationshipChangeStatus, RelationshipChangeType } from "@nmshd/transport"; +import { RelationshipAuditLogDTO } from "../../types/transport/RelationshipDTO"; import { LocalAttributeDVO } from "../consumption"; import { DataViewObject } from "../DataViewObject"; @@ -14,8 +14,8 @@ export interface RelationshipDVO extends DataViewObject { statusText: string; isPinned: boolean; theme?: RelationshipTheme; - changes: RelationshipChangeDVO[]; - changeCount: number; + creationContent: any; + auditLog: RelationshipAuditLogDTO; items: LocalAttributeDVO[]; attributeMap: Record; nameMap: Record; @@ -28,30 +28,3 @@ export interface RelationshipTheme { backgroundColor?: string; foregroundColor?: string; } - -export interface RelationshipChangeDVO extends DataViewObject { - type: "RelationshipChangeDVO"; - request: RelationshipChangeRequestDVO; - response?: RelationshipChangeResponseDVO; - status: RelationshipChangeStatus; - statusText: string; - changeType: RelationshipChangeType; - changeTypeText: string; - isOwn: boolean; -} - -export interface RelationshipChangeRequestDVO extends DataViewObject { - type: "RelationshipChangeRequestDVO"; - createdBy: string; - createdByDevice: string; - createdAt: string; - content?: unknown; -} - -export interface RelationshipChangeResponseDVO extends DataViewObject { - type: "RelationshipChangeResponseDVO"; - createdBy: string; - createdByDevice: string; - createdAt: string; - content?: unknown; -} diff --git a/packages/runtime/src/events/EventProxy.ts b/packages/runtime/src/events/EventProxy.ts index 0d2f3cf99..19794094b 100644 --- a/packages/runtime/src/events/EventProxy.ts +++ b/packages/runtime/src/events/EventProxy.ts @@ -10,7 +10,7 @@ import { IncomingRequestStatusChangedEvent, OutgoingRequestCreatedAndCompletedEvent, OutgoingRequestCreatedEvent, - OutgoingRequestFromRelationshipCreationChangeCreatedAndCompletedEvent, + OutgoingRequestFromRelationshipCreationCreatedAndCompletedEvent, OutgoingRequestStatusChangedEvent, OwnSharedAttributeDeletedByOwnerEvent, OwnSharedAttributeSucceededEvent, @@ -141,8 +141,8 @@ export class EventProxy { this.targetEventBus.publish(new OutgoingRequestCreatedAndCompletedEvent(event.eventTargetAddress, mappedRequest)); - if (event.data.response?.source?.type === "RelationshipChange") { - this.targetEventBus.publish(new OutgoingRequestFromRelationshipCreationChangeCreatedAndCompletedEvent(event.eventTargetAddress, mappedRequest)); + if (event.data.response?.source?.type === "Relationship") { + this.targetEventBus.publish(new OutgoingRequestFromRelationshipCreationCreatedAndCompletedEvent(event.eventTargetAddress, mappedRequest)); } }); diff --git a/packages/runtime/src/events/consumption/OutgoingRequestFromRelationshipCreationChangeCreatedAndCompletedEvent.ts b/packages/runtime/src/events/consumption/OutgoingRequestFromRelationshipCreationCreatedAndCompletedEvent.ts similarity index 52% rename from packages/runtime/src/events/consumption/OutgoingRequestFromRelationshipCreationChangeCreatedAndCompletedEvent.ts rename to packages/runtime/src/events/consumption/OutgoingRequestFromRelationshipCreationCreatedAndCompletedEvent.ts index 05d5e99e7..461fe66c9 100644 --- a/packages/runtime/src/events/consumption/OutgoingRequestFromRelationshipCreationChangeCreatedAndCompletedEvent.ts +++ b/packages/runtime/src/events/consumption/OutgoingRequestFromRelationshipCreationCreatedAndCompletedEvent.ts @@ -1,11 +1,11 @@ import { LocalRequestDTO } from "../../types"; import { DataEvent } from "../DataEvent"; -export class OutgoingRequestFromRelationshipCreationChangeCreatedAndCompletedEvent extends DataEvent { - public static readonly namespace = "consumption.outgoingRequestFromRelationshipCreationChangeCreatedAndCompleted"; +export class OutgoingRequestFromRelationshipCreationCreatedAndCompletedEvent extends DataEvent { + public static readonly namespace = "consumption.outgoingRequestFromRelationshipCreationCreatedAndCompleted"; public constructor(eventTargetAddress: string, data: LocalRequestDTO) { - super(OutgoingRequestFromRelationshipCreationChangeCreatedAndCompletedEvent.namespace, eventTargetAddress, data); + super(OutgoingRequestFromRelationshipCreationCreatedAndCompletedEvent.namespace, eventTargetAddress, data); if (!data.isOwn) throw new Error("Cannot create this event for an incoming Request"); } diff --git a/packages/runtime/src/events/consumption/index.ts b/packages/runtime/src/events/consumption/index.ts index 3dba1195e..6d47d61cc 100644 --- a/packages/runtime/src/events/consumption/index.ts +++ b/packages/runtime/src/events/consumption/index.ts @@ -8,7 +8,7 @@ export * from "./MailReceivedEvent"; export * from "./MessageProcessedEvent"; export * from "./OutgoingRequestCreatedAndCompletedEvent"; export * from "./OutgoingRequestCreatedEvent"; -export * from "./OutgoingRequestFromRelationshipCreationChangeCreatedAndCompletedEvent"; +export * from "./OutgoingRequestFromRelationshipCreationCreatedAndCompletedEvent"; export * from "./OutgoingRequestStatusChangedEvent"; export * from "./OwnSharedAttributeDeletedByOwnerEvent"; export * from "./OwnSharedAttributeSucceededEvent"; diff --git a/packages/runtime/src/extensibility/facades/transport/RelationshipsFacade.ts b/packages/runtime/src/extensibility/facades/transport/RelationshipsFacade.ts index da032f63c..2de41a73e 100644 --- a/packages/runtime/src/extensibility/facades/transport/RelationshipsFacade.ts +++ b/packages/runtime/src/extensibility/facades/transport/RelationshipsFacade.ts @@ -2,8 +2,8 @@ import { ApplicationError, Result } from "@js-soft/ts-utils"; import { Inject } from "typescript-ioc"; import { RelationshipDTO } from "../../../types"; import { - AcceptRelationshipChangeRequest, - AcceptRelationshipChangeUseCase, + AcceptRelationshipRequest, + AcceptRelationshipUseCase, CreateRelationshipRequest, CreateRelationshipUseCase, GetAttributesForRelationshipRequest, @@ -12,13 +12,13 @@ import { GetRelationshipByAddressRequest, GetRelationshipByAddressUseCase, GetRelationshipRequest, - GetRelationshipUseCase, GetRelationshipsRequest, GetRelationshipsUseCase, - RejectRelationshipChangeRequest, - RejectRelationshipChangeUseCase, - RevokeRelationshipChangeRequest, - RevokeRelationshipChangeUseCase + GetRelationshipUseCase, + RejectRelationshipRequest, + RejectRelationshipUseCase, + RevokeRelationshipRequest, + RevokeRelationshipUseCase } from "../../../useCases"; export class RelationshipsFacade { @@ -27,9 +27,9 @@ export class RelationshipsFacade { @Inject private readonly getRelationshipUseCase: GetRelationshipUseCase, @Inject private readonly getRelationshipByAddressUseCase: GetRelationshipByAddressUseCase, @Inject private readonly createRelationshipUseCase: CreateRelationshipUseCase, - @Inject private readonly acceptRelationshipChangeUseCase: AcceptRelationshipChangeUseCase, - @Inject private readonly rejectRelationshipChangeUseCase: RejectRelationshipChangeUseCase, - @Inject private readonly revokeRelationshipChangeUseCase: RevokeRelationshipChangeUseCase, + @Inject private readonly acceptRelationshipUseCase: AcceptRelationshipUseCase, + @Inject private readonly rejectRelationshipUseCase: RejectRelationshipUseCase, + @Inject private readonly revokeRelationshipUseCase: RevokeRelationshipUseCase, @Inject private readonly getAttributesForRelationshipUseCase: GetAttributesForRelationshipUseCase ) {} @@ -49,16 +49,16 @@ export class RelationshipsFacade { return await this.createRelationshipUseCase.execute(request); } - public async acceptRelationshipChange(request: AcceptRelationshipChangeRequest): Promise> { - return await this.acceptRelationshipChangeUseCase.execute(request); + public async acceptRelationship(request: AcceptRelationshipRequest): Promise> { + return await this.acceptRelationshipUseCase.execute(request); } - public async rejectRelationshipChange(request: RejectRelationshipChangeRequest): Promise> { - return await this.rejectRelationshipChangeUseCase.execute(request); + public async rejectRelationship(request: RejectRelationshipRequest): Promise> { + return await this.rejectRelationshipUseCase.execute(request); } - public async revokeRelationshipChange(request: RevokeRelationshipChangeRequest): Promise> { - return await this.revokeRelationshipChangeUseCase.execute(request); + public async revokeRelationship(request: RevokeRelationshipRequest): Promise> { + return await this.revokeRelationshipUseCase.execute(request); } public async getAttributesForRelationship(request: GetAttributesForRelationshipRequest): Promise> { diff --git a/packages/runtime/src/modules/RequestModule.ts b/packages/runtime/src/modules/RequestModule.ts index 381ea0656..cef9c2f98 100644 --- a/packages/runtime/src/modules/RequestModule.ts +++ b/packages/runtime/src/modules/RequestModule.ts @@ -1,14 +1,5 @@ import { LocalRequestStatus } from "@nmshd/consumption"; -import { - RelationshipCreationChangeRequestContent, - RelationshipCreationChangeRequestContentJSON, - RelationshipTemplateContentJSON, - RequestJSON, - ResponseJSON, - ResponseResult, - ResponseWrapper, - ResponseWrapperJSON -} from "@nmshd/content"; +import { RelationshipCreationContent, RelationshipTemplateContentJSON, RequestJSON, ResponseJSON, ResponseResult, ResponseWrapper, ResponseWrapperJSON } from "@nmshd/content"; import { IncomingRequestStatusChangedEvent, MessageProcessedEvent, @@ -226,8 +217,8 @@ export class RequestModule extends RuntimeModule { return; } - const creationChangeContent = RelationshipCreationChangeRequestContent.from({ response: request.response!.content }); - const createRelationshipResult = await services.transportServices.relationships.createRelationship({ templateId, content: creationChangeContent }); + const creationContent = RelationshipCreationContent.from({ response: request.response!.content }); + const createRelationshipResult = await services.transportServices.relationships.createRelationship({ templateId, creationContent }); if (createRelationshipResult.isError) { this.logger.error(`Could not create relationship for templateId '${templateId}'. Root error:`, createRelationshipResult.error); return; @@ -236,7 +227,7 @@ export class RequestModule extends RuntimeModule { const requestId = request.id; const completeRequestResult = await services.consumptionServices.incomingRequests.complete({ requestId, - responseSourceId: createRelationshipResult.value.changes[0].id + responseSourceId: createRelationshipResult.value.id }); if (completeRequestResult.isError) { this.logger.error(`Could not complete the request '${requestId}'. Root error:`, completeRequestResult.error); @@ -291,19 +282,13 @@ export class RequestModule extends RuntimeModule { // do not trigger for templates without the correct content type if (template.content["@type"] !== "RelationshipTemplateContent") return; - const relationshipCreationChange = createdRelationship.changes[0]; - const relationshipChangeId = relationshipCreationChange.id; - // do not trigger for creation changes without the correct content type - if (relationshipCreationChange.request.content["@type"] !== "RelationshipCreationChangeRequestContent") return; - - const relationshipCreationChangeContent = relationshipCreationChange.request.content as RelationshipCreationChangeRequestContentJSON; const result = await services.consumptionServices.outgoingRequests.createAndCompleteFromRelationshipTemplateResponse({ templateId, - responseSourceId: relationshipChangeId, - response: relationshipCreationChangeContent.response + responseSourceId: createdRelationship.id, + response: createdRelationship.creationContent.response }); if (result.isError) { - this.logger.error(`Could not create and complete request for templateId '${templateId}' and changeId '${relationshipChangeId}'. Root error:`, result.error); + this.logger.error(`Could not create and complete request for templateId '${templateId}' and relationshipId '${createdRelationship.id}'. Root error:`, result.error); return; } } diff --git a/packages/runtime/src/types/consumption/LocalRequestDTO.ts b/packages/runtime/src/types/consumption/LocalRequestDTO.ts index f23d5f682..49510d8e9 100644 --- a/packages/runtime/src/types/consumption/LocalRequestDTO.ts +++ b/packages/runtime/src/types/consumption/LocalRequestDTO.ts @@ -18,7 +18,7 @@ export interface LocalRequestSourceDTO { } export interface LocalResponseSourceDTO { - type: "Message" | "RelationshipChange"; + type: "Message" | "Relationship"; reference: string; } diff --git a/packages/runtime/src/types/transport/RelationshipDTO.ts b/packages/runtime/src/types/transport/RelationshipDTO.ts index 657dfa64a..eb9e671a1 100644 --- a/packages/runtime/src/types/transport/RelationshipDTO.ts +++ b/packages/runtime/src/types/transport/RelationshipDTO.ts @@ -11,6 +11,24 @@ export enum RelationshipStatus { Terminated = "Terminated" } +export enum RelationshipAuditLogEntryReason { + Creation = "Creation", + AcceptanceOfCreation = "AcceptanceOfCreation", + RejectionOfCreation = "RejectionOfCreation", + RevocationOfCreation = "RevocationOfCreation" +} + +export interface RelationshipAuditLogEntryDTO { + createdAt: string; + createdBy: string; + createdByDevice: string; + reason: RelationshipAuditLogEntryReason; + oldStatus?: RelationshipStatus; + newStatus: RelationshipStatus; +} + +export interface RelationshipAuditLogDTO extends Array {} + export interface RelationshipDTO { id: string; template: RelationshipTemplateDTO; @@ -18,4 +36,6 @@ export interface RelationshipDTO { peer: string; peerIdentity: IdentityDTO; changes: RelationshipChangeDTO[]; + creationContent: any; + auditLog: RelationshipAuditLogDTO; } diff --git a/packages/runtime/src/useCases/common/Schemas.ts b/packages/runtime/src/useCases/common/Schemas.ts index 03de260a8..db108d62f 100644 --- a/packages/runtime/src/useCases/common/Schemas.ts +++ b/packages/runtime/src/useCases/common/Schemas.ts @@ -3054,7 +3054,7 @@ export const CompleteIncomingRequestRequest: any = { "$ref": "#/definitions/MessageIdString" }, { - "$ref": "#/definitions/RelationshipChangeIdString" + "$ref": "#/definitions/RelationshipIdString" } ] } @@ -3071,10 +3071,6 @@ export const CompleteIncomingRequestRequest: any = { "MessageIdString": { "type": "string", "pattern": "MSG[A-Za-z0-9]{17}" - }, - "RelationshipChangeIdString": { - "type": "string", - "pattern": "RCH[A-Za-z0-9]{17}" } } } @@ -5422,7 +5418,7 @@ export const CreateAndCompleteOutgoingRequestFromRelationshipTemplateResponseReq "responseSourceId": { "anyOf": [ { - "$ref": "#/definitions/RelationshipChangeIdString" + "$ref": "#/definitions/RelationshipIdString" }, { "$ref": "#/definitions/MessageIdString" @@ -5444,10 +5440,6 @@ export const CreateAndCompleteOutgoingRequestFromRelationshipTemplateResponseReq "type": "string", "pattern": "RLT[A-Za-z0-9]{17}" }, - "RelationshipChangeIdString": { - "type": "string", - "pattern": "RCH[A-Za-z0-9]{17}" - }, "MessageIdString": { "type": "string", "pattern": "MSG[A-Za-z0-9]{17}" @@ -21374,25 +21366,19 @@ export const SendMessageRequest: any = { } } -export const AcceptRelationshipChangeRequest: any = { +export const AcceptRelationshipRequest: any = { "$schema": "http://json-schema.org/draft-07/schema#", - "$ref": "#/definitions/AcceptRelationshipChangeRequest", + "$ref": "#/definitions/AcceptRelationshipRequest", "definitions": { - "AcceptRelationshipChangeRequest": { + "AcceptRelationshipRequest": { "type": "object", "properties": { "relationshipId": { "$ref": "#/definitions/RelationshipIdString" }, - "changeId": { - "$ref": "#/definitions/RelationshipChangeIdString" - }, - "content": {} }, "required": [ - "relationshipId", - "changeId", - "content" + "relationshipId" ], "additionalProperties": false }, @@ -21400,10 +21386,6 @@ export const AcceptRelationshipChangeRequest: any = { "type": "string", "pattern": "REL[A-Za-z0-9]{17}" }, - "RelationshipChangeIdString": { - "type": "string", - "pattern": "RCH[A-Za-z0-9]{17}" - } } } @@ -21417,11 +21399,11 @@ export const CreateRelationshipRequest: any = { "templateId": { "$ref": "#/definitions/RelationshipTemplateIdString" }, - "content": {} + "creationContent": {} }, "required": [ "templateId", - "content" + "creationContent" ], "additionalProperties": false }, @@ -21569,68 +21551,48 @@ export const GetRelationshipsRequest: any = { } } -export const RejectRelationshipChangeRequest: any = { +export const RejectRelationshipRequest: any = { "$schema": "http://json-schema.org/draft-07/schema#", - "$ref": "#/definitions/RejectRelationshipChangeRequest", + "$ref": "#/definitions/RejectRelationshipRequest", "definitions": { - "RejectRelationshipChangeRequest": { + "RejectRelationshipRequest": { "type": "object", "properties": { "relationshipId": { "$ref": "#/definitions/RelationshipIdString" - }, - "changeId": { - "$ref": "#/definitions/RelationshipChangeIdString" - }, - "content": {} + } }, "required": [ - "relationshipId", - "changeId", - "content" + "relationshipId" ], "additionalProperties": false }, "RelationshipIdString": { "type": "string", "pattern": "REL[A-Za-z0-9]{17}" - }, - "RelationshipChangeIdString": { - "type": "string", - "pattern": "RCH[A-Za-z0-9]{17}" } } } -export const RevokeRelationshipChangeRequest: any = { +export const RevokeRelationshipRequest: any = { "$schema": "http://json-schema.org/draft-07/schema#", - "$ref": "#/definitions/RevokeRelationshipChangeRequest", + "$ref": "#/definitions/RevokeRelationshipRequest", "definitions": { - "RevokeRelationshipChangeRequest": { + "RevokeRelationshipRequest": { "type": "object", "properties": { "relationshipId": { "$ref": "#/definitions/RelationshipIdString" - }, - "changeId": { - "$ref": "#/definitions/RelationshipChangeIdString" - }, - "content": {} + } }, "required": [ - "relationshipId", - "changeId", - "content" + "relationshipId" ], "additionalProperties": false }, "RelationshipIdString": { "type": "string", "pattern": "REL[A-Za-z0-9]{17}" - }, - "RelationshipChangeIdString": { - "type": "string", - "pattern": "RCH[A-Za-z0-9]{17}" } } } diff --git a/packages/runtime/src/useCases/common/validation/ValidatableStrings.ts b/packages/runtime/src/useCases/common/validation/ValidatableStrings.ts index af48ed7fa..8d3c35819 100644 --- a/packages/runtime/src/useCases/common/validation/ValidatableStrings.ts +++ b/packages/runtime/src/useCases/common/validation/ValidatableStrings.ts @@ -33,11 +33,6 @@ export type LocalDraftIdString = string; */ export type LocalSettingIdString = string; -/** - * @pattern RCH[A-Za-z0-9]{17} - */ -export type RelationshipChangeIdString = string; - /** * @pattern MSG[A-Za-z0-9]{17} */ diff --git a/packages/runtime/src/useCases/consumption/requests/CompleteIncomingRequest.ts b/packages/runtime/src/useCases/consumption/requests/CompleteIncomingRequest.ts index 593270a46..3f80156c6 100644 --- a/packages/runtime/src/useCases/consumption/requests/CompleteIncomingRequest.ts +++ b/packages/runtime/src/useCases/consumption/requests/CompleteIncomingRequest.ts @@ -1,14 +1,14 @@ import { ApplicationError, Result } from "@js-soft/ts-utils"; import { IncomingRequestsController } from "@nmshd/consumption"; -import { CoreId, IMessage, IRelationshipChange, Message, MessageController, RelationshipChange, RelationshipsController } from "@nmshd/transport"; +import { CoreId, IMessage, IRelationship, Message, MessageController, Relationship, RelationshipsController } from "@nmshd/transport"; import { Inject } from "typescript-ioc"; import { LocalRequestDTO } from "../../../types"; -import { MessageIdString, RelationshipChangeIdString, RequestIdString, RuntimeErrors, UseCase } from "../../common"; +import { MessageIdString, RelationshipIdString, RequestIdString, RuntimeErrors, UseCase } from "../../common"; import { RequestMapper } from "./RequestMapper"; export interface CompleteIncomingRequestRequest { requestId: RequestIdString; - responseSourceId?: MessageIdString | RelationshipChangeIdString; + responseSourceId?: MessageIdString | RelationshipIdString; } // class Validator extends SchemaValidator { @@ -33,7 +33,7 @@ export class CompleteIncomingRequestUseCase extends UseCase { + private async getResponseSourceObject(request: CompleteIncomingRequestRequest): Promise { if (!request.responseSourceId) return; if (request.responseSourceId.startsWith("MSG")) { @@ -43,11 +43,11 @@ export class CompleteIncomingRequestUseCase extends UseCase { +class Validator extends SchemaValidator { public constructor(@Inject schemaRepository: SchemaRepository) { - super(schemaRepository.getSchema("AcceptRelationshipChangeRequest")); + super(schemaRepository.getSchema("AcceptRelationshipRequest")); } } -export class AcceptRelationshipChangeUseCase extends UseCase { +export class AcceptRelationshipUseCase extends UseCase { public constructor( @Inject private readonly relationshipsController: RelationshipsController, @Inject private readonly accountController: AccountController, @@ -26,7 +24,7 @@ export class AcceptRelationshipChangeUseCase extends UseCase> { + protected async executeInternal(request: AcceptRelationshipRequest): Promise> { const relationship = await this.relationshipsController.getRelationship(CoreId.from(request.relationshipId)); if (!relationship) { return Result.fail(RuntimeErrors.general.recordNotFound(Relationship)); @@ -36,12 +34,7 @@ export class AcceptRelationshipChangeUseCase extends UseCase c.id.toString() === request.changeId); - if (!change) { - return Result.fail(RuntimeErrors.general.recordNotFound(RelationshipChange)); - } - - const updatedRelationship = await this.relationshipsController.acceptChange(change, request.content); + const updatedRelationship = await this.relationshipsController.accept(relationship.id); await this.accountController.syncDatawallet(); diff --git a/packages/runtime/src/useCases/transport/relationships/CreateRelationship.ts b/packages/runtime/src/useCases/transport/relationships/CreateRelationship.ts index b38b452ed..18e60b954 100644 --- a/packages/runtime/src/useCases/transport/relationships/CreateRelationship.ts +++ b/packages/runtime/src/useCases/transport/relationships/CreateRelationship.ts @@ -7,7 +7,7 @@ import { RelationshipMapper } from "./RelationshipMapper"; export interface CreateRelationshipRequest { templateId: RelationshipTemplateIdString; - content: any; + creationContent: any; } class Validator extends SchemaValidator { @@ -34,7 +34,7 @@ export class CreateRelationshipUseCase extends UseCase { +class Validator extends SchemaValidator { public constructor(@Inject schemaRepository: SchemaRepository) { - super(schemaRepository.getSchema("RejectRelationshipChangeRequest")); + super(schemaRepository.getSchema("RejectRelationshipRequest")); } } -export class RejectRelationshipChangeUseCase extends UseCase { +export class RejectRelationshipUseCase extends UseCase { public constructor( @Inject private readonly relationshipsController: RelationshipsController, @Inject private readonly accountController: AccountController, @@ -26,7 +24,7 @@ export class RejectRelationshipChangeUseCase extends UseCase> { + protected async executeInternal(request: RejectRelationshipRequest): Promise> { const relationship = await this.relationshipsController.getRelationship(CoreId.from(request.relationshipId)); if (!relationship) { return Result.fail(RuntimeErrors.general.recordNotFound(Relationship)); @@ -36,12 +34,7 @@ export class RejectRelationshipChangeUseCase extends UseCase c.id.toString() === request.changeId); - if (!change) { - return Result.fail(RuntimeErrors.general.recordNotFound(RelationshipChange)); - } - - const updatedRelationship = await this.relationshipsController.rejectChange(change, request.content); + const updatedRelationship = await this.relationshipsController.reject(relationship.id); await this.accountController.syncDatawallet(); diff --git a/packages/runtime/src/useCases/transport/relationships/RelationshipMapper.ts b/packages/runtime/src/useCases/transport/relationships/RelationshipMapper.ts index 685e4cff6..5ecdaab15 100644 --- a/packages/runtime/src/useCases/transport/relationships/RelationshipMapper.ts +++ b/packages/runtime/src/useCases/transport/relationships/RelationshipMapper.ts @@ -1,5 +1,5 @@ -import { Relationship, RelationshipChange, RelationshipChangeRequest, RelationshipChangeResponse } from "@nmshd/transport"; -import { RelationshipChangeDTO, RelationshipChangeRequestDTO, RelationshipChangeResponseDTO, RelationshipDTO } from "../../../types"; +import { Relationship, RelationshipAuditLogEntry } from "@nmshd/transport"; +import { RelationshipAuditLogEntryDTO, RelationshipAuditLogEntryReason, RelationshipChangeStatus, RelationshipChangeType, RelationshipDTO } from "../../../types"; import { RuntimeErrors } from "../../common"; import { RelationshipTemplateMapper } from "../relationshipTemplates/RelationshipTemplateMapper"; @@ -19,39 +19,49 @@ export class RelationshipMapper { publicKey: relationship.peer.publicKey.toBase64(false), realm: relationship.peer.realm }, - changes: relationship.cache.changes.map((c) => this.toRelationshipChangeDTO(c)) + auditLog: relationship.cache.auditLog.map((entry) => this.toAuditLogEntryDTO(entry)), + creationContent: relationship.cache.creationContent?.toJSON(), + changes: [ + { + id: "RCH00000000000000001", + request: { + createdAt: relationship.cache.auditLog[0].createdAt.toString(), + createdBy: relationship.cache.auditLog[0].createdBy.toString(), + createdByDevice: relationship.cache.auditLog[0].createdByDevice.toString(), + content: { ...relationship.cache.creationContent?.toJSON(), "@type": "RelationshipCreationChangeRequestContent" } + }, + status: this.getStatus(relationship.cache.auditLog), + type: RelationshipChangeType.Creation + } + ] }; } - public static toRelationshipDTOList(relationships: Relationship[]): RelationshipDTO[] { - return relationships.map((r) => this.toRelationshipDTO(r)); - } - - private static toRelationshipChangeRequestDTO(change: RelationshipChangeRequest): RelationshipChangeRequestDTO { - return { - createdBy: change.createdBy.toString(), - createdByDevice: change.createdByDevice.toString(), - createdAt: change.createdAt.toString(), - content: change.content?.toJSON() - }; + private static getStatus(auditLog: RelationshipAuditLogEntry[]): RelationshipChangeStatus { + switch (auditLog[1]?.reason) { + case RelationshipAuditLogEntryReason.AcceptanceOfCreation: + return RelationshipChangeStatus.Accepted; + case RelationshipAuditLogEntryReason.RejectionOfCreation: + return RelationshipChangeStatus.Rejected; + case RelationshipAuditLogEntryReason.RevocationOfCreation: + return RelationshipChangeStatus.Revoked; + default: + return RelationshipChangeStatus.Pending; + } } - private static toRelationshipChangeResponseDTO(change: RelationshipChangeResponse): RelationshipChangeResponseDTO { + private static toAuditLogEntryDTO(entry: RelationshipAuditLogEntry): RelationshipAuditLogEntryDTO { return { - createdBy: change.createdBy.toString(), - createdByDevice: change.createdByDevice.toString(), - createdAt: change.createdAt.toString(), - content: change.content?.toJSON() + createdAt: entry.createdAt.toString(), + createdBy: entry.createdBy.toString(), + createdByDevice: entry.createdByDevice.toString(), + reason: entry.reason, + oldStatus: entry.oldStatus, + newStatus: entry.newStatus }; } - private static toRelationshipChangeDTO(change: RelationshipChange): RelationshipChangeDTO { - return { - id: change.id.toString(), - request: this.toRelationshipChangeRequestDTO(change.request), - status: change.status, - type: change.type, - response: change.response ? this.toRelationshipChangeResponseDTO(change.response) : undefined - }; + public static toRelationshipDTOList(relationships: Relationship[]): RelationshipDTO[] { + return relationships.map((r) => this.toRelationshipDTO(r)); } } diff --git a/packages/runtime/src/useCases/transport/relationships/RevokeRelationshipChange.ts b/packages/runtime/src/useCases/transport/relationships/RevokeRelationship.ts similarity index 53% rename from packages/runtime/src/useCases/transport/relationships/RevokeRelationshipChange.ts rename to packages/runtime/src/useCases/transport/relationships/RevokeRelationship.ts index a562ed523..115e2e0f1 100644 --- a/packages/runtime/src/useCases/transport/relationships/RevokeRelationshipChange.ts +++ b/packages/runtime/src/useCases/transport/relationships/RevokeRelationship.ts @@ -1,23 +1,21 @@ import { Result } from "@js-soft/ts-utils"; -import { AccountController, CoreId, Relationship, RelationshipChange, RelationshipsController } from "@nmshd/transport"; +import { AccountController, CoreId, Relationship, RelationshipsController } from "@nmshd/transport"; import { Inject } from "typescript-ioc"; import { RelationshipDTO } from "../../../types"; -import { RelationshipChangeIdString, RelationshipIdString, RuntimeErrors, SchemaRepository, SchemaValidator, UseCase } from "../../common"; +import { RelationshipIdString, RuntimeErrors, SchemaRepository, SchemaValidator, UseCase } from "../../common"; import { RelationshipMapper } from "./RelationshipMapper"; -export interface RevokeRelationshipChangeRequest { +export interface RevokeRelationshipRequest { relationshipId: RelationshipIdString; - changeId: RelationshipChangeIdString; - content: any; } -class Validator extends SchemaValidator { +class Validator extends SchemaValidator { public constructor(@Inject schemaRepository: SchemaRepository) { - super(schemaRepository.getSchema("RevokeRelationshipChangeRequest")); + super(schemaRepository.getSchema("RevokeRelationshipRequest")); } } -export class RevokeRelationshipChangeUseCase extends UseCase { +export class RevokeRelationshipUseCase extends UseCase { public constructor( @Inject private readonly relationshipsController: RelationshipsController, @Inject private readonly accountController: AccountController, @@ -26,7 +24,7 @@ export class RevokeRelationshipChangeUseCase extends UseCase> { + protected async executeInternal(request: RevokeRelationshipRequest): Promise> { const relationship = await this.relationshipsController.getRelationship(CoreId.from(request.relationshipId)); if (!relationship) { return Result.fail(RuntimeErrors.general.recordNotFound(Relationship)); @@ -36,12 +34,7 @@ export class RevokeRelationshipChangeUseCase extends UseCase c.id.toString() === request.changeId); - if (!change) { - return Result.fail(RuntimeErrors.general.recordNotFound(RelationshipChange)); - } - - const updatedRelationship = await this.relationshipsController.revokeChange(change, request.content); + const updatedRelationship = await this.relationshipsController.revoke(relationship.id); await this.accountController.syncDatawallet(); diff --git a/packages/runtime/src/useCases/transport/relationships/index.ts b/packages/runtime/src/useCases/transport/relationships/index.ts index 3203254e0..4335a1a5d 100644 --- a/packages/runtime/src/useCases/transport/relationships/index.ts +++ b/packages/runtime/src/useCases/transport/relationships/index.ts @@ -1,9 +1,9 @@ -export * from "./AcceptRelationshipChange"; +export * from "./AcceptRelationship"; export * from "./CreateRelationship"; export * from "./GetAttributesForRelationship"; export * from "./GetRelationship"; export * from "./GetRelationshipByAddress"; export * from "./GetRelationships"; -export * from "./RejectRelationshipChange"; +export * from "./RejectRelationship"; export * from "./RelationshipMapper"; -export * from "./RevokeRelationshipChange"; +export * from "./RevokeRelationship"; diff --git a/packages/runtime/test/consumption/requests.test.ts b/packages/runtime/test/consumption/requests.test.ts index 87c6e30f1..5a0476cee 100644 --- a/packages/runtime/test/consumption/requests.test.ts +++ b/packages/runtime/test/consumption/requests.test.ts @@ -1,12 +1,11 @@ import { EventBus } from "@js-soft/ts-utils"; import { LocalRequestStatus } from "@nmshd/consumption"; -import { RelationshipCreationChangeRequestContentJSON } from "@nmshd/content"; import { CoreDate } from "@nmshd/transport"; import { ConsumptionServices, CreateOutgoingRequestRequest, OutgoingRequestCreatedEvent, - OutgoingRequestFromRelationshipCreationChangeCreatedAndCompletedEvent, + OutgoingRequestFromRelationshipCreationCreatedAndCompletedEvent, OutgoingRequestStatusChangedEvent, TransportServices } from "../../src"; @@ -556,7 +555,7 @@ describe("Requests", () => { const result = await rConsumptionServices.incomingRequests.complete({ requestId: request.id, - responseSourceId: action === "Accept" ? relationship?.changes[0].id : undefined + responseSourceId: action === "Accept" ? relationship?.id : undefined }); expect(result).toBeSuccessful(); @@ -577,17 +576,17 @@ describe("Requests", () => { expect(syncResult).toHaveLength(1); - const sRelationshipChange = syncResult[0].changes[0]; + const sRelationship = syncResult[0]; // eslint-disable-next-line @typescript-eslint/no-unused-vars - let triggeredCompletionEvent: OutgoingRequestFromRelationshipCreationChangeCreatedAndCompletedEvent | undefined; - sEventBus.subscribeOnce(OutgoingRequestFromRelationshipCreationChangeCreatedAndCompletedEvent, (event) => { + let triggeredCompletionEvent: OutgoingRequestFromRelationshipCreationCreatedAndCompletedEvent | undefined; + sEventBus.subscribeOnce(OutgoingRequestFromRelationshipCreationCreatedAndCompletedEvent, (event) => { triggeredCompletionEvent = event; }); const completionResult = await sConsumptionServices.outgoingRequests.createAndCompleteFromRelationshipTemplateResponse({ - responseSourceId: sRelationshipChange.id, - response: (sRelationshipChange.request.content as RelationshipCreationChangeRequestContentJSON).response, + responseSourceId: sRelationship.id, + response: sRelationship.creationContent.response, templateId: relationship!.template.id }); diff --git a/packages/runtime/test/dataViews/RelationshipDVO.test.ts b/packages/runtime/test/dataViews/RelationshipDVO.test.ts index 709dc5856..5a641d429 100644 --- a/packages/runtime/test/dataViews/RelationshipDVO.test.ts +++ b/packages/runtime/test/dataViews/RelationshipDVO.test.ts @@ -65,30 +65,13 @@ describe("RelationshipDVO", () => { expect(dvo.name).toBe(dto.peer.substring(3, 9)); expect(dvo.description).toBe("i18n://dvo.relationship.Active"); expect(dvo.type).toBe("IdentityDVO"); - expect(dvo.date).toBe(dto.changes[0].request.createdAt); + expect(dvo.isSelf).toBe(false); expect(dvo.relationship!.id).toBe(dto.id); expect(dvo.relationship!.direction).toBe("Incoming"); expect(dvo.relationship!.status).toBe("Active"); expect(dvo.relationship!.statusText).toBe("i18n://dvo.relationship.Active"); - expect(dvo.relationship!.changeCount).toBe(1); - const change = dvo.relationship!.changes[0]; - expect(change.id).toBe(dto.changes[0].id); - expect(change.type).toBe("RelationshipChangeDVO"); - expect(change.status).toBe(dto.changes[0].status); - expect(change.statusText).toBe("i18n://dvo.relationshipChange.Accepted"); - expect(change.request.type).toBe("RelationshipChangeRequestDVO"); - expect(change.request.createdAt).toBe(dto.changes[0].request.createdAt); - expect(change.request.createdBy).toBe(dto.changes[0].request.createdBy); - expect(change.request.createdByDevice).toBe(dto.changes[0].request.createdByDevice); - expect(change.request.content).toBe(dto.changes[0].request.content); - expect(change.isOwn).toBe(false); - expect(change.response!.type).toBe("RelationshipChangeResponseDVO"); - expect(change.response!.createdAt).toBe(dto.changes[0].response!.createdAt); - expect(change.response!.createdBy).toBe(dto.changes[0].response!.createdBy); - expect(change.response!.createdByDevice).toBe(dto.changes[0].response!.createdByDevice); - expect(change.response!.content).toBe(dto.changes[0].response!.content); - expect(change.date).toBe(dto.changes[0].response!.createdAt); + expect(dvo.relationship!.templateId).toBe(dto.template.id); }); test("check the relationship dvo for the requestor", async () => { @@ -101,30 +84,13 @@ describe("RelationshipDVO", () => { expect(dvo.name).toBe(dto.peer.substring(3, 9)); expect(dvo.description).toBe("i18n://dvo.relationship.Active"); expect(dvo.type).toBe("IdentityDVO"); - expect(dvo.date).toBe(dto.changes[0].request.createdAt); + expect(dvo.isSelf).toBe(false); expect(dvo.relationship!.id).toBe(dto.id); expect(dvo.relationship!.direction).toBe("Outgoing"); expect(dvo.relationship!.status).toBe("Active"); expect(dvo.relationship!.statusText).toBe("i18n://dvo.relationship.Active"); - expect(dvo.relationship!.changeCount).toBe(1); - const change = dvo.relationship!.changes[0]; - expect(change.id).toBe(dto.changes[0].id); - expect(change.type).toBe("RelationshipChangeDVO"); - expect(change.status).toBe(dto.changes[0].status); - expect(change.statusText).toBe("i18n://dvo.relationshipChange.Accepted"); - expect(change.request.type).toBe("RelationshipChangeRequestDVO"); - expect(change.request.createdAt).toBe(dto.changes[0].request.createdAt); - expect(change.request.createdBy).toBe(dto.changes[0].request.createdBy); - expect(change.request.createdByDevice).toBe(dto.changes[0].request.createdByDevice); - expect(change.request.content).toBe(dto.changes[0].request.content); - expect(change.isOwn).toBe(true); - expect(change.response!.type).toBe("RelationshipChangeResponseDVO"); - expect(change.response!.createdAt).toBe(dto.changes[0].response!.createdAt); - expect(change.response!.createdBy).toBe(dto.changes[0].response!.createdBy); - expect(change.response!.createdByDevice).toBe(dto.changes[0].response!.createdByDevice); - expect(change.response!.content).toBe(dto.changes[0].response!.content); - expect(change.date).toBe(dto.changes[0].response!.createdAt); + expect(dvo.relationship!.templateId).toBe(dto.template.id); }); }); diff --git a/packages/runtime/test/dataViews/RelationshipTemplateDVO.test.ts b/packages/runtime/test/dataViews/RelationshipTemplateDVO.test.ts index a87608e16..2f40c69b7 100644 --- a/packages/runtime/test/dataViews/RelationshipTemplateDVO.test.ts +++ b/packages/runtime/test/dataViews/RelationshipTemplateDVO.test.ts @@ -12,7 +12,7 @@ import { import { CoreAddress } from "@nmshd/transport"; import { IncomingRequestStatusChangedEvent, - OutgoingRequestFromRelationshipCreationChangeCreatedAndCompletedEvent, + OutgoingRequestFromRelationshipCreationCreatedAndCompletedEvent, PeerRelationshipTemplateDVO, PeerRelationshipTemplateLoadedEvent, RelationshipTemplateDTO, @@ -323,7 +323,7 @@ describe("RelationshipTemplateDVO", () => { expect(attributeResult.value).toHaveLength(4); await syncUntilHasRelationships(templator.transport); - await templator.eventBus.waitForEvent(OutgoingRequestFromRelationshipCreationChangeCreatedAndCompletedEvent); + await templator.eventBus.waitForEvent(OutgoingRequestFromRelationshipCreationCreatedAndCompletedEvent); const requestResultTemplator = await templator.consumption.outgoingRequests.getRequests({ query: { "source.reference": requestorTemplate.id diff --git a/packages/runtime/test/lib/testUtils.ts b/packages/runtime/test/lib/testUtils.ts index a82c6893e..d59f0d61d 100644 --- a/packages/runtime/test/lib/testUtils.ts +++ b/packages/runtime/test/lib/testUtils.ts @@ -8,11 +8,11 @@ import { } from "@nmshd/consumption"; import { INotificationItem, - IRelationshipCreationChangeRequestContent, + IRelationshipCreationContent, IRelationshipTemplateContent, Notification, - RelationshipCreationChangeRequestContent, - RelationshipCreationChangeRequestContentJSON, + RelationshipCreationContent, + RelationshipCreationContentJSON, RelationshipTemplateContent, RelationshipTemplateContentJSON } from "@nmshd/content"; @@ -285,17 +285,15 @@ export async function establishRelationship(transportServices1: TransportService const createRelationshipResponse = await transportServices2.relationships.createRelationship({ templateId: template.id, - content: { a: "b" } + creationContent: { a: "b" } }); expect(createRelationshipResponse).toBeSuccessful(); const relationships = await syncUntilHasRelationships(transportServices1); expect(relationships).toHaveLength(1); - const acceptResponse = await transportServices1.relationships.acceptRelationshipChange({ - relationshipId: relationships[0].id, - changeId: relationships[0].changes[0].id, - content: { a: "b" } + const acceptResponse = await transportServices1.relationships.acceptRelationship({ + relationshipId: relationships[0].id }); expect(acceptResponse).toBeSuccessful(); @@ -308,23 +306,21 @@ export async function establishRelationshipWithContents( transportServices1: TransportServices, transportServices2: TransportServices, templateContent: RelationshipTemplateContentJSON | RelationshipTemplateContent | IRelationshipTemplateContent, - requestContent: RelationshipCreationChangeRequestContentJSON | RelationshipCreationChangeRequestContent | IRelationshipCreationChangeRequestContent + creationContent: RelationshipCreationContentJSON | RelationshipCreationContent | IRelationshipCreationContent ): Promise { const template = await exchangeTemplate(transportServices1, transportServices2, templateContent); const createRelationshipResponse = await transportServices2.relationships.createRelationship({ templateId: template.id, - content: requestContent + creationContent: creationContent }); expect(createRelationshipResponse).toBeSuccessful(); const relationships = await syncUntilHasRelationships(transportServices1); expect(relationships).toHaveLength(1); - const acceptResponse = await transportServices1.relationships.acceptRelationshipChange({ - relationshipId: relationships[0].id, - changeId: relationships[0].changes[0].id, - content: { a: "b" } + const acceptResponse = await transportServices1.relationships.acceptRelationship({ + relationshipId: relationships[0].id }); expect(acceptResponse).toBeSuccessful(); @@ -339,7 +335,7 @@ export async function ensureActiveRelationship(sTransportServices: TransportServ await establishRelationship(sTransportServices, rTransportServices); } else if (relationships[0].status === RelationshipStatus.Pending) { const relationship = relationships[0]; - await sTransportServices.relationships.acceptRelationshipChange({ relationshipId: relationship.id, changeId: relationship.changes[0].id, content: {} }); + await sTransportServices.relationships.acceptRelationship({ relationshipId: relationship.id }); await syncUntilHasRelationships(rTransportServices, 1); } diff --git a/packages/runtime/test/lib/testUtilsWithInactiveModules.ts b/packages/runtime/test/lib/testUtilsWithInactiveModules.ts index 7059b8f0f..ba2e547fc 100644 --- a/packages/runtime/test/lib/testUtilsWithInactiveModules.ts +++ b/packages/runtime/test/lib/testUtilsWithInactiveModules.ts @@ -1,4 +1,4 @@ -import { IResponse, RelationshipCreationChangeRequestContent } from "@nmshd/content"; +import { IResponse, RelationshipCreationContent } from "@nmshd/content"; import { CreateOutgoingRequestRequest, LocalRequestDTO, MessageDTO, RelationshipDTO } from "src"; import { TestRuntimeServices } from "./RuntimeServiceProvider"; import { exchangeMessageWithRequest, exchangeTemplate, syncUntilHasMessageWithResponse } from "./testUtils"; @@ -121,16 +121,16 @@ export async function exchangeTemplateAndReceiverSendsResponse( let relationship; if (actionLowerCase === "accept") { - const content = RelationshipCreationChangeRequestContent.from({ response: decidedRequest.response!.content as unknown as IResponse }); - const result = await rRuntimeServices.transport.relationships.createRelationship({ content, templateId }); + const creationContent = RelationshipCreationContent.from({ response: decidedRequest.response!.content as unknown as IResponse }); + const result = await rRuntimeServices.transport.relationships.createRelationship({ creationContent, templateId }); expect(result).toBeSuccessful(); relationship = result.value; - const rRelationshipChange = result.value.changes[0]; expect(rRelationshipChange.request.content["@type"]).toBe("RelationshipCreationChangeRequestContent"); + expect(relationship.creationContent["@type"]).toBe("RelationshipCreationContent"); } return { request, relationship }; } diff --git a/packages/runtime/test/modules/RequestModule.test.ts b/packages/runtime/test/modules/RequestModule.test.ts index 8e901d4a4..8ad5f40ab 100644 --- a/packages/runtime/test/modules/RequestModule.test.ts +++ b/packages/runtime/test/modules/RequestModule.test.ts @@ -3,6 +3,7 @@ import { GivenName, IdentityAttribute, RelationshipCreationChangeRequestContentJSON, + RelationshipCreationContentJSON, RelationshipTemplateContentJSON, ResponseItemJSON, ResponseItemResult, @@ -17,7 +18,7 @@ import { MessageProcessedEvent, MessageSentEvent, OutgoingRequestCreatedAndCompletedEvent, - OutgoingRequestFromRelationshipCreationChangeCreatedAndCompletedEvent, + OutgoingRequestFromRelationshipCreationCreatedAndCompletedEvent, OutgoingRequestStatusChangedEvent, RelationshipStatus, RelationshipTemplateDTO, @@ -99,10 +100,8 @@ describe("RequestModule", () => { const requestId = (await rConsumptionServices.incomingRequests.getRequests({ query: { "source.reference": template.id } })).value[0].id; await rConsumptionServices.incomingRequests.accept({ requestId, items: [{ accept: true }] }); const relationship = (await syncUntilHasRelationships(sTransportServices, 1))[0]; - await sTransportServices.relationships.acceptRelationshipChange({ - relationshipId: relationship.id, - changeId: relationship.changes[0].id, - content: {} + await sTransportServices.relationships.acceptRelationship({ + relationshipId: relationship.id }); } } @@ -183,10 +182,13 @@ describe("RequestModule", () => { const relationship = relationships[0]; + const creationContent = relationship.creationContent as RelationshipCreationContentJSON; + expect(creationContent["@type"]).toBe("RelationshipCreationContent"); + const creationChangeRequestContent = relationship.changes[0].request.content as RelationshipCreationChangeRequestContentJSON; expect(creationChangeRequestContent["@type"]).toBe("RelationshipCreationChangeRequestContent"); - const response = creationChangeRequestContent.response; + const response = creationContent.response; const responseItems = response.items; expect(responseItems).toHaveLength(1); @@ -194,12 +196,12 @@ describe("RequestModule", () => { expect(responseItem["@type"]).toBe("AcceptResponseItem"); expect(responseItem.result).toBe(ResponseItemResult.Accepted); - await expect(sEventBus).toHavePublished(OutgoingRequestFromRelationshipCreationChangeCreatedAndCompletedEvent, (e) => e.data.id === response.requestId); + await expect(sEventBus).toHavePublished(OutgoingRequestFromRelationshipCreationCreatedAndCompletedEvent, (e) => e.data.id === response.requestId); const requestsResult = await sConsumptionServices.outgoingRequests.getRequest({ id: response.requestId }); expect(requestsResult).toBeSuccessful(); - await sTransportServices.relationships.acceptRelationshipChange({ relationshipId: relationship.id, changeId: relationship.changes[0].id, content: {} }); + await sTransportServices.relationships.acceptRelationship({ relationshipId: relationship.id }); await syncUntilHasRelationships(rTransportServices, 1); }); diff --git a/packages/runtime/test/transport/relationships.test.ts b/packages/runtime/test/transport/relationships.test.ts index f15f29692..034f46e42 100644 --- a/packages/runtime/test/transport/relationships.test.ts +++ b/packages/runtime/test/transport/relationships.test.ts @@ -39,19 +39,16 @@ describe("Create Relationship", () => { const createRelationshipResponse = await services2.transport.relationships.createRelationship({ templateId: templateId, - content: { a: "b" } + creationContent: { a: "b" } }); expect(createRelationshipResponse).toBeSuccessful(); const relationships1 = await syncUntilHasRelationships(services1.transport); expect(relationships1).toHaveLength(1); const relationshipId = relationships1[0].id; - const relationshipChangeId = relationships1[0].changes[0].id; - const acceptRelationshipResponse = await services1.transport.relationships.acceptRelationshipChange({ - relationshipId: relationshipId, - changeId: relationshipChangeId, - content: { a: "b" } + const acceptRelationshipResponse = await services1.transport.relationships.acceptRelationship({ + relationshipId }); expect(acceptRelationshipResponse).toBeSuccessful(); diff --git a/packages/transport/src/core/CoreErrors.ts b/packages/transport/src/core/CoreErrors.ts index df1831c10..f95a0ad61 100644 --- a/packages/transport/src/core/CoreErrors.ts +++ b/packages/transport/src/core/CoreErrors.ts @@ -1,10 +1,10 @@ import stringify from "json-stringify-safe"; -import { RelationshipChangeStatus } from "../modules/relationships/transmission/changes/RelationshipChangeStatus"; +import { RelationshipStatus } from "../modules"; import { CoreError } from "./CoreError"; class Relationships { - public wrongChangeStatus(status: RelationshipChangeStatus) { - return new CoreError("error.transport.relationships.wrongChangeStatus", `The relationship change has the wrong status (${status}) to run this operation`); + public wrongRelationshipStatus(status: RelationshipStatus) { + return new CoreError("error.transport.relationships.wrongRelationshipStatus", `The relationship has the wrong status (${status}) to run this operation`); } } diff --git a/packages/transport/src/core/backbone/BackboneIds.ts b/packages/transport/src/core/backbone/BackboneIds.ts index 4817cf031..7d8f30757 100644 --- a/packages/transport/src/core/backbone/BackboneIds.ts +++ b/packages/transport/src/core/backbone/BackboneIds.ts @@ -6,6 +6,5 @@ export class BackboneIds { public static readonly message = new CoreIdHelper("MSG", true); public static readonly relationshipTemplate = new CoreIdHelper("RLT", true); public static readonly token = new CoreIdHelper("TOK", true); - public static readonly relationshipChange = new CoreIdHelper("RCH", true); public static readonly device = new CoreIdHelper("DVC", true); } diff --git a/packages/transport/src/modules/devices/local/Device.ts b/packages/transport/src/modules/devices/local/Device.ts index d688fcf6b..a940d37aa 100644 --- a/packages/transport/src/modules/devices/local/Device.ts +++ b/packages/transport/src/modules/devices/local/Device.ts @@ -88,7 +88,9 @@ export class Device extends CoreSynchronizable implements IDevice { @serialize() public lastLoginAt?: CoreDate; - @validate() + @validate({ + customValidator: (v) => (!Object.values(DeviceType).includes(v) ? `must be one of: ${Object.values(DeviceType)}` : undefined) + }) @serialize() public type: DeviceType; diff --git a/packages/transport/src/modules/index.ts b/packages/transport/src/modules/index.ts index 8ae9cb5e5..706b7e84c 100644 --- a/packages/transport/src/modules/index.ts +++ b/packages/transport/src/modules/index.ts @@ -57,26 +57,23 @@ export * from "./messages/transmission/MessageEnvelopeRecipient"; export * from "./messages/transmission/MessageSignature"; export * from "./messages/transmission/MessageSigned"; export * from "./relationships/backbone/BackboneGetRelationships"; -export * from "./relationships/backbone/BackboneGetRelationshipsChanges"; -export * from "./relationships/backbone/BackbonePostRelationshipsChanges"; +export * from "./relationships/backbone/BackbonePostRelationship"; export * from "./relationships/backbone/RelationshipClient"; export * from "./relationships/local/CachedRelationship"; export * from "./relationships/local/Relationship"; +export * from "./relationships/local/RelationshipAuditLog"; +export * from "./relationships/local/RelationshipAuditLogEntry"; export * from "./relationships/local/SendRelationshipParameters"; export * from "./relationships/RelationshipsController"; export * from "./relationships/RelationshipSecretController"; -export * from "./relationships/transmission/changes/RelationshipChange"; -export * from "./relationships/transmission/changes/RelationshipChangeRequest"; -export * from "./relationships/transmission/changes/RelationshipChangeResponse"; -export * from "./relationships/transmission/changes/RelationshipChangeStatus"; -export * from "./relationships/transmission/changes/RelationshipChangeType"; +export * from "./relationships/transmission/RelationshipAuditLog"; export * from "./relationships/transmission/RelationshipStatus"; -export * from "./relationships/transmission/requests/RelationshipCreationChangeRequestCipher"; -export * from "./relationships/transmission/requests/RelationshipCreationChangeRequestContentWrapper"; -export * from "./relationships/transmission/requests/RelationshipCreationChangeRequestSigned"; -export * from "./relationships/transmission/responses/RelationshipCreationChangeResponseCipher"; -export * from "./relationships/transmission/responses/RelationshipCreationChangeResponseContentWrapper"; -export * from "./relationships/transmission/responses/RelationshipCreationChangeResponseSigned"; +export * from "./relationships/transmission/requests/RelationshipCreationContentCipher"; +export * from "./relationships/transmission/requests/RelationshipCreationContentSigned"; +export * from "./relationships/transmission/requests/RelationshipCreationContentWrapper"; +export * from "./relationships/transmission/responses/RelationshipCreationResponseContentCipher"; +export * from "./relationships/transmission/responses/RelationshipCreationResponseContentSigned"; +export * from "./relationships/transmission/responses/RelationshipCreationResponseContentWrapper"; export * from "./relationshipTemplates/backbone/BackboneGetRelationshipTemplates"; export * from "./relationshipTemplates/backbone/BackbonePostRelationshipTemplates"; export * from "./relationshipTemplates/backbone/RelationshipTemplateClient"; diff --git a/packages/transport/src/modules/relationships/RelationshipSecretController.ts b/packages/transport/src/modules/relationships/RelationshipSecretController.ts index 4ca286da7..8db400c60 100644 --- a/packages/transport/src/modules/relationships/RelationshipSecretController.ts +++ b/packages/transport/src/modules/relationships/RelationshipSecretController.ts @@ -88,7 +88,7 @@ export class RelationshipSecretController extends SecretController { } @log() - public async getPublicResponse(relationshipSecretId: CoreId): Promise { + public async getPublicCreationResponseContentCrypto(relationshipSecretId: CoreId): Promise { const secret = await this.loadActiveSecretByName(relationshipSecretId.toString()); if (!secret) { throw CoreErrors.general.recordNotFound(CryptoRelationshipSecrets, relationshipSecretId.toString()); @@ -134,7 +134,7 @@ export class RelationshipSecretController extends SecretController { } @log() - public async encryptRequest(relationshipSecretId: CoreId, content: Serializable | string | CoreBuffer): Promise { + public async encryptCreationContent(relationshipSecretId: CoreId, content: Serializable | string | CoreBuffer): Promise { const buffer = CoreUtil.toBuffer(content); const secrets = await this.getSecret(relationshipSecretId); @@ -158,7 +158,7 @@ export class RelationshipSecretController extends SecretController { } @log() - public async decryptRequest(relationshipSecretId: CoreId, cipher: CryptoCipher): Promise { + public async decryptCreationContent(relationshipSecretId: CoreId, cipher: CryptoCipher): Promise { const secrets = await this.getSecret(relationshipSecretId); if (!(secrets instanceof CryptoRelationshipRequestSecrets) && !(secrets instanceof CryptoRelationshipSecrets)) { diff --git a/packages/transport/src/modules/relationships/RelationshipsController.ts b/packages/transport/src/modules/relationships/RelationshipsController.ts index 412896634..a0f605bd5 100644 --- a/packages/transport/src/modules/relationships/RelationshipsController.ts +++ b/packages/transport/src/modules/relationships/RelationshipsController.ts @@ -2,7 +2,7 @@ import { ISerializable } from "@js-soft/ts-serval"; import { log } from "@js-soft/ts-utils"; import { CoreBuffer, CryptoSignature } from "@nmshd/crypto"; import { nameof } from "ts-simple-nameof"; -import { ControllerName, CoreAddress, CoreCrypto, CoreDate, CoreId, ICoreSerializable, TransportController, TransportError } from "../../core"; +import { ControllerName, CoreAddress, CoreCrypto, CoreDate, CoreId, TransportController, TransportError } from "../../core"; import { CoreErrors } from "../../core/CoreErrors"; import { CoreUtil } from "../../core/CoreUtil"; import { DbCollectionName } from "../../core/DbCollectionName"; @@ -12,24 +12,21 @@ import { AccountController } from "../accounts/AccountController"; import { Identity } from "../accounts/data/Identity"; import { RelationshipTemplate } from "../relationshipTemplates/local/RelationshipTemplate"; import { SynchronizedCollection } from "../sync/SynchronizedCollection"; -import { BackboneGetRelationshipsResponse } from "./backbone/BackboneGetRelationships"; -import { BackboneGetRelationshipsChangesResponse, BackboneGetRelationshipsChangesSingleChangeResponse } from "./backbone/BackboneGetRelationshipsChanges"; +import { BackbonePutRelationshipsResponse } from "./backbone/BackbonePutRelationship"; +import { BackboneRelationship } from "./backbone/BackboneRelationship"; import { RelationshipClient } from "./backbone/RelationshipClient"; import { CachedRelationship } from "./local/CachedRelationship"; import { Relationship } from "./local/Relationship"; +import { RelationshipAuditLog } from "./local/RelationshipAuditLog"; import { ISendRelationshipParameters, SendRelationshipParameters } from "./local/SendRelationshipParameters"; import { RelationshipSecretController } from "./RelationshipSecretController"; -import { RelationshipChange } from "./transmission/changes/RelationshipChange"; -import { RelationshipChangeResponse } from "./transmission/changes/RelationshipChangeResponse"; -import { RelationshipChangeStatus } from "./transmission/changes/RelationshipChangeStatus"; -import { RelationshipChangeType } from "./transmission/changes/RelationshipChangeType"; import { RelationshipStatus } from "./transmission/RelationshipStatus"; -import { RelationshipCreationChangeRequestCipher } from "./transmission/requests/RelationshipCreationChangeRequestCipher"; -import { RelationshipCreationChangeRequestContentWrapper } from "./transmission/requests/RelationshipCreationChangeRequestContentWrapper"; -import { RelationshipCreationChangeRequestSigned } from "./transmission/requests/RelationshipCreationChangeRequestSigned"; -import { RelationshipCreationChangeResponseCipher } from "./transmission/responses/RelationshipCreationChangeResponseCipher"; -import { RelationshipCreationChangeResponseContentWrapper } from "./transmission/responses/RelationshipCreationChangeResponseContentWrapper"; -import { RelationshipCreationChangeResponseSigned } from "./transmission/responses/RelationshipCreationChangeResponseSigned"; +import { RelationshipCreationContentCipher } from "./transmission/requests/RelationshipCreationContentCipher"; +import { RelationshipCreationContentSigned } from "./transmission/requests/RelationshipCreationContentSigned"; +import { RelationshipCreationContentWrapper } from "./transmission/requests/RelationshipCreationContentWrapper"; +import { RelationshipCreationResponseContentCipher } from "./transmission/responses/RelationshipCreationResponseContentCipher"; +import { RelationshipCreationResponseContentSigned } from "./transmission/responses/RelationshipCreationResponseContentSigned"; +import { RelationshipCreationResponseContentWrapper } from "./transmission/responses/RelationshipCreationResponseContentWrapper"; export class RelationshipsController extends TransportController { private client: RelationshipClient; @@ -52,7 +49,9 @@ export class RelationshipsController extends TransportController { public async getRelationships(query?: any): Promise { const relationshipDocs = await this.relationships.find(query); - return this.parseArray(relationshipDocs, Relationship); + const relationships = this.parseArray(relationshipDocs, Relationship); + + return relationships; } public async updateCache(ids: string[]): Promise { @@ -63,7 +62,7 @@ export class RelationshipsController extends TransportController { const resultItems = (await this.client.getRelationships({ ids })).value; const promises = []; for await (const resultItem of resultItems) { - promises.push(this.updateCacheOfExistingRelationshipInDb(resultItem.id, resultItem)); + promises.push(this.updateExistingRelationshipInDb(resultItem.id, resultItem)); } return await Promise.all(promises); } @@ -87,13 +86,14 @@ export class RelationshipsController extends TransportController { } @log() - private async updateCacheOfExistingRelationshipInDb(id: string, response?: BackboneGetRelationshipsResponse) { + private async updateExistingRelationshipInDb(id: string, response: BackboneRelationship) { const relationshipDoc = await this.relationships.read(id); if (!relationshipDoc) throw CoreErrors.general.recordNotFound(Relationship, id); const relationship = Relationship.from(relationshipDoc); await this.updateCacheOfRelationship(relationship, response); + relationship.status = response.status; await this.relationships.update(relationshipDoc, relationship); return relationship; } @@ -128,7 +128,9 @@ export class RelationshipsController extends TransportController { return; } - return Relationship.from(relationshipDoc); + const relationship = Relationship.from(relationshipDoc); + + return relationship; } public async sign(relationship: Relationship, content: CoreBuffer): Promise { @@ -152,22 +154,16 @@ export class RelationshipsController extends TransportController { const secretId = await TransportIds.relationshipSecret.generate(); - const { requestCipher, requestContent } = await this.prepareRequest(secretId, template, parameters.content); + const creationContentCipher = await this.prepareCreationContent(secretId, template, parameters.creationContent); const backboneResponse = ( await this.client.createRelationship({ - content: requestCipher.toBase64(), + creationContent: creationContentCipher.toBase64(), relationshipTemplateId: template.id.toString() }) ).value; - const newRelationship = Relationship.fromRequestSent( - CoreId.from(backboneResponse.id), - template, - template.cache.identity, - RelationshipChange.fromBackbone(backboneResponse.changes[0], requestContent.content), - secretId - ); + const newRelationship = Relationship.fromBackboneAndCreationContent(backboneResponse, template, template.cache.identity, parameters.creationContent, secretId); await this.relationships.create(newRelationship); @@ -190,19 +186,40 @@ export class RelationshipsController extends TransportController { return relationship; } - public async acceptChange(change: RelationshipChange, content?: ICoreSerializable): Promise { - return await this.completeChange(RelationshipChangeStatus.Accepted, change, content); + public async accept(relationshipId: CoreId): Promise { + const relationship = await this.getRelationship(relationshipId); + if (!relationship) { + throw CoreErrors.general.recordNotFound("Relationship", relationshipId.toString()); + } + if (relationship.status !== RelationshipStatus.Pending) { + throw CoreErrors.relationships.wrongRelationshipStatus(relationship.status); + } + return await this.completeStateTransition(RelationshipStatus.Active, relationshipId); } - public async rejectChange(change: RelationshipChange, content?: ICoreSerializable): Promise { - return await this.completeChange(RelationshipChangeStatus.Rejected, change, content); + public async reject(relationshipId: CoreId): Promise { + const relationship = await this.getRelationship(relationshipId); + if (!relationship) { + throw CoreErrors.general.recordNotFound("Relationship", relationshipId.toString()); + } + if (relationship.status !== RelationshipStatus.Pending) { + throw CoreErrors.relationships.wrongRelationshipStatus(relationship.status); + } + return await this.completeStateTransition(RelationshipStatus.Rejected, relationshipId); } - public async revokeChange(change: RelationshipChange, content?: ICoreSerializable): Promise { - return await this.completeChange(RelationshipChangeStatus.Revoked, change, content); + public async revoke(relationshipId: CoreId): Promise { + const relationship = await this.getRelationship(relationshipId); + if (!relationship) { + throw CoreErrors.general.recordNotFound("Relationship", relationshipId.toString()); + } + if (relationship.status !== RelationshipStatus.Pending) { + throw CoreErrors.relationships.wrongRelationshipStatus(relationship.status); + } + return await this.completeStateTransition(RelationshipStatus.Revoked, relationshipId); } - private async updateCacheOfRelationship(relationship: Relationship, response?: BackboneGetRelationshipsResponse) { + private async updateCacheOfRelationship(relationship: Relationship, response?: BackboneRelationship) { if (!response) { response = (await this.client.getRelationship(relationship.id.toString())).value; } @@ -212,7 +229,9 @@ export class RelationshipsController extends TransportController { relationship.setCache(cachedRelationship); } - private async decryptRelationship(response: BackboneGetRelationshipsResponse, relationshipSecretId: CoreId) { + private async decryptRelationship(response: BackboneRelationship, relationshipSecretId: CoreId) { + if (!response.creationContent) throw new TransportError("Creation content is missing"); + const templateId = CoreId.from(response.relationshipTemplateId); this._log.trace(`Parsing relationship template ${templateId} for ${response.id}...`); @@ -221,299 +240,168 @@ export class RelationshipsController extends TransportController { throw CoreErrors.general.recordNotFound(RelationshipTemplate, templateId.toString()); } - this._log.trace(`Parsing relationship changes of ${response.id}...`); + this._log.trace(`Parsing relationship creation content of ${response.id}...`); - const changesPromises = []; - for (const change of response.changes) { - switch (change.type) { - case RelationshipChangeType.Creation: - changesPromises.push(this.parseCreationChange(change, relationshipSecretId, templateId)); - break; - default: - break; - } - } - - const changes = await Promise.all(changesPromises); + const creationContent = await this.decryptCreationContent(response.creationContent, CoreAddress.from(response.from), relationshipSecretId); const cachedRelationship = CachedRelationship.from({ - changes: changes, - template: template + creationContent: creationContent.content, + template, + auditLog: RelationshipAuditLog.fromBackboneAuditLog(response.auditLog) }); return cachedRelationship; } - private async prepareRequest( - relationshipSecretId: CoreId, - template: RelationshipTemplate, - content: ISerializable - ): Promise<{ - requestCipher: RelationshipCreationChangeRequestCipher; - requestContent: RelationshipCreationChangeRequestContentWrapper; - }> { + private async prepareCreationContent(relationshipSecretId: CoreId, template: RelationshipTemplate, content: ISerializable): Promise { if (!template.cache) { throw this.newCacheEmptyError(RelationshipTemplate, template.id.toString()); } - const requestPublic = await this.secrets.createRequestorSecrets(template.cache, relationshipSecretId); + const publicCreationContentCrypto = await this.secrets.createRequestorSecrets(template.cache, relationshipSecretId); - const requestContent = RelationshipCreationChangeRequestContentWrapper.from({ - content: content, + const creationContent: RelationshipCreationContentWrapper = RelationshipCreationContentWrapper.from({ + content, identity: this.parent.identity.identity, templateId: template.id }); - const serializedRequest = requestContent.serialize(); - const buffer = CoreUtil.toBuffer(serializedRequest); + const serializedCreationContent = creationContent.serialize(); + const buffer = CoreUtil.toBuffer(serializedCreationContent); const [deviceSignature, relationshipSignature] = await Promise.all([this.parent.activeDevice.sign(buffer), this.secrets.sign(relationshipSecretId, buffer)]); - const signedRequest = RelationshipCreationChangeRequestSigned.from({ - serializedRequest: serializedRequest, - deviceSignature: deviceSignature, - relationshipSignature: relationshipSignature + const signedCreationContent = RelationshipCreationContentSigned.from({ + serializedCreationContent, + deviceSignature, + relationshipSignature }); - const cipher = await this.secrets.encryptRequest(relationshipSecretId, signedRequest); - const requestCipher = RelationshipCreationChangeRequestCipher.from({ - cipher: cipher, - publicRequestCrypto: requestPublic + const cipher = await this.secrets.encryptCreationContent(relationshipSecretId, signedCreationContent); + const creationContentCipher = RelationshipCreationContentCipher.from({ + cipher, + publicCreationContentCrypto }); - return { requestCipher, requestContent }; - } - - public async applyChangeById(changeId: string): Promise { - const relationshipChange = (await this.client.getRelationshipChange(changeId.toString())).value; - return await this.applyChange(relationshipChange); - } - - @log() - public async applyChange(change: BackboneGetRelationshipsChangesResponse): Promise { - switch (change.type) { - case RelationshipChangeType.Creation: - return await this.applyCreationChange(change); - case RelationshipChangeType.Termination: - case RelationshipChangeType.TerminationCancellation: - default: - throw CoreErrors.general.notSupported(); - } - } - - private async applyCreationChange(change: BackboneGetRelationshipsChangesResponse): Promise { - const relationshipDoc = await this.relationships.read(change.relationshipId); - if (relationshipDoc) { - // Incoming change from sync might still have an empty response - // This could happen if we've processed the change before (duplicate) - if (change.response) { - return await this.updatePendingRelationshipWithPeerResponse(relationshipDoc, change); - } - - // If we have a relationship already but no response, do nothing - return undefined; - } - - const newRelationship = await this.createNewRelationshipByIncomingCreationChange(change); - - if (change.response) { - // The request was revoked before we fetched the creation, - // thus the creation and revoke change is one - const relationshipDoc = await this.relationships.read(change.relationshipId); - return await this.updatePendingRelationshipWithPeerResponse(relationshipDoc, change); - } - return newRelationship; - } - - @log() - private async parseCreationChange(change: BackboneGetRelationshipsChangesResponse, relationshipSecretId: CoreId, templateId: CoreId) { - if (change.type !== RelationshipChangeType.Creation) this.throwWrongChangeType(change.type); - - const promises: any[] = []; - promises.push(this.decryptCreationChangeRequest(change.request, relationshipSecretId, templateId)); - - const hasRelationshipSecret = await this.secrets.hasCryptoRelationshipSecrets(relationshipSecretId); - - if (change.response && hasRelationshipSecret) { - promises.push(this.decryptCreationChangeResponse(change, relationshipSecretId)); - } - - const [requestContent, responseContent] = await Promise.all(promises); - - const creationChange = RelationshipChange.fromBackbone(change, requestContent.content, responseContent?.content); - return creationChange; + return creationContentCipher; } @log() - private async decryptCreationChangeRequest( - change: BackboneGetRelationshipsChangesSingleChangeResponse, - secretId: CoreId, - templateId: CoreId - ): Promise { - if (!change.content) throw this.newEmptyOrInvalidContentError(); - - const isOwnChange = this.parent.identity.isMe(CoreAddress.from(change.createdBy)); + private async updatePendingRelationshipWithPeerResponse(relationshipDoc: any): Promise { + const relationship = Relationship.from(relationshipDoc); - const requestCipher = RelationshipCreationChangeRequestCipher.fromBase64(change.content); - const signedRequestBuffer = await this.secrets.decryptRequest(secretId, requestCipher.cipher); - const signedRequest = RelationshipCreationChangeRequestSigned.deserialize(signedRequestBuffer.toUtf8()); + const backboneRelationship = (await this.client.getRelationship(relationship.id.toString())).value; - let relationshipSignatureValid; - if (isOwnChange) { - relationshipSignatureValid = await this.secrets.verifyOwn(secretId, CoreBuffer.fromUtf8(signedRequest.serializedRequest), signedRequest.relationshipSignature); - } else { - relationshipSignatureValid = await this.secrets.verifyPeer(secretId, CoreBuffer.fromUtf8(signedRequest.serializedRequest), signedRequest.relationshipSignature); - } - - if (!relationshipSignatureValid) { - throw CoreErrors.general.signatureNotValid("relationshipRequest"); - } + if (!(await this.secrets.hasCryptoRelationshipSecrets(relationship.relationshipSecretId)) && backboneRelationship.creationResponseContent) { + const creationResponseContent = backboneRelationship.creationResponseContent; + const cipher = RelationshipCreationResponseContentCipher.fromBase64(creationResponseContent); - const requestContent = RelationshipCreationChangeRequestContentWrapper.deserialize(signedRequest.serializedRequest); - if (!requestContent.templateId.equals(templateId)) { - throw new TransportError("The relationship request contains a wrong template id."); + await this.secrets.convertSecrets(relationship.relationshipSecretId, cipher.publicCreationResponseContentCrypto); } + relationship.cache!.auditLog = RelationshipAuditLog.fromBackboneAuditLog(backboneRelationship.auditLog); + relationship.status = backboneRelationship.status; - return requestContent; + await this.relationships.update(relationshipDoc, relationship); + return relationship; } @log() - private async decryptCreationChangeResponse( - change: BackboneGetRelationshipsChangesResponse, - relationshipSecretId: CoreId - ): Promise { - if (!change.response) throw this.newChangeResponseMissingError(change.id); - - if (change.type !== RelationshipChangeType.Creation) this.throwWrongChangeType(change.type); - - if (!change.response.content) { - throw this.newEmptyOrInvalidContentError(change); - } + private async decryptCreationContent(backboneCreationContent: string, creationContentCreator: CoreAddress, secretId: CoreId): Promise { + const isOwnContent = this.parent.identity.isMe(creationContentCreator); - const isOwnChange = this.parent.identity.isMe(CoreAddress.from(change.response.createdBy)); - - const cipher = RelationshipCreationChangeResponseCipher.fromBase64(change.response.content); - let signedResponseBuffer; - if (change.status !== RelationshipChangeStatus.Revoked) { - if (isOwnChange) { - signedResponseBuffer = await this.secrets.decryptOwn(relationshipSecretId, cipher.cipher); - } else { - signedResponseBuffer = await this.secrets.decryptPeer(relationshipSecretId, cipher.cipher, true); - } - } else { - signedResponseBuffer = await this.secrets.decryptRequest(relationshipSecretId, cipher.cipher); - } + const creationContentCipher = RelationshipCreationContentCipher.fromBase64(backboneCreationContent); + const signedCreationContentBuffer = await this.secrets.decryptCreationContent(secretId, creationContentCipher.cipher); + const signedCreationContent = RelationshipCreationContentSigned.deserialize(signedCreationContentBuffer.toUtf8()); - const signedResponse = RelationshipCreationChangeResponseSigned.deserialize(signedResponseBuffer.toUtf8()); let relationshipSignatureValid; - if (isOwnChange) { + if (isOwnContent) { relationshipSignatureValid = await this.secrets.verifyOwn( - relationshipSecretId, - CoreBuffer.fromUtf8(signedResponse.serializedResponse), - signedResponse.relationshipSignature + secretId, + CoreBuffer.fromUtf8(signedCreationContent.serializedCreationContent), + signedCreationContent.relationshipSignature ); } else { relationshipSignatureValid = await this.secrets.verifyPeer( - relationshipSecretId, - CoreBuffer.fromUtf8(signedResponse.serializedResponse), - signedResponse.relationshipSignature + secretId, + CoreBuffer.fromUtf8(signedCreationContent.serializedCreationContent), + signedCreationContent.relationshipSignature ); } if (!relationshipSignatureValid) { - throw CoreErrors.general.signatureNotValid("relationshipResponse"); + throw CoreErrors.general.signatureNotValid("relationshipCreationContent"); } - const responseContent = RelationshipCreationChangeResponseContentWrapper.deserialize(signedResponse.serializedResponse); + const creationContent = RelationshipCreationContentWrapper.deserialize(signedCreationContent.serializedCreationContent); - if (!responseContent.relationshipId.equals(change.relationshipId)) { - throw new TransportError("The relationship response contains a wrong relationship id."); - } - return responseContent; + return creationContent; } @log() - private async updatePendingRelationshipWithPeerResponse(relationshipDoc: any, change: BackboneGetRelationshipsChangesResponse): Promise { - const relationship = Relationship.from(relationshipDoc); + private async createNewRelationshipByIncomingCreation(relationshipId: string): Promise { + const backboneRelationship = (await this.client.getRelationship(relationshipId)).value; + if (!backboneRelationship.creationContent) throw new TransportError("Creation content is missing"); - if (relationship.status !== RelationshipStatus.Pending) { - this.log.debug("Trying to update non-pending relationship with creation change", change); - return; - } + const templateId = CoreId.from(backboneRelationship.relationshipTemplateId); + const template = await this.parent.relationshipTemplates.getRelationshipTemplate(templateId); - if (!relationship.cache) { - await this.updateCacheOfRelationship(relationship, undefined); - } + if (!template) throw CoreErrors.general.recordNotFound(RelationshipTemplate, templateId.toString()); + if (!template.cache) throw this.newCacheEmptyError(RelationshipTemplate, template.id.toString()); - if (!change.response) throw this.newChangeResponseMissingError(change.id); + const secretId = await TransportIds.relationshipSecret.generate(); + const creationContentCipher = RelationshipCreationContentCipher.fromBase64(backboneRelationship.creationContent); + await this.secrets.createTemplatorSecrets(secretId, template.cache, creationContentCipher.publicCreationContentCrypto); - if (!change.response.content) { - throw this.newEmptyOrInvalidContentError(change); - } + const creationContent = await this.decryptCreationContent(backboneRelationship.creationContent, CoreAddress.from(backboneRelationship.from), secretId); + const relationship = Relationship.fromBackboneAndCreationContent(backboneRelationship, template, creationContent.identity, creationContent.content, secretId); - const cipher = RelationshipCreationChangeResponseCipher.fromBase64(change.response.content); + await this.relationships.create(relationship); + return relationship; + } - if (change.status !== RelationshipChangeStatus.Revoked) { - if (!cipher.publicResponseCrypto) { - throw new TransportError("The response crypto is missing."); + public async applyRelationshipStatusChangedEvent(relationshipId: string): Promise { + let relationshipDoc = await this.relationships.read(relationshipId); + if (!relationshipDoc) { + const newRelationship = await this.createNewRelationshipByIncomingCreation(relationshipId); + if (newRelationship.status === RelationshipStatus.Pending) { + return newRelationship; } - await this.secrets.convertSecrets(relationship.relationshipSecretId, cipher.publicResponseCrypto); + // this path is for a revocation that is processed before its corresponding creation + relationshipDoc = await this.relationships.read(relationshipId); } - const responseContent = await this.decryptCreationChangeResponse(change, relationship.relationshipSecretId); - - const response = RelationshipChangeResponse.fromBackbone(change.response, responseContent.content); - - if (!relationship.cache) { - throw this.newCacheEmptyError(Relationship, relationship.id.toString()); - } - relationship.cache.changes[0].status = change.status; - switch (change.status) { - case RelationshipChangeStatus.Accepted: - relationship.toActive(response); - break; - case RelationshipChangeStatus.Rejected: - relationship.toRejected(response); - break; - case RelationshipChangeStatus.Revoked: - relationship.toRevoked(response); - break; - default: - throw CoreErrors.general.notSupported(); - } - - await this.relationships.update(relationshipDoc, relationship); - return relationship; + return await this.updatePendingRelationshipWithPeerResponse(relationshipDoc); } - @log() - private async createNewRelationshipByIncomingCreationChange(change: BackboneGetRelationshipsChangesResponse): Promise { - const backboneRelationship = (await this.client.getRelationship(change.relationshipId)).value; + private async prepareCreationResponseContent(relationship: Relationship) { + const publicCreationResponseContentCrypto = await this.secrets.getPublicCreationResponseContentCrypto(relationship.relationshipSecretId); - const templateId = CoreId.from(backboneRelationship.relationshipTemplateId); - const template = await this.parent.relationshipTemplates.getRelationshipTemplate(templateId); + const creationResponseContent = RelationshipCreationResponseContentWrapper.from({ relationshipId: relationship.id }); - if (!template) throw CoreErrors.general.recordNotFound(RelationshipTemplate, templateId.toString()); - if (!template.cache) throw this.newCacheEmptyError(RelationshipTemplate, template.id.toString()); - if (!change.request.content) throw this.newEmptyOrInvalidContentError(change); + const serializedCreationResponseContent = creationResponseContent.serialize(); + const buffer = CoreUtil.toBuffer(serializedCreationResponseContent); - const secretId = await TransportIds.relationshipSecret.generate(); - const requestCipher = RelationshipCreationChangeRequestCipher.fromBase64(change.request.content); - await this.secrets.createTemplatorSecrets(secretId, template.cache, requestCipher.publicRequestCrypto); + const [deviceSignature, relationshipSignature] = await Promise.all([this.parent.activeDevice.sign(buffer), this.secrets.sign(relationship.relationshipSecretId, buffer)]); - const requestContent = await this.decryptCreationChangeRequest(backboneRelationship.changes[0].request, secretId, templateId); - const relationshipChange = RelationshipChange.fromBackbone(change, requestContent.content); + const signedCreationResponseContent = RelationshipCreationResponseContentSigned.from({ + serializedCreationResponseContent, + deviceSignature, + relationshipSignature + }); - const relationship = Relationship.fromCreationChangeReceived(backboneRelationship, template, requestContent.identity, relationshipChange, secretId); + const cipher = await this.secrets.encrypt(relationship.relationshipSecretId, signedCreationResponseContent); + const creationResponseContentCipher = RelationshipCreationResponseContentCipher.from({ + cipher, + publicCreationResponseContentCrypto + }); - await this.relationships.create(relationship); - return relationship; + return creationResponseContentCipher.toBase64(); } @log() - private async completeChange(targetStatus: RelationshipChangeStatus, change: RelationshipChange, content?: ISerializable) { - const relationshipDoc = await this.relationships.read(change.relationshipId.toString()); + private async completeStateTransition(targetStatus: RelationshipStatus, id: CoreId) { + const relationshipDoc = await this.relationships.read(id.toString()); if (!relationshipDoc) { - throw CoreErrors.general.recordNotFound(Relationship, change.relationshipId.toString()); + throw CoreErrors.general.recordNotFound(Relationship, id.toString()); } const relationship = Relationship.from(relationshipDoc); @@ -523,48 +411,30 @@ export class RelationshipsController extends TransportController { } if (!relationship.cache) { - throw this.newCacheEmptyError(Relationship, relationship.id.toString()); - } - - const queriedChange = relationship.cache.changes.find((r) => r.id.toString() === change.id.toString()); - if (!queriedChange) { - throw CoreErrors.general.recordNotFound(RelationshipChange, change.id.toString()); + throw this.newCacheEmptyError(Relationship, id.toString()); } - if (queriedChange.status !== RelationshipChangeStatus.Pending) { - throw CoreErrors.relationships.wrongChangeStatus(queriedChange.status); - } - - let encryptedContent; - if (content) { - encryptedContent = - targetStatus === RelationshipChangeStatus.Revoked - ? await this.encryptRevokeContent(relationship, content) - : await this.encryptAcceptRejectContent(relationship, content); - } - - let backboneResponse: BackboneGetRelationshipsResponse; + let backboneResponse: BackbonePutRelationshipsResponse; switch (targetStatus) { - case RelationshipChangeStatus.Accepted: - backboneResponse = (await this.client.acceptRelationshipChange(relationship.id.toString(), change.id.toString(), encryptedContent)).value; + case RelationshipStatus.Active: + const encryptedContent = await this.prepareCreationResponseContent(relationship); + + backboneResponse = (await this.client.acceptRelationship(id.toString(), { creationResponseContent: encryptedContent })).value; break; - case RelationshipChangeStatus.Rejected: - backboneResponse = (await this.client.rejectRelationshipChange(relationship.id.toString(), change.id.toString(), encryptedContent)).value; + case RelationshipStatus.Rejected: + backboneResponse = (await this.client.rejectRelationship(id.toString())).value; break; - case RelationshipChangeStatus.Revoked: - backboneResponse = (await this.client.revokeRelationshipChange(relationship.id.toString(), change.id.toString(), encryptedContent)).value; + case RelationshipStatus.Revoked: + backboneResponse = (await this.client.revokeRelationship(id.toString())).value; break; default: throw new TransportError("target change status not supported"); } - const backboneChange = backboneResponse.changes[backboneResponse.changes.length - 1]; - - queriedChange.response = RelationshipChangeResponse.fromBackbone(backboneResponse.changes[backboneResponse.changes.length - 1].response!, content); - queriedChange.status = backboneChange.status; relationship.status = backboneResponse.status; + relationship.cache.auditLog = RelationshipAuditLog.fromBackboneAuditLog(backboneResponse.auditLog); await this.relationships.update(relationshipDoc, relationship); @@ -572,69 +442,4 @@ export class RelationshipsController extends TransportController { return relationship; } - - private async encryptRevokeContent(relationship: Relationship, content: ICoreSerializable) { - const responseContent = RelationshipCreationChangeResponseContentWrapper.from({ - relationshipId: relationship.id, - content: content - }); - - const serializedResponse = responseContent.serialize(); - const buffer = CoreUtil.toBuffer(serializedResponse); - - const [deviceSignature, relationshipSignature] = await Promise.all([this.parent.activeDevice.sign(buffer), this.secrets.sign(relationship.relationshipSecretId, buffer)]); - - const signedResponse = RelationshipCreationChangeResponseSigned.from({ - serializedResponse: serializedResponse, - deviceSignature: deviceSignature, - relationshipSignature: relationshipSignature - }); - - const cipher = await this.secrets.encryptRequest(relationship.relationshipSecretId, signedResponse); - const responseCipher = RelationshipCreationChangeResponseCipher.from({ - cipher: cipher - }); - - return responseCipher.toBase64(); - } - - private async encryptAcceptRejectContent(relationship: Relationship, content: ICoreSerializable) { - const publicResponseCrypto = await this.secrets.getPublicResponse(relationship.relationshipSecretId); - - const responseContent = RelationshipCreationChangeResponseContentWrapper.from({ - relationshipId: relationship.id, - content: content - }); - - const serializedResponse = responseContent.serialize(); - const buffer = CoreUtil.toBuffer(serializedResponse); - - const [deviceSignature, relationshipSignature] = await Promise.all([this.parent.activeDevice.sign(buffer), this.secrets.sign(relationship.relationshipSecretId, buffer)]); - - const signedResponse = RelationshipCreationChangeResponseSigned.from({ - serializedResponse: serializedResponse, - deviceSignature: deviceSignature, - relationshipSignature: relationshipSignature - }); - - const cipher = await this.secrets.encrypt(relationship.relationshipSecretId, signedResponse); - const responseCipher = RelationshipCreationChangeResponseCipher.from({ - cipher: cipher, - publicResponseCrypto: publicResponseCrypto - }); - - return responseCipher.toBase64(); - } - - private throwWrongChangeType(type: RelationshipChangeType) { - throw new TransportError(`The relationship change has the wrong type (${type}) to run this operation`); - } - - private newChangeResponseMissingError(changeId: string) { - return new TransportError(`The response of the relationship change (${changeId}) is missing`); - } - - private newEmptyOrInvalidContentError(change?: RelationshipChange | BackboneGetRelationshipsChangesResponse) { - return new TransportError(`The content property of the relationship change ${change?.id} is missing or invalid`); - } } diff --git a/packages/transport/src/modules/relationships/backbone/BackboneGetRelationships.ts b/packages/transport/src/modules/relationships/backbone/BackboneGetRelationships.ts index 11e002ece..752ac6fb3 100644 --- a/packages/transport/src/modules/relationships/backbone/BackboneGetRelationships.ts +++ b/packages/transport/src/modules/relationships/backbone/BackboneGetRelationships.ts @@ -1,19 +1,10 @@ -import { RelationshipStatus } from "../transmission/RelationshipStatus"; -import { BackboneGetRelationshipsChangesResponse } from "./BackboneGetRelationshipsChanges"; +import { BackboneRelationship } from "./BackboneRelationship"; export interface BackboneGetRelationshipsRequest { ids: string[]; } -export interface BackboneGetRelationshipsResponse { - id: string; - relationshipTemplateId: string; - from: string; - to: string; - changes: BackboneGetRelationshipsChangesResponse[]; - createdAt: string; - status: RelationshipStatus; -} +export type BackboneGetRelationshipResponse = BackboneRelationship; export interface BackboneGetRelationshipsDateRange { from?: T; diff --git a/packages/transport/src/modules/relationships/backbone/BackboneGetRelationshipsChanges.ts b/packages/transport/src/modules/relationships/backbone/BackboneGetRelationshipsChanges.ts deleted file mode 100644 index 0cb75f017..000000000 --- a/packages/transport/src/modules/relationships/backbone/BackboneGetRelationshipsChanges.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { RelationshipChangeStatus } from "../transmission/changes/RelationshipChangeStatus"; -import { RelationshipChangeType } from "../transmission/changes/RelationshipChangeType"; - -export interface BackboneGetRelationshipsChangesRequest { - ids: string[]; -} -export interface BackboneGetRelationshipsChangesResponse { - id: string; - relationshipId: string; - request: BackboneGetRelationshipsChangesSingleChangeResponse; - response: BackboneGetRelationshipsChangesSingleChangeResponse | null; - status: RelationshipChangeStatus; - type: RelationshipChangeType; -} -export interface BackboneGetRelationshipsChangesSingleChangeResponse { - createdBy: string; - createdByDevice: string; - createdAt: string; - content: string | null; -} -export interface BackboneGetRelationshipsChangesDateRange { - from?: T; - to?: T; -} diff --git a/packages/transport/src/modules/relationships/backbone/BackbonePostRelationship.ts b/packages/transport/src/modules/relationships/backbone/BackbonePostRelationship.ts new file mode 100644 index 000000000..ab03abe86 --- /dev/null +++ b/packages/transport/src/modules/relationships/backbone/BackbonePostRelationship.ts @@ -0,0 +1,8 @@ +import { BackboneRelationship } from "./BackboneRelationship"; + +export interface BackbonePostRelationshipsRequest { + relationshipTemplateId: string; + creationContent: string; +} + +export type BackbonePostRelationshipsResponse = Omit; diff --git a/packages/transport/src/modules/relationships/backbone/BackbonePostRelationshipsChanges.ts b/packages/transport/src/modules/relationships/backbone/BackbonePostRelationshipsChanges.ts deleted file mode 100644 index a3941dfbc..000000000 --- a/packages/transport/src/modules/relationships/backbone/BackbonePostRelationshipsChanges.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { RelationshipChangeType } from "../transmission/changes/RelationshipChangeType"; - -export interface BackbonePostRelationshipsRequest { - relationshipTemplateId: string; - content: any; -} - -export interface BackbonePostRelationshipsChangesRequest { - type: RelationshipChangeType; -} diff --git a/packages/transport/src/modules/relationships/backbone/BackbonePutRelationship.ts b/packages/transport/src/modules/relationships/backbone/BackbonePutRelationship.ts new file mode 100644 index 000000000..f3a23cd55 --- /dev/null +++ b/packages/transport/src/modules/relationships/backbone/BackbonePutRelationship.ts @@ -0,0 +1,7 @@ +import { BackboneRelationship } from "./BackboneRelationship"; + +export interface BackboneAcceptRelationshipsRequest { + creationResponseContent: string; +} + +export type BackbonePutRelationshipsResponse = Omit; diff --git a/packages/transport/src/modules/relationships/backbone/BackboneRelationship.ts b/packages/transport/src/modules/relationships/backbone/BackboneRelationship.ts new file mode 100644 index 000000000..da60d34c6 --- /dev/null +++ b/packages/transport/src/modules/relationships/backbone/BackboneRelationship.ts @@ -0,0 +1,15 @@ +import { BackboneRelationshipAuditLog } from "../transmission/RelationshipAuditLog"; +import { RelationshipStatus } from "../transmission/RelationshipStatus"; + +export interface BackboneRelationship { + id: string; + relationshipTemplateId: string; + from: string; + to: string; + + createdAt: string; + status: RelationshipStatus; + auditLog: BackboneRelationshipAuditLog; + creationContent?: string; + creationResponseContent?: string; +} diff --git a/packages/transport/src/modules/relationships/backbone/RelationshipClient.ts b/packages/transport/src/modules/relationships/backbone/RelationshipClient.ts index 06cf5d1c0..5a58729d6 100644 --- a/packages/transport/src/modules/relationships/backbone/RelationshipClient.ts +++ b/packages/transport/src/modules/relationships/backbone/RelationshipClient.ts @@ -1,51 +1,32 @@ import { ClientResult } from "../../../core/backbone/ClientResult"; import { Paginator } from "../../../core/backbone/Paginator"; import { RESTClientAuthenticate } from "../../../core/backbone/RESTClientAuthenticate"; -import { BackboneGetRelationshipsRequest, BackboneGetRelationshipsResponse } from "./BackboneGetRelationships"; -import { BackboneGetRelationshipsChangesRequest, BackboneGetRelationshipsChangesResponse } from "./BackboneGetRelationshipsChanges"; -import { BackbonePostRelationshipsChangesRequest, BackbonePostRelationshipsRequest } from "./BackbonePostRelationshipsChanges"; +import { BackboneGetRelationshipResponse, BackboneGetRelationshipsRequest } from "./BackboneGetRelationships"; +import { BackbonePostRelationshipsRequest, BackbonePostRelationshipsResponse } from "./BackbonePostRelationship"; +import { BackboneAcceptRelationshipsRequest, BackbonePutRelationshipsResponse } from "./BackbonePutRelationship"; export class RelationshipClient extends RESTClientAuthenticate { - public async createRelationship(request: BackbonePostRelationshipsRequest): Promise> { - return await this.post("/api/v1/Relationships", request); + public async createRelationship(request: BackbonePostRelationshipsRequest): Promise> { + return await this.post("/api/v1/Relationships", request); } - public async createRelationshipChange(id: string, request: BackbonePostRelationshipsChangesRequest): Promise> { - return await this.post(`/api/v1/Relationships/${id}/Changes`, request); + public async acceptRelationship(relationshipId: string, request: BackboneAcceptRelationshipsRequest): Promise> { + return await this.put(`/api/v1/Relationships/${relationshipId}/Accept`, request); } - public async acceptRelationshipChange(relationshipId: string, changeId: string, content?: any): Promise> { - return await this.put(`/api/v1/Relationships/${relationshipId}/Changes/${changeId}/Accept`, { - content: content - }); + public async rejectRelationship(relationshipId: string): Promise> { + return await this.put(`/api/v1/Relationships/${relationshipId}/Reject`, {}); } - public async rejectRelationshipChange(relationshipId: string, changeId: string, content?: any): Promise> { - return await this.put(`/api/v1/Relationships/${relationshipId}/Changes/${changeId}/Reject`, { - content: content - }); + public async revokeRelationship(relationshipId: string): Promise> { + return await this.put(`/api/v1/Relationships/${relationshipId}/Revoke`, {}); } - public async revokeRelationshipChange(relationshipId: string, changeId: string, content?: any): Promise> { - return await this.put(`/api/v1/Relationships/${relationshipId}/Changes/${changeId}/Revoke`, { - content: content - }); + public async getRelationships(request?: BackboneGetRelationshipsRequest): Promise>> { + return await this.getPaged("/api/v1/Relationships", request); } - public async getRelationships(request?: BackboneGetRelationshipsRequest): Promise>> { - return await this.getPaged("/api/v1/Relationships", request); - } - - public async getRelationship(relationshipId: string): Promise> { - return await this.get(`/api/v1/Relationships/${relationshipId}`); - } - - public async getRelationshipChanges(request?: BackboneGetRelationshipsChangesRequest): Promise>> { - return await this.getPaged("/api/v1/Relationships/Changes", request); - } - - public async getRelationshipChange(relationshipChangeId: string): Promise> { - const change = await this.get(`/api/v1/Relationships/Changes/${relationshipChangeId}`); - return change; + public async getRelationship(relationshipId: string): Promise> { + return await this.get(`/api/v1/Relationships/${relationshipId}`); } } diff --git a/packages/transport/src/modules/relationships/local/CachedRelationship.ts b/packages/transport/src/modules/relationships/local/CachedRelationship.ts index 507b96949..142c5c808 100644 --- a/packages/transport/src/modules/relationships/local/CachedRelationship.ts +++ b/packages/transport/src/modules/relationships/local/CachedRelationship.ts @@ -1,14 +1,15 @@ -import { serialize, type, validate } from "@js-soft/ts-serval"; +import { ISerializable, Serializable, serialize, type, validate } from "@js-soft/ts-serval"; import { CoreDate, CoreSerializable, ICoreDate, ICoreSerializable } from "../../../core"; import { IRelationshipTemplate, RelationshipTemplate } from "../../relationshipTemplates/local/RelationshipTemplate"; -import { IRelationshipChange, RelationshipChange } from "../transmission/changes/RelationshipChange"; +import { IRelationshipAuditLogEntry, RelationshipAuditLogEntry } from "./RelationshipAuditLogEntry"; export interface ICachedRelationship extends ICoreSerializable { template: IRelationshipTemplate; - changes: IRelationshipChange[]; + creationContent?: ISerializable; lastMessageSentAt?: ICoreDate; lastMessageReceivedAt?: ICoreDate; + auditLog: IRelationshipAuditLogEntry[]; } @type("CachedRelationship") @@ -17,9 +18,9 @@ export class CachedRelationship extends CoreSerializable implements ICachedRelat @serialize() public template: RelationshipTemplate; - @validate() - @serialize({ type: RelationshipChange }) - public changes: RelationshipChange[]; + @validate({ nullable: true }) + @serialize() + public creationContent?: Serializable; @validate({ nullable: true }) @serialize() @@ -29,9 +30,9 @@ export class CachedRelationship extends CoreSerializable implements ICachedRelat @serialize() public lastMessageReceivedAt?: CoreDate; - public get creationChange(): RelationshipChange { - return this.changes[0]; - } + @validate() + @serialize({ type: RelationshipAuditLogEntry }) + public auditLog: RelationshipAuditLogEntry[]; public static from(value: ICachedRelationship): CachedRelationship { return this.fromAny(value); diff --git a/packages/transport/src/modules/relationships/local/Relationship.ts b/packages/transport/src/modules/relationships/local/Relationship.ts index 6ddaa5454..2cb95082d 100644 --- a/packages/transport/src/modules/relationships/local/Relationship.ts +++ b/packages/transport/src/modules/relationships/local/Relationship.ts @@ -1,13 +1,12 @@ -import { serialize, type, validate } from "@js-soft/ts-serval"; +import { ISerializable, serialize, type, validate } from "@js-soft/ts-serval"; import { nameof } from "ts-simple-nameof"; import { CoreDate, CoreId, CoreSynchronizable, ICoreId, ICoreSynchronizable, TransportError } from "../../../core"; -import { IIdentity, Identity } from "../../accounts/data/Identity"; +import { Identity, IIdentity } from "../../accounts/data/Identity"; import { IRelationshipTemplate } from "../../relationshipTemplates/local/RelationshipTemplate"; -import { BackboneGetRelationshipsResponse } from "../backbone/BackboneGetRelationships"; +import { BackboneGetRelationshipResponse } from "../backbone/BackboneGetRelationships"; import { RelationshipStatus } from "../transmission/RelationshipStatus"; -import { IRelationshipChange } from "../transmission/changes/RelationshipChange"; -import { RelationshipChangeResponse } from "../transmission/changes/RelationshipChangeResponse"; import { CachedRelationship, ICachedRelationship } from "./CachedRelationship"; +import { RelationshipAuditLog } from "./RelationshipAuditLog"; export interface IRelationship extends ICoreSynchronizable { relationshipSecretId: ICoreId; @@ -72,32 +71,17 @@ export class Relationship extends CoreSynchronizable implements IRelationship { return json; } - public static fromRequestSent(id: CoreId, template: IRelationshipTemplate, peer: IIdentity, creationChange: IRelationshipChange, relationshipSecretId: CoreId): Relationship { - const cache = CachedRelationship.from({ - changes: [creationChange], - template: template - }); - - return Relationship.from({ - id: id, - peer: peer, - status: RelationshipStatus.Pending, - cache: cache, - cachedAt: CoreDate.utc(), - relationshipSecretId: relationshipSecretId - }); - } - - public static fromCreationChangeReceived( - response: BackboneGetRelationshipsResponse, + public static fromBackboneAndCreationContent( + response: BackboneGetRelationshipResponse, template: IRelationshipTemplate, peer: IIdentity, - creationChange: IRelationshipChange, + creationContent: ISerializable, relationshipSecretId: CoreId ): Relationship { const cache = CachedRelationship.from({ - changes: [creationChange], - template: template + creationContent, + template: template, + auditLog: RelationshipAuditLog.fromBackboneAuditLog(response.auditLog) }); return Relationship.from({ id: CoreId.from(response.id), @@ -109,27 +93,6 @@ export class Relationship extends CoreSynchronizable implements IRelationship { }); } - public toActive(response: RelationshipChangeResponse): void { - if (!this.cache) throw this.newCacheEmptyError(); - - this.cache.changes[0].response = response; - this.status = RelationshipStatus.Active; - } - - public toRejected(response: RelationshipChangeResponse): void { - if (!this.cache) throw this.newCacheEmptyError(); - - this.cache.changes[0].response = response; - this.status = RelationshipStatus.Rejected; - } - - public toRevoked(response: RelationshipChangeResponse): void { - if (!this.cache) throw this.newCacheEmptyError(); - - this.cache.changes[0].response = response; - this.status = RelationshipStatus.Revoked; - } - public static from(value: IRelationship): Relationship { return this.fromAny(value); } diff --git a/packages/transport/src/modules/relationships/local/RelationshipAuditLog.ts b/packages/transport/src/modules/relationships/local/RelationshipAuditLog.ts new file mode 100644 index 000000000..5923d31eb --- /dev/null +++ b/packages/transport/src/modules/relationships/local/RelationshipAuditLog.ts @@ -0,0 +1,21 @@ +import _ from "lodash"; +import { CoreAddress, CoreDate, CoreId } from "../../../core"; +import { BackboneRelationshipAuditLog as BackboneAuditLog } from "../transmission/RelationshipAuditLog"; +import { RelationshipAuditLogEntry } from "./RelationshipAuditLogEntry"; + +export class RelationshipAuditLog { + public static fromBackboneAuditLog(backboneAuditLog: BackboneAuditLog): RelationshipAuditLogEntry[] { + const auditLog = backboneAuditLog.map((entry) => { + return RelationshipAuditLogEntry.from({ + createdAt: CoreDate.from(entry.createdAt), + createdBy: CoreAddress.from(entry.createdBy), + createdByDevice: CoreId.from(entry.createdByDevice), + reason: entry.reason, + oldStatus: entry.oldStatus, + newStatus: entry.newStatus + }); + }); + + return _.orderBy(auditLog, ["createdAt"], ["asc"]); + } +} diff --git a/packages/transport/src/modules/relationships/local/RelationshipAuditLogEntry.ts b/packages/transport/src/modules/relationships/local/RelationshipAuditLogEntry.ts new file mode 100644 index 000000000..114fa1fc3 --- /dev/null +++ b/packages/transport/src/modules/relationships/local/RelationshipAuditLogEntry.ts @@ -0,0 +1,51 @@ +import { serialize, type, validate } from "@js-soft/ts-serval"; +import { CoreAddress, CoreDate, CoreId, CoreSerializable, ICoreAddress, ICoreDate, ICoreId } from "../../../core"; +import { RelationshipAuditLogEntryReason } from "../transmission/RelationshipAuditLog"; +import { RelationshipStatus } from "../transmission/RelationshipStatus"; + +export interface IRelationshipAuditLogEntry { + createdAt: ICoreDate; + createdBy: ICoreAddress; + createdByDevice: ICoreId; + reason: RelationshipAuditLogEntryReason; + oldStatus?: RelationshipStatus; + newStatus: RelationshipStatus; +} + +@type("RelationshipAuditLogEntry") +export class RelationshipAuditLogEntry extends CoreSerializable implements IRelationshipAuditLogEntry { + @validate() + @serialize() + public createdAt: CoreDate; + + @validate() + @serialize() + public createdBy: CoreAddress; + + @validate() + @serialize() + public createdByDevice: CoreId; + + @validate({ + customValidator: (v) => (!Object.values(RelationshipAuditLogEntryReason).includes(v) ? `must be one of: ${Object.values(RelationshipAuditLogEntryReason)}` : undefined) + }) + @serialize() + public reason: RelationshipAuditLogEntryReason; + + @validate({ + nullable: true, + customValidator: (v) => (!Object.values(RelationshipStatus).includes(v) ? `must be one of: ${Object.values(RelationshipStatus)}` : undefined) + }) + @serialize() + public oldStatus?: RelationshipStatus; + + @validate({ + customValidator: (v) => (!Object.values(RelationshipStatus).includes(v) ? `must be one of: ${Object.values(RelationshipStatus)}` : undefined) + }) + @serialize() + public newStatus: RelationshipStatus; + + public static from(value: IRelationshipAuditLogEntry): RelationshipAuditLogEntry { + return this.fromAny({ ...value, oldStatus: value.oldStatus ?? undefined }); + } +} diff --git a/packages/transport/src/modules/relationships/local/SendRelationshipParameters.ts b/packages/transport/src/modules/relationships/local/SendRelationshipParameters.ts index 16c5d647a..7ac9af12c 100644 --- a/packages/transport/src/modules/relationships/local/SendRelationshipParameters.ts +++ b/packages/transport/src/modules/relationships/local/SendRelationshipParameters.ts @@ -3,7 +3,7 @@ import { CoreSerializable, ICoreSerializable } from "../../../core"; import { IRelationshipTemplate, RelationshipTemplate } from "../../relationshipTemplates/local/RelationshipTemplate"; export interface ISendRelationshipParameters extends ICoreSerializable { - content: ISerializable; + creationContent: ISerializable; template: IRelationshipTemplate; } @@ -11,7 +11,7 @@ export interface ISendRelationshipParameters extends ICoreSerializable { export class SendRelationshipParameters extends CoreSerializable implements ISendRelationshipParameters { @validate() @serialize() - public content: Serializable; + public creationContent: Serializable; @validate() @serialize() diff --git a/packages/transport/src/modules/relationships/transmission/RelationshipAuditLog.ts b/packages/transport/src/modules/relationships/transmission/RelationshipAuditLog.ts new file mode 100644 index 000000000..9473996f0 --- /dev/null +++ b/packages/transport/src/modules/relationships/transmission/RelationshipAuditLog.ts @@ -0,0 +1,19 @@ +import { RelationshipStatus } from "./RelationshipStatus"; + +export interface BackboneRelationshipAuditLog extends Array {} + +export interface BackboneRelationshipAuditLogEntry { + createdAt: string; + createdBy: string; + createdByDevice: string; + reason: RelationshipAuditLogEntryReason; + oldStatus?: RelationshipStatus; + newStatus: RelationshipStatus; +} + +export enum RelationshipAuditLogEntryReason { + Creation = "Creation", + AcceptanceOfCreation = "AcceptanceOfCreation", + RejectionOfCreation = "RejectionOfCreation", + RevocationOfCreation = "RevocationOfCreation" +} diff --git a/packages/transport/src/modules/relationships/transmission/changes/RelationshipChange.ts b/packages/transport/src/modules/relationships/transmission/changes/RelationshipChange.ts deleted file mode 100644 index f84f269e9..000000000 --- a/packages/transport/src/modules/relationships/transmission/changes/RelationshipChange.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { ISerializable, serialize, type, validate } from "@js-soft/ts-serval"; -import { CoreSerializable, ICoreSerializable } from "../../../../core"; -import { CoreId, ICoreId } from "../../../../core/types/CoreId"; -import { BackboneGetRelationshipsChangesResponse } from "../../backbone/BackboneGetRelationshipsChanges"; -import { IRelationshipChangeRequest, RelationshipChangeRequest } from "./RelationshipChangeRequest"; -import { IRelationshipChangeResponse, RelationshipChangeResponse } from "./RelationshipChangeResponse"; -import { RelationshipChangeStatus } from "./RelationshipChangeStatus"; -import { RelationshipChangeType } from "./RelationshipChangeType"; - -export interface IRelationshipChange extends ICoreSerializable { - id: ICoreId; - relationshipId: ICoreId; - request: IRelationshipChangeRequest; - response?: IRelationshipChangeResponse; - status: RelationshipChangeStatus; - type: RelationshipChangeType; -} - -@type("RelationshipChange") -export class RelationshipChange extends CoreSerializable implements IRelationshipChange { - @validate() - @serialize() - public id: CoreId; - - @validate() - @serialize() - public relationshipId: CoreId; - - @validate() - @serialize() - public request: RelationshipChangeRequest; - - @validate({ nullable: true }) - @serialize() - public response?: RelationshipChangeResponse; - - @validate() - @serialize() - public status: RelationshipChangeStatus; - - @validate() - @serialize() - public type: RelationshipChangeType; - - public static fromBackbone(backboneChange: BackboneGetRelationshipsChangesResponse, requestContent?: ISerializable, responseContent?: ISerializable): RelationshipChange { - const relationshipChange = this.from({ - id: CoreId.from(backboneChange.id), - relationshipId: CoreId.from(backboneChange.relationshipId), - type: backboneChange.type, - status: backboneChange.status, - request: RelationshipChangeRequest.fromBackbone(backboneChange.request, requestContent) - }); - - if (backboneChange.response) { - relationshipChange.response = RelationshipChangeResponse.fromBackbone(backboneChange.response, responseContent); - } - - return relationshipChange; - } - - public static from(value: IRelationshipChange): RelationshipChange { - return this.fromAny(value); - } -} diff --git a/packages/transport/src/modules/relationships/transmission/changes/RelationshipChangeRequest.ts b/packages/transport/src/modules/relationships/transmission/changes/RelationshipChangeRequest.ts deleted file mode 100644 index 2f84e8059..000000000 --- a/packages/transport/src/modules/relationships/transmission/changes/RelationshipChangeRequest.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { ISerializable, Serializable, serialize, type, validate } from "@js-soft/ts-serval"; -import { CoreAddress, CoreDate, CoreSerializable, ICoreAddress } from "../../../../core"; -import { ICoreDate } from "../../../../core/types/CoreDate"; -import { CoreId, ICoreId } from "../../../../core/types/CoreId"; -import { BackboneGetRelationshipsChangesSingleChangeResponse } from "../../backbone/BackboneGetRelationshipsChanges"; - -export interface IRelationshipChangeRequest { - createdBy: ICoreAddress; - createdByDevice: ICoreId; - createdAt: ICoreDate; - content?: ISerializable; -} - -@type("RelationshipChangeRequest") -export class RelationshipChangeRequest extends CoreSerializable implements IRelationshipChangeRequest { - @validate() - @serialize() - public createdBy: CoreAddress; - - @validate() - @serialize() - public createdByDevice: CoreId; - - @validate() - @serialize() - public createdAt: CoreDate; - - @validate({ nullable: true }) - @serialize() - public content?: Serializable; - - public static fromBackbone(backboneChange: BackboneGetRelationshipsChangesSingleChangeResponse, content?: ISerializable): RelationshipChangeRequest { - return this.from({ - createdBy: CoreAddress.from(backboneChange.createdBy), - createdByDevice: CoreId.from(backboneChange.createdByDevice), - createdAt: CoreDate.from(backboneChange.createdAt), - content: content - }); - } - - public static from(value: IRelationshipChangeRequest): RelationshipChangeRequest { - return this.fromAny(value); - } -} diff --git a/packages/transport/src/modules/relationships/transmission/changes/RelationshipChangeResponse.ts b/packages/transport/src/modules/relationships/transmission/changes/RelationshipChangeResponse.ts deleted file mode 100644 index 2a8200e0c..000000000 --- a/packages/transport/src/modules/relationships/transmission/changes/RelationshipChangeResponse.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { ISerializable, Serializable, serialize, type, validate } from "@js-soft/ts-serval"; -import { CoreAddress, CoreDate, CoreSerializable, ICoreAddress, ICoreSerializable } from "../../../../core"; -import { ICoreDate } from "../../../../core/types/CoreDate"; -import { CoreId, ICoreId } from "../../../../core/types/CoreId"; -import { BackboneGetRelationshipsChangesSingleChangeResponse } from "../../backbone/BackboneGetRelationshipsChanges"; - -export interface IRelationshipChangeResponse extends ICoreSerializable { - createdBy: ICoreAddress; - createdByDevice: ICoreId; - createdAt: ICoreDate; - content?: ISerializable; -} - -@type("RelationshipChangeResponse") -export class RelationshipChangeResponse extends CoreSerializable implements IRelationshipChangeResponse { - @validate() - @serialize() - public createdBy: CoreAddress; - - @validate() - @serialize() - public createdByDevice: CoreId; - - @validate() - @serialize() - public createdAt: CoreDate; - - @validate({ nullable: true }) - @serialize() - public content?: Serializable; - - public static fromBackbone(backboneChange: BackboneGetRelationshipsChangesSingleChangeResponse, content?: ISerializable): RelationshipChangeResponse { - return this.from({ - createdBy: CoreAddress.from(backboneChange.createdBy), - createdByDevice: CoreId.from(backboneChange.createdByDevice), - createdAt: CoreDate.from(backboneChange.createdAt), - content: content - }); - } - - public static from(value: IRelationshipChangeResponse): RelationshipChangeResponse { - return this.fromAny(value); - } -} diff --git a/packages/transport/src/modules/relationships/transmission/changes/RelationshipChangeStatus.ts b/packages/transport/src/modules/relationships/transmission/changes/RelationshipChangeStatus.ts deleted file mode 100644 index 3e207dd8b..000000000 --- a/packages/transport/src/modules/relationships/transmission/changes/RelationshipChangeStatus.ts +++ /dev/null @@ -1,6 +0,0 @@ -export enum RelationshipChangeStatus { - Pending = "Pending", - Rejected = "Rejected", - Revoked = "Revoked", - Accepted = "Accepted" -} diff --git a/packages/transport/src/modules/relationships/transmission/changes/RelationshipChangeType.ts b/packages/transport/src/modules/relationships/transmission/changes/RelationshipChangeType.ts deleted file mode 100644 index 5ee635209..000000000 --- a/packages/transport/src/modules/relationships/transmission/changes/RelationshipChangeType.ts +++ /dev/null @@ -1,5 +0,0 @@ -export enum RelationshipChangeType { - Creation = "Creation", - Termination = "Termination", - TerminationCancellation = "TerminationCancellation" -} diff --git a/packages/transport/src/modules/relationships/transmission/requests/RelationshipCreationChangeRequestCipher.ts b/packages/transport/src/modules/relationships/transmission/requests/RelationshipCreationChangeRequestCipher.ts deleted file mode 100644 index ec8a4547e..000000000 --- a/packages/transport/src/modules/relationships/transmission/requests/RelationshipCreationChangeRequestCipher.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { serialize, type, validate } from "@js-soft/ts-serval"; -import { CryptoCipher, CryptoRelationshipPublicRequest, ICryptoCipher, ICryptoRelationshipPublicRequest } from "@nmshd/crypto"; -import { CoreSerializable, ICoreSerializable } from "../../../../core"; - -export interface IRelationshipCreationChangeRequestCipher extends ICoreSerializable { - cipher: ICryptoCipher; - publicRequestCrypto: ICryptoRelationshipPublicRequest; -} - -@type("RelationshipCreationChangeRequestCipher") -export class RelationshipCreationChangeRequestCipher extends CoreSerializable implements IRelationshipCreationChangeRequestCipher { - @validate() - @serialize() - public cipher: CryptoCipher; - - @validate() - @serialize() - public publicRequestCrypto: CryptoRelationshipPublicRequest; - - public static from(value: IRelationshipCreationChangeRequestCipher): RelationshipCreationChangeRequestCipher { - return this.fromAny(value); - } - - public static fromBase64(value: string): RelationshipCreationChangeRequestCipher { - return super.fromBase64T(value); - } -} diff --git a/packages/transport/src/modules/relationships/transmission/requests/RelationshipCreationContentCipher.ts b/packages/transport/src/modules/relationships/transmission/requests/RelationshipCreationContentCipher.ts new file mode 100644 index 000000000..7d7c32d42 --- /dev/null +++ b/packages/transport/src/modules/relationships/transmission/requests/RelationshipCreationContentCipher.ts @@ -0,0 +1,27 @@ +import { serialize, type, validate } from "@js-soft/ts-serval"; +import { CryptoCipher, CryptoRelationshipPublicRequest, ICryptoCipher, ICryptoRelationshipPublicRequest } from "@nmshd/crypto"; +import { CoreSerializable, ICoreSerializable } from "../../../../core"; + +export interface IRelationshipCreationContentCipher extends ICoreSerializable { + cipher: ICryptoCipher; + publicCreationContentCrypto: ICryptoRelationshipPublicRequest; +} + +@type("RelationshipCreationContentCipher") +export class RelationshipCreationContentCipher extends CoreSerializable implements IRelationshipCreationContentCipher { + @validate() + @serialize() + public cipher: CryptoCipher; + + @validate() + @serialize() + public publicCreationContentCrypto: CryptoRelationshipPublicRequest; + + public static from(value: IRelationshipCreationContentCipher): RelationshipCreationContentCipher { + return this.fromAny(value); + } + + public static fromBase64(value: string): RelationshipCreationContentCipher { + return super.fromBase64T(value); + } +} diff --git a/packages/transport/src/modules/relationships/transmission/requests/RelationshipCreationChangeRequestSigned.ts b/packages/transport/src/modules/relationships/transmission/requests/RelationshipCreationContentSigned.ts similarity index 54% rename from packages/transport/src/modules/relationships/transmission/requests/RelationshipCreationChangeRequestSigned.ts rename to packages/transport/src/modules/relationships/transmission/requests/RelationshipCreationContentSigned.ts index 5dab25686..8a8407f1c 100644 --- a/packages/transport/src/modules/relationships/transmission/requests/RelationshipCreationChangeRequestSigned.ts +++ b/packages/transport/src/modules/relationships/transmission/requests/RelationshipCreationContentSigned.ts @@ -2,17 +2,17 @@ import { serialize, type, validate } from "@js-soft/ts-serval"; import { CryptoSignature, ICryptoSignature } from "@nmshd/crypto"; import { CoreSerializable, ICoreSerializable } from "../../../../core"; -export interface IRelationshipCreationChangeRequestSigned extends ICoreSerializable { - serializedRequest: string; +export interface IRelationshipCreationContentSigned extends ICoreSerializable { + serializedCreationContent: string; deviceSignature: ICryptoSignature; relationshipSignature: ICryptoSignature; } -@type("RelationshipCreationChangeRequestSigned") -export class RelationshipCreationChangeRequestSigned extends CoreSerializable implements IRelationshipCreationChangeRequestSigned { +@type("RelationshipCreationContentSigned") +export class RelationshipCreationContentSigned extends CoreSerializable implements IRelationshipCreationContentSigned { @validate() @serialize() - public serializedRequest: string; + public serializedCreationContent: string; @validate() @serialize() @@ -22,7 +22,7 @@ export class RelationshipCreationChangeRequestSigned extends CoreSerializable im @serialize() public relationshipSignature: CryptoSignature; - public static from(value: IRelationshipCreationChangeRequestSigned): RelationshipCreationChangeRequestSigned { + public static from(value: IRelationshipCreationContentSigned): RelationshipCreationContentSigned { return this.fromAny(value); } } diff --git a/packages/transport/src/modules/relationships/transmission/requests/RelationshipCreationChangeRequestContentWrapper.ts b/packages/transport/src/modules/relationships/transmission/requests/RelationshipCreationContentWrapper.ts similarity index 50% rename from packages/transport/src/modules/relationships/transmission/requests/RelationshipCreationChangeRequestContentWrapper.ts rename to packages/transport/src/modules/relationships/transmission/requests/RelationshipCreationContentWrapper.ts index 3136c43d7..0250057f5 100644 --- a/packages/transport/src/modules/relationships/transmission/requests/RelationshipCreationChangeRequestContentWrapper.ts +++ b/packages/transport/src/modules/relationships/transmission/requests/RelationshipCreationContentWrapper.ts @@ -1,15 +1,15 @@ import { ISerializable, Serializable, serialize, type, validate } from "@js-soft/ts-serval"; import { CoreId, CoreSerializable, ICoreId, ICoreSerializable } from "../../../../core"; -import { IIdentity, Identity } from "../../../accounts/data/Identity"; +import { Identity, IIdentity } from "../../../accounts/data/Identity"; -export interface IRelationshipCreationChangeRequestContentWrapper extends ICoreSerializable { +export interface IRelationshipCreationContentWrapper extends ICoreSerializable { identity: IIdentity; content: ISerializable; templateId: ICoreId; } -@type("RelationshipCreationChangeRequestContentWrapper") -export class RelationshipCreationChangeRequestContentWrapper extends CoreSerializable implements IRelationshipCreationChangeRequestContentWrapper { +@type("RelationshipCreationContentWrapper") +export class RelationshipCreationContentWrapper extends CoreSerializable implements IRelationshipCreationContentWrapper { @validate() @serialize() public identity: Identity; @@ -22,7 +22,7 @@ export class RelationshipCreationChangeRequestContentWrapper extends CoreSeriali @serialize() public templateId: CoreId; - public static from(value: IRelationshipCreationChangeRequestContentWrapper): RelationshipCreationChangeRequestContentWrapper { + public static from(value: IRelationshipCreationContentWrapper): RelationshipCreationContentWrapper { return this.fromAny(value); } } diff --git a/packages/transport/src/modules/relationships/transmission/responses/RelationshipCreationChangeResponseCipher.ts b/packages/transport/src/modules/relationships/transmission/responses/RelationshipCreationChangeResponseCipher.ts deleted file mode 100644 index d6e1b0d3e..000000000 --- a/packages/transport/src/modules/relationships/transmission/responses/RelationshipCreationChangeResponseCipher.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { serialize, type, validate } from "@js-soft/ts-serval"; -import { CryptoCipher, CryptoRelationshipPublicResponse, ICryptoCipher, ICryptoRelationshipPublicResponse } from "@nmshd/crypto"; -import { CoreSerializable, ICoreSerializable } from "../../../../core"; - -export interface IRelationshipCreationChangeResponseCipher extends ICoreSerializable { - cipher: ICryptoCipher; - publicResponseCrypto?: ICryptoRelationshipPublicResponse; -} - -@type("RelationshipCreationChangeResponseCipher") -export class RelationshipCreationChangeResponseCipher extends CoreSerializable implements IRelationshipCreationChangeResponseCipher { - @validate() - @serialize() - public cipher: CryptoCipher; - - @validate({ nullable: true }) - @serialize() - public publicResponseCrypto?: CryptoRelationshipPublicResponse; - - public static from(value: IRelationshipCreationChangeResponseCipher): RelationshipCreationChangeResponseCipher { - return this.fromAny(value); - } - - public static fromBase64(value: string): RelationshipCreationChangeResponseCipher { - return super.fromBase64T(value); - } -} diff --git a/packages/transport/src/modules/relationships/transmission/responses/RelationshipCreationChangeResponseContentWrapper.ts b/packages/transport/src/modules/relationships/transmission/responses/RelationshipCreationChangeResponseContentWrapper.ts deleted file mode 100644 index e9c5ca0f2..000000000 --- a/packages/transport/src/modules/relationships/transmission/responses/RelationshipCreationChangeResponseContentWrapper.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { ISerializable, Serializable, serialize, type, validate } from "@js-soft/ts-serval"; -import { CoreId, CoreSerializable, ICoreId, ICoreSerializable } from "../../../../core"; - -export interface IRelationshipCreationChangeResponseContentWrapper extends ICoreSerializable { - content: ISerializable; - relationshipId: ICoreId; -} - -@type("RelationshipCreationChangeResponseContentWrapper") -export class RelationshipCreationChangeResponseContentWrapper extends CoreSerializable implements IRelationshipCreationChangeResponseContentWrapper { - @validate() - @serialize() - public content: Serializable; - - @validate() - @serialize() - public relationshipId: CoreId; - - public static from(value: IRelationshipCreationChangeResponseContentWrapper): RelationshipCreationChangeResponseContentWrapper { - return this.fromAny(value); - } -} diff --git a/packages/transport/src/modules/relationships/transmission/responses/RelationshipCreationResponseContentCipher.ts b/packages/transport/src/modules/relationships/transmission/responses/RelationshipCreationResponseContentCipher.ts new file mode 100644 index 000000000..b7b6c5b4a --- /dev/null +++ b/packages/transport/src/modules/relationships/transmission/responses/RelationshipCreationResponseContentCipher.ts @@ -0,0 +1,27 @@ +import { serialize, type, validate } from "@js-soft/ts-serval"; +import { CryptoCipher, CryptoRelationshipPublicResponse, ICryptoCipher, ICryptoRelationshipPublicResponse } from "@nmshd/crypto"; +import { CoreSerializable, ICoreSerializable } from "../../../../core"; + +export interface IRelationshipCreationResponseContentCipher extends ICoreSerializable { + cipher: ICryptoCipher; + publicCreationResponseContentCrypto: ICryptoRelationshipPublicResponse; +} + +@type("RelationshipCreationResponseContentCipher") +export class RelationshipCreationResponseContentCipher extends CoreSerializable implements IRelationshipCreationResponseContentCipher { + @validate() + @serialize() + public cipher: CryptoCipher; + + @validate() + @serialize() + public publicCreationResponseContentCrypto: CryptoRelationshipPublicResponse; + + public static from(value: IRelationshipCreationResponseContentCipher): RelationshipCreationResponseContentCipher { + return this.fromAny(value); + } + + public static fromBase64(value: string): RelationshipCreationResponseContentCipher { + return super.fromBase64T(value); + } +} diff --git a/packages/transport/src/modules/relationships/transmission/responses/RelationshipCreationChangeResponseSigned.ts b/packages/transport/src/modules/relationships/transmission/responses/RelationshipCreationResponseContentSigned.ts similarity index 51% rename from packages/transport/src/modules/relationships/transmission/responses/RelationshipCreationChangeResponseSigned.ts rename to packages/transport/src/modules/relationships/transmission/responses/RelationshipCreationResponseContentSigned.ts index 35c7bc61c..75721bcad 100644 --- a/packages/transport/src/modules/relationships/transmission/responses/RelationshipCreationChangeResponseSigned.ts +++ b/packages/transport/src/modules/relationships/transmission/responses/RelationshipCreationResponseContentSigned.ts @@ -2,17 +2,17 @@ import { serialize, type, validate } from "@js-soft/ts-serval"; import { CryptoSignature, ICryptoSignature } from "@nmshd/crypto"; import { CoreSerializable, ICoreSerializable } from "../../../../core"; -export interface IRelationshipCreationChangeResponseSigned extends ICoreSerializable { - serializedResponse: string; +export interface IRelationshipCreationResponseContentSigned extends ICoreSerializable { + serializedCreationResponseContent: string; deviceSignature: ICryptoSignature; relationshipSignature: ICryptoSignature; } -@type("RelationshipCreationChangeResponseSigned") -export class RelationshipCreationChangeResponseSigned extends CoreSerializable implements IRelationshipCreationChangeResponseSigned { +@type("RelationshipCreationResponseContentSigned") +export class RelationshipCreationResponseContentSigned extends CoreSerializable implements IRelationshipCreationResponseContentSigned { @validate() @serialize() - public serializedResponse: string; + public serializedCreationResponseContent: string; @validate() @serialize() @@ -22,7 +22,7 @@ export class RelationshipCreationChangeResponseSigned extends CoreSerializable i @serialize() public relationshipSignature: CryptoSignature; - public static from(value: IRelationshipCreationChangeResponseSigned): RelationshipCreationChangeResponseSigned { + public static from(value: IRelationshipCreationResponseContentSigned): RelationshipCreationResponseContentSigned { return this.fromAny(value); } } diff --git a/packages/transport/src/modules/relationships/transmission/responses/RelationshipCreationResponseContentWrapper.ts b/packages/transport/src/modules/relationships/transmission/responses/RelationshipCreationResponseContentWrapper.ts new file mode 100644 index 000000000..f137a2eba --- /dev/null +++ b/packages/transport/src/modules/relationships/transmission/responses/RelationshipCreationResponseContentWrapper.ts @@ -0,0 +1,17 @@ +import { serialize, type, validate } from "@js-soft/ts-serval"; +import { CoreId, CoreSerializable, ICoreId, ICoreSerializable } from "../../../../core"; + +export interface IRelationshipCreationResponseContentWrapper extends ICoreSerializable { + relationshipId: ICoreId; +} + +@type("RelationshipCreationResponseContentWrapper") +export class RelationshipCreationResponseContentWrapper extends CoreSerializable implements IRelationshipCreationResponseContentWrapper { + @validate() + @serialize() + public relationshipId: CoreId; + + public static from(value: IRelationshipCreationResponseContentWrapper): RelationshipCreationResponseContentWrapper { + return this.fromAny(value); + } +} diff --git a/packages/transport/src/modules/sync/externalEventProcessors/ExternalEventProcessorRegistry.ts b/packages/transport/src/modules/sync/externalEventProcessors/ExternalEventProcessorRegistry.ts index 6d5644b8c..651cb6af9 100644 --- a/packages/transport/src/modules/sync/externalEventProcessors/ExternalEventProcessorRegistry.ts +++ b/packages/transport/src/modules/sync/externalEventProcessors/ExternalEventProcessorRegistry.ts @@ -2,16 +2,14 @@ import { TransportError } from "../../../core"; import { ExternalEventProcessorConstructor } from "./ExternalEventProcessor"; import { MessageDeliveredExternalEventProcessor } from "./MessageDeliveredExternalEventProcessor"; import { MessageReceivedExternalEventProcessor } from "./MessageReceivedExternalEventProcessor"; -import { RelationshipChangeCompletedExternalEventProcessor } from "./RelationshipChangeCompletedExternalEventProcessor"; -import { RelationshipChangeCreatedExternalEventProcessor } from "./RelationshipChangeCreatedExternalEventProcessor"; +import { RelationshipStatusChangedExternalEventProcessor } from "./RelationshipStatusChangedExternalEventProcessor"; export class ExternalEventProcessorRegistry { private readonly processors = new Map(); public constructor() { this.registerProcessor("MessageReceived", MessageReceivedExternalEventProcessor); this.registerProcessor("MessageDelivered", MessageDeliveredExternalEventProcessor); - this.registerProcessor("RelationshipChangeCreated", RelationshipChangeCreatedExternalEventProcessor); - this.registerProcessor("RelationshipChangeCompleted", RelationshipChangeCompletedExternalEventProcessor); + this.registerProcessor("RelationshipStatusChanged", RelationshipStatusChangedExternalEventProcessor); } public registerProcessor(externalEventName: string, externalEventProcessor: ExternalEventProcessorConstructor): void { diff --git a/packages/transport/src/modules/sync/externalEventProcessors/RelationshipChangeCompletedExternalEventProcessor.ts b/packages/transport/src/modules/sync/externalEventProcessors/RelationshipChangeCompletedExternalEventProcessor.ts deleted file mode 100644 index 7b50c595e..000000000 --- a/packages/transport/src/modules/sync/externalEventProcessors/RelationshipChangeCompletedExternalEventProcessor.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { RelationshipChangedEvent } from "../../../events"; -import { Relationship } from "../../relationships/local/Relationship"; -import { BackboneExternalEvent } from "../backbone/BackboneExternalEvent"; -import { ExternalEventProcessor } from "./ExternalEventProcessor"; - -export class RelationshipChangeCompletedExternalEventProcessor extends ExternalEventProcessor { - public override async execute(externalEvent: BackboneExternalEvent): Promise { - const payload = externalEvent.payload as { changeId: string }; - const relationship = await this.accountController.relationships.applyChangeById(payload.changeId); - - if (relationship) { - this.eventBus.publish(new RelationshipChangedEvent(this.ownAddress, relationship)); - return relationship; - } - return; - } -} diff --git a/packages/transport/src/modules/sync/externalEventProcessors/RelationshipChangeCreatedExternalEventProcessor.ts b/packages/transport/src/modules/sync/externalEventProcessors/RelationshipStatusChangedExternalEventProcessor.ts similarity index 76% rename from packages/transport/src/modules/sync/externalEventProcessors/RelationshipChangeCreatedExternalEventProcessor.ts rename to packages/transport/src/modules/sync/externalEventProcessors/RelationshipStatusChangedExternalEventProcessor.ts index 511a22b92..5b46af334 100644 --- a/packages/transport/src/modules/sync/externalEventProcessors/RelationshipChangeCreatedExternalEventProcessor.ts +++ b/packages/transport/src/modules/sync/externalEventProcessors/RelationshipStatusChangedExternalEventProcessor.ts @@ -3,10 +3,10 @@ import { Relationship } from "../../relationships/local/Relationship"; import { BackboneExternalEvent } from "../backbone/BackboneExternalEvent"; import { ExternalEventProcessor } from "./ExternalEventProcessor"; -export class RelationshipChangeCreatedExternalEventProcessor extends ExternalEventProcessor { +export class RelationshipStatusChangedExternalEventProcessor extends ExternalEventProcessor { public override async execute(externalEvent: BackboneExternalEvent): Promise { - const payload = externalEvent.payload as { changeId: string; relationshipId: string }; - const relationship = await this.accountController.relationships.applyChangeById(payload.changeId); + const payload = externalEvent.payload as { relationshipId: string }; + const relationship = await this.accountController.relationships.applyRelationshipStatusChangedEvent(payload.relationshipId); if (relationship) { this.eventBus.publish(new RelationshipChangedEvent(this.ownAddress, relationship)); diff --git a/packages/transport/src/modules/sync/externalEventProcessors/index.ts b/packages/transport/src/modules/sync/externalEventProcessors/index.ts index 9fcb7e88d..dcc78f771 100644 --- a/packages/transport/src/modules/sync/externalEventProcessors/index.ts +++ b/packages/transport/src/modules/sync/externalEventProcessors/index.ts @@ -1,5 +1,4 @@ export * from "./ExternalEventProcessorRegistry"; export * from "./MessageDeliveredExternalEventProcessor"; export * from "./MessageReceivedExternalEventProcessor"; -export * from "./RelationshipChangeCompletedExternalEventProcessor"; -export * from "./RelationshipChangeCreatedExternalEventProcessor"; +export * from "./RelationshipStatusChangedExternalEventProcessor"; diff --git a/packages/transport/test/end2end/End2End.test.ts b/packages/transport/test/end2end/End2End.test.ts index 333b5b169..481d82896 100644 --- a/packages/transport/test/end2end/End2End.test.ts +++ b/packages/transport/test/end2end/End2End.test.ts @@ -1,16 +1,7 @@ import { IDatabaseConnection } from "@js-soft/docdb-access-abstractions"; import { JSONWrapper, Serializable } from "@js-soft/ts-serval"; import { CoreBuffer } from "@nmshd/crypto"; -import { - AccountController, - CoreDate, - FileReference, - RelationshipChangeStatus, - RelationshipChangeType, - RelationshipStatus, - TokenContentRelationshipTemplate, - Transport -} from "../../src"; +import { AccountController, CoreDate, FileReference, RelationshipStatus, TokenContentRelationshipTemplate, Transport } from "../../src"; import { TestUtil } from "../testHelpers/TestUtil"; describe("AccountTest", function () { @@ -95,7 +86,7 @@ describe("RelationshipTest: Accept", function () { // Send Request const request = await to.relationships.sendRelationship({ template: templateTo, - content: { + creationContent: { mycontent: "request" } }); @@ -107,10 +98,11 @@ describe("RelationshipTest: Accept", function () { expect(request.cache!.template.id.toString()).toStrictEqual(templateTo.id.toString()); expect(request.cache!.template.isOwn).toBe(false); - expect(request.cache!.creationChange.type).toStrictEqual(RelationshipChangeType.Creation); - expect(request.cache!.creationChange.status).toStrictEqual(RelationshipChangeStatus.Pending); expect(request.status).toStrictEqual(RelationshipStatus.Pending); + expect(request.cache?.auditLog).toHaveLength(1); + expect(request.cache!.auditLog[0].newStatus).toBe(RelationshipStatus.Pending); + // Accept relationship const syncedRelationships = await TestUtil.syncUntilHasRelationships(from); expect(syncedRelationships).toHaveLength(1); @@ -123,22 +115,16 @@ describe("RelationshipTest: Accept", function () { expect(templateResponseContent.value).toHaveProperty("mycontent"); expect(templateResponseContent.value.mycontent).toBe("template"); - expect(pendingRelationship.cache!.creationChange.type).toStrictEqual(RelationshipChangeType.Creation); - expect(pendingRelationship.cache!.creationChange.status).toStrictEqual(RelationshipChangeStatus.Pending); expect(pendingRelationship.status).toStrictEqual(RelationshipStatus.Pending); - const acceptedRelationshipFromSelf = await from.relationships.acceptChange(pendingRelationship.cache!.creationChange, { - mycontent: "acceptContent" - }); + const acceptedRelationshipFromSelf = await from.relationships.accept(relationshipId); expect(acceptedRelationshipFromSelf.id.toString()).toStrictEqual(relationshipId.toString()); expect(acceptedRelationshipFromSelf.status).toStrictEqual(RelationshipStatus.Active); - expect(acceptedRelationshipFromSelf.cache!.creationChange.status).toStrictEqual(RelationshipChangeStatus.Accepted); - expect(acceptedRelationshipFromSelf.peer).toBeDefined(); - expect(acceptedRelationshipFromSelf.peer.address.toString()).toStrictEqual(acceptedRelationshipFromSelf.cache!.creationChange.request.createdBy.toString()); - const acceptedContentSelf = acceptedRelationshipFromSelf.cache!.creationChange.response?.content as any; - expect(acceptedContentSelf).toBeInstanceOf(JSONWrapper); - expect(acceptedContentSelf.value.mycontent).toBe("acceptContent"); + expect(acceptedRelationshipFromSelf.cache?.auditLog).toHaveLength(2); + expect(acceptedRelationshipFromSelf.cache!.auditLog[1].newStatus).toBe(RelationshipStatus.Active); + + expect(acceptedRelationshipFromSelf.peer).toBeDefined(); // Get accepted relationship const syncedRelationshipsPeer = await TestUtil.syncUntilHasRelationships(to); @@ -147,13 +133,11 @@ describe("RelationshipTest: Accept", function () { expect(acceptedRelationshipPeer.id.toString()).toStrictEqual(relationshipId.toString()); expect(acceptedRelationshipPeer.status).toStrictEqual(RelationshipStatus.Active); - expect(acceptedRelationshipPeer.cache!.creationChange.status).toStrictEqual(RelationshipChangeStatus.Accepted); + + expect(acceptedRelationshipPeer.cache?.auditLog).toHaveLength(2); + expect(acceptedRelationshipPeer.cache!.auditLog[1].newStatus).toBe(RelationshipStatus.Active); expect(acceptedRelationshipPeer.peer).toBeDefined(); expect(acceptedRelationshipPeer.peer.address.toString()).toStrictEqual(templateTo.cache?.identity.address.toString()); - - const acceptedContentPeer = acceptedRelationshipPeer.cache!.creationChange.response?.content as any; - expect(acceptedContentPeer).toBeInstanceOf(JSONWrapper); - expect(acceptedContentPeer.value.mycontent).toBe("acceptContent"); }); }); @@ -218,7 +202,7 @@ describe("RelationshipTest: Reject", function () { const request = await to.relationships.sendRelationship({ template: templateTo, - content: { + creationContent: { mycontent: "request" } }); @@ -230,8 +214,6 @@ describe("RelationshipTest: Reject", function () { expect(request.cache!.template.id.toString()).toStrictEqual(templateTo.id.toString()); expect(request.cache!.template.isOwn).toBe(false); - expect(request.cache!.creationChange.type).toStrictEqual(RelationshipChangeType.Creation); - expect(request.cache!.creationChange.status).toStrictEqual(RelationshipChangeStatus.Pending); expect(request.status).toStrictEqual(RelationshipStatus.Pending); // Reject relationship @@ -245,24 +227,17 @@ describe("RelationshipTest: Reject", function () { expect(templateResponseContent.value).toHaveProperty("mycontent"); expect(templateResponseContent.value.mycontent).toBe("template"); - expect(pendingRelationship.cache!.creationChange.type).toStrictEqual(RelationshipChangeType.Creation); - expect(pendingRelationship.cache!.creationChange.status).toStrictEqual(RelationshipChangeStatus.Pending); expect(pendingRelationship.status).toStrictEqual(RelationshipStatus.Pending); - const rejectedRelationshipFromSelf = await from.relationships.rejectChange(pendingRelationship.cache!.creationChange, { - mycontent: "rejectContent" - }); + const rejectedRelationshipFromSelf = await from.relationships.reject(relationshipId); expect(rejectedRelationshipFromSelf.id.toString()).toStrictEqual(relationshipId.toString()); expect(rejectedRelationshipFromSelf.status).toStrictEqual(RelationshipStatus.Rejected); - expect(rejectedRelationshipFromSelf.cache!.creationChange.status).toStrictEqual(RelationshipChangeStatus.Rejected); - expect(rejectedRelationshipFromSelf.peer).toBeDefined(); - expect(rejectedRelationshipFromSelf.peer.address.toString()).toStrictEqual(rejectedRelationshipFromSelf.cache!.creationChange.request.createdBy.toString()); + expect(rejectedRelationshipFromSelf.cache?.auditLog).toHaveLength(2); + expect(rejectedRelationshipFromSelf.cache!.auditLog[1].newStatus).toBe(RelationshipStatus.Rejected); - const rejectionContentSelf = rejectedRelationshipFromSelf.cache!.creationChange.response?.content as any; - expect(rejectionContentSelf).toBeInstanceOf(JSONWrapper); - expect(rejectionContentSelf.value.mycontent).toBe("rejectContent"); + expect(rejectedRelationshipFromSelf.peer).toBeDefined(); - // Get accepted relationship + // Get rejected relationship const syncedRelationshipsPeer = await TestUtil.syncUntilHasRelationships(to); expect(syncedRelationshipsPeer).toHaveLength(1); const rejectedRelationshipPeer = syncedRelationshipsPeer[0]; @@ -270,13 +245,10 @@ describe("RelationshipTest: Reject", function () { expect(rejectedRelationshipPeer.id.toString()).toStrictEqual(relationshipId.toString()); expect(rejectedRelationshipPeer.status).toStrictEqual(RelationshipStatus.Rejected); - expect(rejectedRelationshipPeer.cache!.creationChange.status).toStrictEqual(RelationshipChangeStatus.Rejected); + expect(rejectedRelationshipPeer.cache?.auditLog).toHaveLength(2); + expect(rejectedRelationshipPeer.cache!.auditLog[1].newStatus).toBe(RelationshipStatus.Rejected); expect(rejectedRelationshipPeer.peer).toBeDefined(); expect(rejectedRelationshipPeer.peer.address.toString()).toStrictEqual(templateTo.cache?.identity.address.toString()); - - const rejectionContentPeer = rejectedRelationshipPeer.cache!.creationChange.response?.content as any; - expect(rejectionContentPeer).toBeInstanceOf(JSONWrapper); - expect(rejectionContentPeer.value.mycontent).toBe("rejectContent"); }); }); @@ -344,7 +316,7 @@ describe("RelationshipTest: Revoke", function () { const request = await requestor.relationships.sendRelationship({ template: templateRequestor, - content: { + creationContent: { mycontent: "request" } }); @@ -357,8 +329,6 @@ describe("RelationshipTest: Revoke", function () { expect(request.cache!.template.id.toString()).toStrictEqual(templateRequestor.id.toString()); expect(request.cache!.template.isOwn).toBe(false); - expect(request.cache!.creationChange.type).toStrictEqual(RelationshipChangeType.Creation); - expect(request.cache!.creationChange.status).toStrictEqual(RelationshipChangeStatus.Pending); expect(request.status).toStrictEqual(RelationshipStatus.Pending); // Revoke relationship @@ -373,24 +343,17 @@ describe("RelationshipTest: Revoke", function () { const templateResponseContent = pendingRelationship.cache!.template.cache!.content as JSONWrapper; expect(templateResponseContent.value).toHaveProperty("mycontent"); expect(templateResponseContent.value.mycontent).toBe("template"); - - expect(pendingRelationship.cache!.creationChange.type).toStrictEqual(RelationshipChangeType.Creation); - expect(pendingRelationship.cache!.creationChange.status).toStrictEqual(RelationshipChangeStatus.Pending); expect(pendingRelationship.status).toStrictEqual(RelationshipStatus.Pending); - const revokedRelationshipSelf = await requestor.relationships.revokeChange(pendingRelationship.cache!.creationChange, { - mycontent: "revokeContent" - }); + const revokedRelationshipSelf = await requestor.relationships.revoke(relationshipId); expect(revokedRelationshipSelf.status).toStrictEqual(RelationshipStatus.Revoked); expect(revokedRelationshipSelf.id.toString()).toStrictEqual(relationshipId.toString()); expect(revokedRelationshipSelf.status).toStrictEqual(RelationshipStatus.Revoked); - expect(revokedRelationshipSelf.cache!.creationChange.status).toStrictEqual(RelationshipChangeStatus.Revoked); + expect(revokedRelationshipSelf.cache?.auditLog).toHaveLength(2); + expect(revokedRelationshipSelf.cache!.auditLog[1].newStatus).toBe(RelationshipStatus.Revoked); expect(revokedRelationshipSelf.peer).toBeDefined(); expect(revokedRelationshipSelf.peer.address.toString()).toStrictEqual(revokedRelationshipSelf.cache!.template.cache?.identity.address.toString()); - const revocationContentSelf = revokedRelationshipSelf.cache!.creationChange.response?.content as any; - expect(revocationContentSelf).toBeInstanceOf(JSONWrapper); - expect(revocationContentSelf.value.mycontent).toBe("revokeContent"); // Get revoked relationship const syncedRelationshipsPeer = await TestUtil.syncUntilHasRelationships(templator); @@ -399,13 +362,9 @@ describe("RelationshipTest: Revoke", function () { expect(revokedRelationshipPeer.status).toStrictEqual(RelationshipStatus.Revoked); expect(revokedRelationshipPeer.id.toString()).toStrictEqual(relationshipId.toString()); expect(revokedRelationshipPeer.status).toStrictEqual(RelationshipStatus.Revoked); - expect(revokedRelationshipPeer.cache!.creationChange.status).toStrictEqual(RelationshipChangeStatus.Revoked); + expect(revokedRelationshipPeer.cache?.auditLog).toHaveLength(2); + expect(revokedRelationshipPeer.cache!.auditLog[1].newStatus).toBe(RelationshipStatus.Revoked); expect(revokedRelationshipPeer.peer).toBeDefined(); - expect(revokedRelationshipPeer.peer.address.toString()).toStrictEqual(revokedRelationshipPeer.cache!.creationChange.request.createdBy.toString()); - - const revocationContentPeer = revokedRelationshipPeer.cache!.creationChange.response?.content as any; - expect(revocationContentPeer).toBeInstanceOf(JSONWrapper); - expect(revocationContentPeer.value.mycontent).toBe("revokeContent"); }); test("should handle an incoming relationship request which was already revoked by the sender", async function () { @@ -443,29 +402,21 @@ describe("RelationshipTest: Revoke", function () { const pendingRelationship = await requestor.relationships.sendRelationship({ template: templateRequestor, - content: { + creationContent: { mycontent: "request" } }); // Revoke relationship - const revokedRelationshipSelf = await requestor.relationships.revokeChange(pendingRelationship.cache!.creationChange, { - mycontent: "revokeContent" - }); + const revokedRelationshipSelf = await requestor.relationships.revoke(pendingRelationship.id); expect(revokedRelationshipSelf.status).toStrictEqual(RelationshipStatus.Revoked); - const revocationContentSelf = revokedRelationshipSelf.cache!.creationChange.response?.content as any; - expect(revocationContentSelf).toBeInstanceOf(JSONWrapper); - expect(revocationContentSelf.value.mycontent).toBe("revokeContent"); // Get revoked relationship - const syncedRelationshipsPeer = await TestUtil.syncUntilHasRelationships(templator); - expect(syncedRelationshipsPeer).toHaveLength(1); - const revokedRelationshipPeer = syncedRelationshipsPeer[0]; + await TestUtil.syncUntilHasRelationships(templator, 2); // wait for pending and revoked + const relationshipsPeer = await templator.relationships.getRelationships({}); + expect(relationshipsPeer).toHaveLength(1); + const revokedRelationshipPeer = relationshipsPeer[0]; expect(revokedRelationshipPeer.status).toStrictEqual(RelationshipStatus.Revoked); - - const revocationContentPeer = revokedRelationshipPeer.cache!.creationChange.response?.content as any; - expect(revocationContentPeer).toBeInstanceOf(JSONWrapper); - expect(revocationContentPeer.value.mycontent).toBe("revokeContent"); }); }); diff --git a/packages/transport/test/modules/PublicAPI.test.ts b/packages/transport/test/modules/PublicAPI.test.ts index 3303307e8..f6c2a901c 100644 --- a/packages/transport/test/modules/PublicAPI.test.ts +++ b/packages/transport/test/modules/PublicAPI.test.ts @@ -108,23 +108,23 @@ publicFunctions[RelationshipsController.name] = [ nameof((r) => r.verify), nameof((r) => r.verifyIdentity), nameof((r) => r.sendRelationship), - nameof((r) => r.acceptChange), - nameof((r) => r.rejectChange), - nameof((r) => r.revokeChange), + nameof((r) => r.accept), + nameof((r) => r.reject), + nameof((r) => r.revoke), nameof((r) => r.updateCache) ]; publicFunctions[RelationshipSecretController.name] = [ nameof((r) => r.init), nameof((r) => r.createRequestorSecrets), nameof((r) => r.createTemplatorSecrets), - nameof((r) => r.getPublicResponse), + nameof((r) => r.getPublicCreationResponseContentCrypto), nameof((r) => r.convertSecrets), nameof((r) => r.deleteSecretForRequest), nameof((r) => r.decryptTemplate), nameof((r) => r.verifyTemplate), - nameof((r) => r.encryptRequest), + nameof((r) => r.encryptCreationContent), nameof((r) => r.encrypt), - nameof((r) => r.decryptRequest), + nameof((r) => r.decryptCreationContent), nameof((r) => r.createTemplateKey), nameof((r) => r.decryptPeer), nameof((r) => r.verifyOwn), diff --git a/packages/transport/test/modules/relationships/RelationshipsController.test.ts b/packages/transport/test/modules/relationships/RelationshipsController.test.ts index bfda40207..a309993f8 100644 --- a/packages/transport/test/modules/relationships/RelationshipsController.test.ts +++ b/packages/transport/test/modules/relationships/RelationshipsController.test.ts @@ -1,19 +1,5 @@ import { IDatabaseConnection } from "@js-soft/docdb-access-abstractions"; -import { - AccountController, - CachedRelationship, - CoreDate, - CoreId, - Identity, - Relationship, - RelationshipChangeRequest, - RelationshipChangeResponse, - RelationshipChangeStatus, - RelationshipChangeType, - RelationshipStatus, - RelationshipTemplate, - Transport -} from "../../../src"; +import { AccountController, CachedRelationship, CoreDate, CoreId, Identity, Relationship, RelationshipStatus, RelationshipTemplate, Transport } from "../../../src"; import { TestUtil } from "../../testHelpers/TestUtil"; describe("RelationshipsController", function () { @@ -37,29 +23,11 @@ describe("RelationshipsController", function () { expect(relationship.peer.address).toStrictEqual(peerAccount.identity.address); expect(relationship.cache!.template).toBeInstanceOf(RelationshipTemplate); - expect(relationship.cache!.changes).toHaveLength(1); expect(relationship.cache).toBeInstanceOf(CachedRelationship); expect(relationship.cachedAt).toBeInstanceOf(CoreDate); expect(relationship.cachedAt!.isWithin(TestUtil.tempDateThreshold, TestUtil.tempDateThreshold, creationTime)).toBe(true); - - const creation = relationship.cache!.changes[0]; - expect(creation.relationshipId.toString()).toBe(relationship.id.toString()); - expect(creation.status).toStrictEqual(RelationshipChangeStatus.Accepted); - expect(creation.type).toStrictEqual(RelationshipChangeType.Creation); - - expect(creation.request).toBeInstanceOf(RelationshipChangeRequest); - expect(creation.request.createdAt.isWithin(TestUtil.tempDateThreshold, TestUtil.tempDateThreshold, creationTime)).toBe(true); - if (creation.request.createdBy.equals(ownAccount.identity.address)) { - expect(creation.request.createdBy.toString()).toBe(ownAccount.identity.address.toString()); - expect(creation.response?.createdBy.toString()).toBe(peerAccount.identity.address.toString()); - } else { - expect(creation.response?.createdBy.toString()).toBe(ownAccount.identity.address.toString()); - expect(creation.request.createdBy.toString()).toBe(peerAccount.identity.address.toString()); - } - - expect(creation.response).toBeInstanceOf(RelationshipChangeResponse); - expect(creation.response!.createdAt.isWithin(TestUtil.tempDateThreshold, TestUtil.tempDateThreshold, creationTime)).toBe(true); + expect(relationship.cache!.creationContent).toBeDefined(); expect(relationship.cache!.lastMessageReceivedAt).toBeUndefined(); expect(relationship.cache!.lastMessageSentAt).toBeUndefined(); diff --git a/packages/transport/test/modules/relationships/RelationshipsCustomContent.test.ts b/packages/transport/test/modules/relationships/RelationshipsCustomContent.test.ts index e5d90abbf..356647ade 100644 --- a/packages/transport/test/modules/relationships/RelationshipsCustomContent.test.ts +++ b/packages/transport/test/modules/relationships/RelationshipsCustomContent.test.ts @@ -1,6 +1,6 @@ import { IDatabaseConnection } from "@js-soft/docdb-access-abstractions"; import { JSONWrapper, Serializable } from "@js-soft/ts-serval"; -import { AccountController, RelationshipChangeRequest, Transport } from "../../../src"; +import { AccountController, Transport } from "../../../src"; import { TestUtil } from "../../testHelpers/TestUtil"; describe("Relationships Custom Content", function () { @@ -33,18 +33,15 @@ describe("Relationships Custom Content", function () { const template = await TestUtil.fetchRelationshipTemplateFromTokenReference(recipient, tokenReference); const customContent = Serializable.fromAny({ content: "TestToken" }); const relRecipient = await TestUtil.sendRelationship(recipient, template, customContent); - const relRecipientRequest = relRecipient.cache!.creationChange.request; + const relRecipientContent = relRecipient.cache!.creationContent; const relSender = await TestUtil.syncUntilHasRelationships(sender); - const relSenderRequest = relSender[0].cache!.creationChange.request; + const relSenderRequest = relSender[0].cache!.creationContent; - expect(relRecipientRequest).toBeInstanceOf(RelationshipChangeRequest); - expect(relSenderRequest).toBeInstanceOf(RelationshipChangeRequest); - - expect(relRecipientRequest.content).toBeInstanceOf(JSONWrapper); - const recipientToken = relRecipientRequest.content as JSONWrapper; - expect(relSenderRequest.content).toBeInstanceOf(JSONWrapper); - const senderToken = relSenderRequest.content as JSONWrapper; + expect(relRecipientContent).toBeInstanceOf(JSONWrapper); + const recipientToken = relRecipientContent as JSONWrapper; + expect(relSenderRequest).toBeInstanceOf(JSONWrapper); + const senderToken = relSenderRequest as JSONWrapper; expect((recipientToken.toJSON() as any).content).toBe("TestToken"); expect((senderToken.toJSON() as any).content).toBe("TestToken"); diff --git a/packages/transport/test/modules/sync/SyncController.error.test.ts b/packages/transport/test/modules/sync/SyncController.error.test.ts index 210ebdf1b..75232279a 100644 --- a/packages/transport/test/modules/sync/SyncController.error.test.ts +++ b/packages/transport/test/modules/sync/SyncController.error.test.ts @@ -35,7 +35,7 @@ describe("SyncController.error", function () { await requestorDevice.relationships.sendRelationship({ template: templateOnRequestorDevice, - content: { someMessageContent: "someMessageContent" } + creationContent: { someMessageContent: "someMessageContent" } }); const error = await TestUtil.syncUntilHasError(templatorDevice2); diff --git a/packages/transport/test/modules/sync/SyncController.relationships.test.ts b/packages/transport/test/modules/sync/SyncController.relationships.test.ts index c6f73e6a4..ba0abee2c 100644 --- a/packages/transport/test/modules/sync/SyncController.relationships.test.ts +++ b/packages/transport/test/modules/sync/SyncController.relationships.test.ts @@ -31,7 +31,7 @@ describe("RelationshipSync", function () { const createdRelationship = await requestorDevice1.relationships.sendRelationship({ template: templateOnRequestorDevice1, - content: { someMessageContent: "someMessageContent" } + creationContent: { someMessageContent: "someMessageContent" } }); await requestorDevice1.syncDatawallet(); @@ -49,10 +49,7 @@ describe("RelationshipSync", function () { await TestUtil.syncUntilHasRelationships(templatorDevice); - const relationshipOnTemplatorDevice = await templatorDevice.relationships.getRelationship(createdRelationship.id); - await templatorDevice.relationships.acceptChange(relationshipOnTemplatorDevice!.cache!.creationChange, { - someResponseContent: "someResponseContent" - }); + await templatorDevice.relationships.accept(createdRelationship.id); let relationshipOnRequestorDevice1 = (await requestorDevice2.relationships.getRelationship(createdRelationship.id))!; expect(relationshipOnRequestorDevice1.status).toStrictEqual(RelationshipStatus.Pending); @@ -89,7 +86,7 @@ describe("RelationshipSync", function () { const createdRelationship = await requestorDevice1.relationships.sendRelationship({ template: templateOnRequestorDevice1, - content: { someMessageContent: "someMessageContent" } + creationContent: { someMessageContent: "someMessageContent" } }); await requestorDevice1.syncDatawallet(); @@ -100,9 +97,7 @@ describe("RelationshipSync", function () { const relationships = await TestUtil.syncUntilHasRelationships(templatorDevice); const relationshipOnTemplatorDevice = relationships[0]; - await templatorDevice.relationships.acceptChange(relationshipOnTemplatorDevice.cache!.creationChange, { - someResponseContent: "someResponseContent" - }); + await templatorDevice.relationships.accept(relationshipOnTemplatorDevice.id); relationshipOnRequestorDevice2 = await requestorDevice2.relationships.getRelationship(createdRelationship.id); expect(relationshipOnRequestorDevice2).toBeUndefined(); @@ -140,7 +135,7 @@ describe("RelationshipSync", function () { const createdRelationship = await requestorDevice.relationships.sendRelationship({ template: templateOnRequestorDevice1, - content: { someMessageContent: "someMessageContent" } + creationContent: { someMessageContent: "someMessageContent" } }); await requestorDevice.syncDatawallet(); @@ -153,10 +148,7 @@ describe("RelationshipSync", function () { await TestUtil.syncUntilHasRelationships(templatorDevice2); - const relationshipOnTemplatorDevice = await templatorDevice2.relationships.getRelationship(createdRelationship.id); - await templatorDevice2.relationships.acceptChange(relationshipOnTemplatorDevice!.cache!.creationChange, { - someResponseContent: "someResponseContent" - }); + await templatorDevice2.relationships.accept(createdRelationship.id); await templatorDevice2.syncDatawallet(); diff --git a/packages/transport/test/testHelpers/TestUtil.ts b/packages/transport/test/testHelpers/TestUtil.ts index bcd6413be..32b1a530e 100644 --- a/packages/transport/test/testHelpers/TestUtil.ts +++ b/packages/transport/test/testHelpers/TestUtil.ts @@ -315,7 +315,7 @@ export class TestUtil { await to.relationships.sendRelationship({ template: templateTo, - content: { + creationContent: { mycontent: "request" } }); @@ -326,7 +326,7 @@ export class TestUtil { const pendingRelationship = syncedRelationships[0]; expect(pendingRelationship.status).toStrictEqual(RelationshipStatus.Pending); - const rejectedRelationshipFromSelf = await from.relationships.rejectChange(pendingRelationship.cache!.creationChange, {}); + const rejectedRelationshipFromSelf = await from.relationships.reject(pendingRelationship.id); expect(rejectedRelationshipFromSelf.status).toStrictEqual(RelationshipStatus.Rejected); // Get accepted relationship @@ -349,7 +349,7 @@ export class TestUtil { const relRequest = await to.relationships.sendRelationship({ template: templateTo, - content: { + creationContent: { mycontent: "request" } }); @@ -360,7 +360,7 @@ export class TestUtil { const pendingRelationship = syncedRelationships[0]; expect(pendingRelationship.status).toStrictEqual(RelationshipStatus.Pending); - const acceptedRelationshipFromSelf = await from.relationships.acceptChange(pendingRelationship.cache!.creationChange, {}); + const acceptedRelationshipFromSelf = await from.relationships.accept(pendingRelationship.id); expect(acceptedRelationshipFromSelf.status).toStrictEqual(RelationshipStatus.Active); // Get accepted relationship @@ -404,8 +404,8 @@ export class TestUtil { return syncResult; } - public static async syncUntilHasRelationships(accountController: AccountController): Promise { - const syncResult = await TestUtil.syncUntil(accountController, (syncResult) => syncResult.relationships.length > 0); + public static async syncUntilHasRelationships(accountController: AccountController, numberOfRelationships = 1): Promise { + const syncResult = await TestUtil.syncUntil(accountController, (syncResult) => syncResult.relationships.length > numberOfRelationships - 1); return syncResult.relationships; } @@ -481,7 +481,7 @@ export class TestUtil { } return await account.relationships.sendRelationship({ template: template, - content: body + creationContent: body }); } diff --git a/packages/transport/test/utils/Reflection.test.ts b/packages/transport/test/utils/Reflection.test.ts index b10b97366..1575c1454 100644 --- a/packages/transport/test/utils/Reflection.test.ts +++ b/packages/transport/test/utils/Reflection.test.ts @@ -31,15 +31,12 @@ import { MessageEnvelopeRecipient, MessageSigned, Relationship, - RelationshipChange, - RelationshipChangeRequest, - RelationshipChangeResponse, - RelationshipCreationChangeRequestCipher, - RelationshipCreationChangeRequestContentWrapper, - RelationshipCreationChangeRequestSigned, - RelationshipCreationChangeResponseCipher, - RelationshipCreationChangeResponseContentWrapper, - RelationshipCreationChangeResponseSigned, + RelationshipCreationContentCipher, + RelationshipCreationContentSigned, + RelationshipCreationContentWrapper, + RelationshipCreationResponseContentCipher, + RelationshipCreationResponseContentSigned, + RelationshipCreationResponseContentWrapper, RelationshipTemplate, RelationshipTemplateContentWrapper, RelationshipTemplatePublicKey, @@ -83,16 +80,13 @@ const transportClassNames: string[] = [ `${Message.name}@1`, `${CachedRelationshipTemplate.name}@1`, `${Relationship.name}@1`, - `${RelationshipChange.name}@1`, - `${RelationshipChangeRequest.name}@1`, - `${RelationshipChangeResponse.name}@1`, `${RelationshipTemplate.name}@1`, - `${RelationshipCreationChangeRequestCipher.name}@1`, - `${RelationshipCreationChangeRequestContentWrapper.name}@1`, - `${RelationshipCreationChangeRequestSigned.name}@1`, - `${RelationshipCreationChangeResponseCipher.name}@1`, - `${RelationshipCreationChangeResponseContentWrapper.name}@1`, - `${RelationshipCreationChangeResponseSigned.name}@1`, + `${RelationshipCreationContentCipher.name}@1`, + `${RelationshipCreationContentWrapper.name}@1`, + `${RelationshipCreationContentSigned.name}@1`, + `${RelationshipCreationResponseContentCipher.name}@1`, + `${RelationshipCreationResponseContentWrapper.name}@1`, + `${RelationshipCreationResponseContentSigned.name}@1`, `${RelationshipTemplateContentWrapper.name}@1`, `${RelationshipTemplatePublicKey.name}@1`, `${RelationshipTemplateSigned.name}@1`,