Skip to content

Commit

Permalink
Refactor/adapt runtime to new relationships api (#90)
Browse files Browse the repository at this point in the history
* chore: adapt accept, reject, revoke runtime use cases

* chore: change relationship classes

* chore: adapt transmission types

* chore: rename transmission request/response types

* fix: relationships backbone response type

* chore: adapt RelationshipsController

* chore: adapt ExternalEventsProcessor

* chore: adapt the relationship use cases

* chore: adapt request response type

* chore: adapt completing incoming requests

* refactor: content type names

* feat: get relationship with audit logs, fix types

* chore: adapt outgoing request controller and use cases

* chore: adapt relationship (change) dtos

* chore: adapt request module

* chore: adapt index file

* refactor: auditLog is transport relationship member

* chore: adapt relationship getter use cases

* chore: rename use cases

* chore: adapt transport validation

* chore: adapt schemas and further renaming

* chore: rename relation creation request content file

* refactor: renaming types

* chore: adapt request consumption tests

* refactor: rename content to creation content in sendRelationship

* fix: relationship creation request type name

* chore: adapt final consumption and transport tests

* chore: adapt runtime tests

* fix: adapt changes in transport tests

* chore: adapt the new external event processor

* chore: use correct backbone version

* fix: adapt to backbone signature

* fix: await promise

* fix: update status with relationship

* refactor: move audit log to relationship cache

* refactor: merge the relationship event handlers

* fix: relationship controller flows

* refactor: relationship types

* fix: audit log deserializer

* fix: relationship event payload

* fix: get correct creation/acceptance content

* fix: only decrypt relationship if secrets available

* fix: don't use sync result in a test

* chore: remove comments / debug code

* fix: transport tests, sort audit log

* fix: consumption tests, request controller type

* refactor: remove auditLog flag in useCases

* fix: assorted fixes

* chore: update app-runtime

* fix: exports

* refactor: massively simplify DTO creation

* fix: app runtime tests

* fix: import

* fix: mandatory audit log

* fix: adapt relationship dto/dvo, remove null checks

* refactor/fix: add audit log to test factory, remove redundant method

* fix: de-duplicate functions

* fix: redo old behaviour

* fix: casing

* fix: update event behaviour

* fix: re-add some tests

* fix: make peer an address again

* refactor: mandatory payload in put

* feat: add the relationship changes to the relationship dto

* chore: add validation to outoing request controller

* refactor: relationshipCreationContent instead of CreationRequestContent

* refactor/fix: auditLog to relationshipAuditLog, add createdByDevice

* refactor: simplify RelationshipMapper

* chore: remove unused runtime error

* fix: re-add check, remove throw

* fix: add createdByDevice to relationship DTO

* refactor: cleaner function call

* test: add old relationship change tests; test for creation content

* fix: add createdByDevice to audit log method

* fix: wrong type annotations

* fix: condition in createRequestFromTemplateResponse

* fix: add createdBDevice to TestObjectFactory audit logs

* refactor: rename auditLog file

* refactor: cosmetic changes

* refactor: correct audit log in test object factory

* fix: add oldStatus

* refactor: remove empty acceptanceContents

* refactor: request/response to creation-/acceptanceContent

* chore: adapt backbone return types, type check

* fix: correctly use types

* refactor: no type extension in backboneGetRelationships

* fix: catch undefined creation content

* refactor: fail fast undefined creation content

* fix: this was supposed to be the previous commit

* refactor: add relationship prefix to audit log

* refactor: rename relationship event processor

* refactor: change checks, use JSONWrapper for creation content

* refactor: split audit log class

* fix: update import

* refactor: acceptanceContent -> creationResponseContent

* refactor: combine events

* fix: naming

* chore: bump backbone

* chore: add admin ui to compose

* fix: naming

* chore: naming

* fix: Relationships

* fix: re-add import

* fix: update types and errors

* chore: bump backbone

* fix: add enum validators

* chore: any is always nullable

* fix: pass creation content

* fix: throw error again

* chore: remove unused content

* fix: make publicCreationResponseContentCrypto required

* chore: update validate annotation

---------

Co-authored-by: Julian König <[email protected]>
Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
  • Loading branch information
3 people authored Apr 24, 2024
1 parent c317bb1 commit 36f9762
Show file tree
Hide file tree
Showing 95 changed files with 919 additions and 1,589 deletions.
2 changes: 1 addition & 1 deletion .dev/compose.backbone.env
Original file line number Diff line number Diff line change
@@ -1 +1 @@
BACKBONE_VERSION=5.0.0
BACKBONE_VERSION=6.0.0-alpha.4
18 changes: 18 additions & 0 deletions .dev/compose.backbone.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
Original file line number Diff line number Diff line change
@@ -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
});
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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";
Expand Down Expand Up @@ -43,34 +43,24 @@ export class AppRelationshipFacade extends AppRuntimeFacade {
return UserfriendlyResult.ok<RelationshipItemsDVO, UserfriendlyApplicationError>(dvo);
}

public async acceptRelationshipCreationChange(relationshipId: string, content: any): Promise<UserfriendlyResult<IdentityDVO>> {
const result = await this.transportServices.relationships.getRelationship({ id: relationshipId });
if (result.isError) {
return await this.parseErrorResult<IdentityDVO>(result);
}

const changeId = result.value.changes[0].id;
return await this.acceptRelationshipChange({ relationshipId, changeId, content });
public async createRelationship(request: CreateRelationshipRequest): Promise<UserfriendlyResult<IdentityDVO>> {
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<UserfriendlyResult<IdentityDVO>> {
const result = await this.transportServices.relationships.getRelationship({ id: relationshipId });
if (result.isError) {
return await this.parseErrorResult<IdentityDVO>(result);
}

const changeId = result.value.changes[0].id;
return await this.rejectRelationshipChange({ relationshipId, changeId, content });
public async acceptRelationship(request: AcceptRelationshipRequest): Promise<UserfriendlyResult<IdentityDVO>> {
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<UserfriendlyResult<IdentityDVO>> {
const result = await this.transportServices.relationships.getRelationship({ id: relationshipId });
if (result.isError) {
return await this.parseErrorResult<IdentityDVO>(result);
}
public async rejectRelationship(request: RejectRelationshipRequest): Promise<UserfriendlyResult<IdentityDVO>> {
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<UserfriendlyResult<IdentityDVO>> {
const result = await this.transportServices.relationships.revokeRelationship(request);
return await this.handleResult(result, (v) => this.expander.expandRelationshipDTO(v));
}

public async getRelationships(request: GetRelationshipsRequest): Promise<UserfriendlyResult<IdentityDVO[]>> {
Expand All @@ -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<UserfriendlyResult<IdentityDVO>> {
const result = await this.transportServices.relationships.createRelationship(request);
return await this.handleResult(result, (v) => this.expander.expandRelationshipDTO(v));
}

public async acceptRelationshipChange(request: AcceptRelationshipChangeRequest): Promise<UserfriendlyResult<IdentityDVO>> {
const result = await this.transportServices.relationships.acceptRelationshipChange(request);
return await this.handleResult(result, (v) => this.expander.expandRelationshipDTO(v));
}

public async rejectRelationshipChange(request: RejectRelationshipChangeRequest): Promise<UserfriendlyResult<IdentityDVO>> {
const result = await this.transportServices.relationships.rejectRelationshipChange(request);
return await this.handleResult(result, (v) => this.expander.expandRelationshipDTO(v));
}

public async revokeRelationshipChange(request: RevokeRelationshipChangeRequest): Promise<UserfriendlyResult<IdentityDVO>> {
const result = await this.transportServices.relationships.revokeRelationshipChange(request);
return await this.handleResult(result, (v) => this.expander.expandRelationshipDTO(v));
}
}
Original file line number Diff line number Diff line change
@@ -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";
Expand All @@ -17,32 +17,35 @@ export class OnboardingChangeReceivedModule extends AppRuntimeModule<OnboardingC
}

private async handleOnboardingChangeReceived(event: OnboardingChangeReceivedEvent) {
const change = event.data.change;
const auditLogEntry = event.data.auditLogEntry;
const identity = event.data.identity;
let title = "";
let text = "";
const session = await this.runtime.getOrCreateSession(event.eventTargetAddress);

switch (change.status) {
case RelationshipChangeStatus.Accepted:
switch (auditLogEntry.reason) {
case RelationshipAuditLogEntryReason.AcceptanceOfCreation:
title = "Kontaktanfrage genehmigt";
text = `Du kannst nun mit ${identity.name} kommunizieren`;
break;

case RelationshipChangeStatus.Pending:
case RelationshipAuditLogEntryReason.Creation:
title = "Kontaktanfrage erhalten";
text = `Du hast eine Kontaktanfrage von ${identity.name} erhalten`;
break;

case RelationshipChangeStatus.Rejected:
case RelationshipAuditLogEntryReason.RejectionOfCreation:
title = "Kontaktanfrage abgelehnt";
text = `${identity.name} hat ihre Kontaktanfrage abgelehnt`;
break;

case RelationshipChangeStatus.Revoked:
case RelationshipAuditLogEntryReason.RevocationOfCreation:
title = "Kontaktanfrage zurückgezogen";
text = `${identity.name} hat die Kontaktanfrage zurückgezogen`;
break;

default:
return;
}
await this.runtime.nativeEnvironment.notificationAccess.schedule(title, text, {
callback: async () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -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";
Expand All @@ -18,20 +18,28 @@ export class RelationshipChangedModule extends AppRuntimeModule<RelationshipChan

private async handleRelationshipChanged(event: RelationshipChangedEvent) {
const relationship = event.data;
// Only listen for the creation change (the first one)
if (relationship.changes.length !== 1) return;

const change = relationship.changes[0];
const lastAuditLogEntry = relationship.auditLog[relationship.auditLog.length - 1];

// response doest not exist and request created by the current identity
if (!change.response && change.request.createdBy === event.eventTargetAddress) return;
// Do not process changes that were created by the current user
if (lastAuditLogEntry.createdBy === event.eventTargetAddress) return;

// response exists and created by the current identity
if (change.response && change.response.createdBy === event.eventTargetAddress) return;
switch (lastAuditLogEntry.reason) {
case RelationshipAuditLogEntryReason.Creation:
case RelationshipAuditLogEntryReason.AcceptanceOfCreation:
case RelationshipAuditLogEntryReason.RevocationOfCreation:
case RelationshipAuditLogEntryReason.RejectionOfCreation:
break;

default:
return;
}

const services = await this.runtime.getServices(event.eventTargetAddress);
const relationshipDVO = await services.dataViewExpander.expandRelationshipDTO(relationship);
this.runtime.eventBus.publish(new OnboardingChangeReceivedEvent(event.eventTargetAddress, change, relationship, relationshipDVO));

const eventToPublish = new OnboardingChangeReceivedEvent(event.eventTargetAddress, relationship, lastAuditLogEntry, relationshipDVO);
this.runtime.eventBus.publish(eventToPublish);
}

public stop(): void {
Expand Down
68 changes: 7 additions & 61 deletions packages/app-runtime/test/lib/TestUtil.ts
Original file line number Diff line number Diff line change
Expand Up @@ -198,76 +198,22 @@ export class TestUtil {
mycontent: "request"
}
): Promise<RelationshipDTO> {
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<RelationshipDTO> {
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<RelationshipDTO> {
const acceptedRelationship = (await session.transportServices.relationships.acceptRelationship({ relationshipId })).value;
return acceptedRelationship;
}

public static async rejectRelationship(
session: LocalAccountSession,
relationshipId: string,
content: any = {
mycontent: "rejection"
}
): Promise<RelationshipDTO> {
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<RelationshipDTO> {
const rejectedRelationship = (await session.transportServices.relationships.rejectRelationship({ relationshipId })).value;
return rejectedRelationship;
}

public static async revokeRelationship(
session: LocalAccountSession,
relationshipId: string,
content: any = {
mycontent: "revokation"
}
): Promise<RelationshipDTO> {
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<RelationshipDTO> {
const rejectedRelationship = (await session.transportServices.relationships.revokeRelationship({ relationshipId })).value;
return rejectedRelationship;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -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";

Expand Down Expand Up @@ -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));
Expand Down Expand Up @@ -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());
Expand Down
Original file line number Diff line number Diff line change
@@ -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";

Expand Down Expand Up @@ -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());
Expand Down Expand Up @@ -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));
Expand Down
Loading

0 comments on commit 36f9762

Please sign in to comment.