diff --git a/packages/app-runtime/src/AppStringProcessor.ts b/packages/app-runtime/src/AppStringProcessor.ts index 816f955aa..38a89a5e1 100644 --- a/packages/app-runtime/src/AppStringProcessor.ts +++ b/packages/app-runtime/src/AppStringProcessor.ts @@ -134,7 +134,8 @@ export class AppStringProcessor { // RelationshipTemplates are processed by the RequestModule break; case "Token": - return Result.fail(AppRuntimeErrors.appStringProcessor.notSupportedTokenContent()); + await uiBridge.showToken(account, result.value.value); + break; case "DeviceOnboardingInfo": return Result.fail(AppRuntimeErrors.appStringProcessor.deviceOnboardingNotAllowed()); } diff --git a/packages/app-runtime/src/extensibility/ui/IUIBridge.ts b/packages/app-runtime/src/extensibility/ui/IUIBridge.ts index 3fa3b6b47..2e51a2aac 100644 --- a/packages/app-runtime/src/extensibility/ui/IUIBridge.ts +++ b/packages/app-runtime/src/extensibility/ui/IUIBridge.ts @@ -1,11 +1,12 @@ import { ApplicationError, Result } from "@js-soft/ts-utils"; -import { DeviceOnboardingInfoDTO, FileDVO, IdentityDVO, LocalRequestDVO, MailDVO, MessageDVO, RequestMessageDVO } from "@nmshd/runtime"; +import { DeviceOnboardingInfoDTO, FileDVO, IdentityDVO, LocalRequestDVO, MailDVO, MessageDVO, RequestMessageDVO, TokenDTO } from "@nmshd/runtime"; import { LocalAccountDTO } from "../../multiAccount"; export interface IUIBridge { showMessage(account: LocalAccountDTO, relationship: IdentityDVO, message: MessageDVO | MailDVO | RequestMessageDVO): Promise>; showRelationship(account: LocalAccountDTO, relationship: IdentityDVO): Promise>; showFile(account: LocalAccountDTO, file: FileDVO): Promise>; + showToken(account: LocalAccountDTO, token: TokenDTO): Promise>; showDeviceOnboarding(deviceOnboardingInfo: DeviceOnboardingInfoDTO): Promise>; showRequest(account: LocalAccountDTO, request: LocalRequestDVO): Promise>; showError(error: ApplicationError, account?: LocalAccountDTO): Promise>; diff --git a/packages/app-runtime/test/lib/FakeUIBridge.ts b/packages/app-runtime/test/lib/FakeUIBridge.ts index 0346ebf37..deeb2b553 100644 --- a/packages/app-runtime/test/lib/FakeUIBridge.ts +++ b/packages/app-runtime/test/lib/FakeUIBridge.ts @@ -14,6 +14,10 @@ export class FakeUIBridge implements IUIBridge { return Promise.resolve(Result.ok(undefined)); } + public showToken(): Promise> { + return Promise.resolve(Result.ok(undefined)); + } + public showDeviceOnboarding(): Promise> { return Promise.resolve(Result.ok(undefined)); } diff --git a/packages/app-runtime/test/lib/MockUIBridge.matchers.ts b/packages/app-runtime/test/lib/MockUIBridge.matchers.ts index 1432113d5..07ae1949f 100644 --- a/packages/app-runtime/test/lib/MockUIBridge.matchers.ts +++ b/packages/app-runtime/test/lib/MockUIBridge.matchers.ts @@ -166,6 +166,38 @@ expect.extend({ return { pass: true, message: () => "" }; }, + showTokenCalled(mockUIBridge: unknown, id: string) { + if (!(mockUIBridge instanceof MockUIBridge)) { + throw new Error("This method can only be used with expect(MockUIBridge)."); + } + + const calls = mockUIBridge.calls.filter((x) => x.method === "showToken"); + if (calls.length === 0) { + return { pass: false, message: () => "The method showToken was not called." }; + } + + const matchingCalls = calls.filter((x) => x.token.id === id); + if (matchingCalls.length === 0) { + return { + pass: false, + message: () => `The method showToken was called, but not with the specified token id '${id}', instead with ids '${calls.map((e) => e.token.id).join(", ")}'.` + }; + } + + return { pass: true, message: () => "" }; + }, + showTokenNotCalled(mockUIBridge: unknown) { + if (!(mockUIBridge instanceof MockUIBridge)) { + throw new Error("This method can only be used with expect(MockUIBridge)."); + } + + const calls = mockUIBridge.calls.filter((x) => x.method === "showToken"); + if (calls.length > 0) { + return { pass: false, message: () => `The method showToken called: ${calls.map((c) => `'account id: ${c.account.id} - tokenId: ${c.token.id}'`)}` }; + } + + return { pass: true, message: () => "" }; + }, showErrorCalled(mockUIBridge: unknown, code: string) { if (!(mockUIBridge instanceof MockUIBridge)) { throw new Error("This method can only be used with expect(MockUIBridge)."); @@ -201,6 +233,7 @@ declare global { showRequestNotCalled(): R; showFileCalled(id: string): R; showFileNotCalled(): R; + showTokenCalled(id: string): R; showErrorCalled(code: string): R; } } diff --git a/packages/app-runtime/test/lib/MockUIBridge.ts b/packages/app-runtime/test/lib/MockUIBridge.ts index 6dcf81d08..05ce4c63b 100644 --- a/packages/app-runtime/test/lib/MockUIBridge.ts +++ b/packages/app-runtime/test/lib/MockUIBridge.ts @@ -1,11 +1,12 @@ import { ApplicationError, Result } from "@js-soft/ts-utils"; -import { DeviceOnboardingInfoDTO, FileDVO, IdentityDVO, LocalRequestDVO, MailDVO, MessageDVO, RequestMessageDVO } from "@nmshd/runtime"; +import { DeviceOnboardingInfoDTO, FileDVO, IdentityDVO, LocalRequestDVO, MailDVO, MessageDVO, RequestMessageDVO, TokenDTO } from "@nmshd/runtime"; import { IUIBridge, LocalAccountDTO } from "../../src"; export type MockUIBridgeCall = | { method: "showMessage"; account: LocalAccountDTO; relationship: IdentityDVO; message: MessageDVO | MailDVO | RequestMessageDVO } | { method: "showRelationship"; account: LocalAccountDTO; relationship: IdentityDVO } | { method: "showFile"; account: LocalAccountDTO; file: FileDVO } + | { method: "showToken"; account: LocalAccountDTO; token: TokenDTO } | { method: "showDeviceOnboarding"; deviceOnboardingInfo: DeviceOnboardingInfoDTO } | { method: "showRequest"; account: LocalAccountDTO; request: LocalRequestDVO } | { method: "showError"; error: ApplicationError; account?: LocalAccountDTO } @@ -53,6 +54,12 @@ export class MockUIBridge implements IUIBridge { return Promise.resolve(Result.ok(undefined)); } + public showToken(account: LocalAccountDTO, token: TokenDTO): Promise> { + this._calls.push({ method: "showToken", account, token }); + + return Promise.resolve(Result.ok(undefined)); + } + public showDeviceOnboarding(deviceOnboardingInfo: DeviceOnboardingInfoDTO): Promise> { this._calls.push({ method: "showDeviceOnboarding", deviceOnboardingInfo }); diff --git a/packages/app-runtime/test/runtime/AppStringProcessor.test.ts b/packages/app-runtime/test/runtime/AppStringProcessor.test.ts index 66ba74f08..20987e72a 100644 --- a/packages/app-runtime/test/runtime/AppStringProcessor.test.ts +++ b/packages/app-runtime/test/runtime/AppStringProcessor.test.ts @@ -377,6 +377,21 @@ describe("AppStringProcessor", function () { expect(runtime4MockUiBridge).showFileCalled(file.id); }); + test("get a token using a url", async function () { + const tokenResult = await runtime1Session.transportServices.tokens.createOwnToken({ + content: { custom: "tokenContent" }, + expiresAt: CoreDate.utc().add({ days: 1 }).toISOString(), + ephemeral: true + }); + const token = tokenResult.value; + + const result = await runtime4.stringProcessor.processURL(token.reference.url, runtime4Session.account); + expect(result).toBeSuccessful(); + expect(result.value).toBeUndefined(); + + expect(runtime4MockUiBridge).showTokenCalled(token.id); + }); + test("get a template using a url", async function () { const templateResult = await runtime1Session.transportServices.relationshipTemplates.createOwnRelationshipTemplate({ content: RelationshipTemplateContent.from({