diff --git a/demos/next/src/components/Message.tsx b/demos/next/src/components/Message.tsx index dc2e25673..eb631ecb1 100644 --- a/demos/next/src/components/Message.tsx +++ b/demos/next/src/components/Message.tsx @@ -27,6 +27,12 @@ const InputFields: React.FC<{ fields: SDK.Domain.InputField[]; }> = props => { ; }; +function replacePlaceholders(text: string, args: any[]): string { + return text.replace(/\{(\d+)\}/g, (match, index) => { + const idx = parseInt(index) - 1; // Adjust for zero-based array index + return args[idx] !== undefined ? args[idx] : match; // Replace or keep original if undefined + }); +} export function Message({ message }) { const app = useMountedApp(); @@ -370,8 +376,36 @@ export function Message({ message }) { ; } - - + if (message.piuri === SDK.ProtocolType.ProblemReporting) { + const content = replacePlaceholders(message.body.comment, message.body.args); + + return
+
+
+ + + +
+

Error {message.id}

+
+
+
+
An error ocurred, {content}, CODE {message.body.code}
+
+
+
+ } if (message.piuri === "https://didcomm.atalaprism.io/present-proof/3.0/request-presentation") { const requestPresentationMessage = SDK.RequestPresentation.fromMessage(message); diff --git a/src/domain/models/errors/Agent.ts b/src/domain/models/errors/Agent.ts index fb38435e0..ab288b9f3 100644 --- a/src/domain/models/errors/Agent.ts +++ b/src/domain/models/errors/Agent.ts @@ -161,6 +161,11 @@ export class InvalidPresentationBodyError extends Error { super(message || "Invalid Presentation body Error"); } } +export class InvalidProblemReportBodyError extends Error { + constructor(message?: string) { + super(message || "Invalid Problem reporting body Error"); + } +} export class InvalidProposeCredentialBodyError extends Error { constructor(message?: string) { super(message || "Invalid Propose CredentialBody Error"); diff --git a/src/edge-agent/connectionsManager/ConnectionsManager.ts b/src/edge-agent/connectionsManager/ConnectionsManager.ts index 24a88b32c..398a7d09b 100644 --- a/src/edge-agent/connectionsManager/ConnectionsManager.ts +++ b/src/edge-agent/connectionsManager/ConnectionsManager.ts @@ -170,7 +170,6 @@ export class ConnectionsManager implements ConnectionsManagerClass { } } } - if (messageIds.length) { await this.mediationHandler.registerMessagesAsRead(messageIds); } diff --git a/src/edge-agent/helpers/ProtocolHelpers.ts b/src/edge-agent/helpers/ProtocolHelpers.ts index 4f5a4ec88..739d3125d 100644 --- a/src/edge-agent/helpers/ProtocolHelpers.ts +++ b/src/edge-agent/helpers/ProtocolHelpers.ts @@ -1,7 +1,7 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ import { AttachmentDescriptor, Message } from "../../domain"; import { AgentError } from "../../domain/models/Errors"; -import { asArray, isArray, isNil, isObject, isString, notEmptyString, notNil } from "../../utils"; +import { asArray, isArray, isEmpty, isNil, isObject, isString, notEmptyString, notNil } from "../../utils"; import { ProtocolType } from "../protocols/ProtocolTypes"; import { CredentialFormat } from "../protocols/issueCredential/CredentialFormat"; import { @@ -14,6 +14,7 @@ import { RequestPresentationBody, ProposePresentationBody, BasicMessageBody, + ProblemReportBody, } from "../protocols/types"; export const parseCredentialAttachments = (credentials: Map) => { @@ -60,6 +61,18 @@ export const parseBasicMessageBody = (msg: Message): BasicMessageBody => { throw new AgentError.InvalidBasicMessageBodyError("Invalid content"); }; +export const parseProblemReportBody = (msg: Message): ProblemReportBody => { + if (notEmptyString(msg.body.code) && + notEmptyString(msg.body.comment) && + notEmptyString(msg.body.escalate_to) && + isArray(msg.body.args) && isEmpty(msg.body.args) + ) { + const { code, comment, escalate_to, args } = msg.body; + return { code, comment, escalate_to, args } + } + throw new AgentError.InvalidProblemReportBodyError() +} + export const parseCredentialBody = (msg: Message): CredentialBody => { if (Object.keys(msg.body).length === 0) { throw new AgentError.InvalidCredentialBodyError( diff --git a/src/edge-agent/mediator/BasicMediatorHandler.ts b/src/edge-agent/mediator/BasicMediatorHandler.ts index cf13465e7..7e43c2b0e 100644 --- a/src/edge-agent/mediator/BasicMediatorHandler.ts +++ b/src/edge-agent/mediator/BasicMediatorHandler.ts @@ -179,7 +179,6 @@ export class BasicMediatorHandler implements MediatorHandler { if (!message) { return []; } - return new PickupRunner(message, this.mercury).run(); } diff --git a/src/edge-agent/protocols/ProtocolTypes.ts b/src/edge-agent/protocols/ProtocolTypes.ts index ef037e421..4ed293dac 100644 --- a/src/edge-agent/protocols/ProtocolTypes.ts +++ b/src/edge-agent/protocols/ProtocolTypes.ts @@ -21,7 +21,8 @@ export enum ProtocolType { PickupStatus = "https://didcomm.org/messagepickup/3.0/status", PickupReceived = "https://didcomm.org/messagepickup/3.0/messages-received", LiveDeliveryChange = "https://didcomm.org/messagepickup/3.0/live-delivery-change", - PrismRevocation = "https://atalaprism.io/revocation_notification/1.0/revoke" + PrismRevocation = "https://atalaprism.io/revocation_notification/1.0/revoke", + ProblemReporting = "https://didcomm.org/report-problem/2.0/problem-report" } export function findProtocolTypeByValue(string: string): ProtocolType { diff --git a/src/edge-agent/protocols/other/ProblemReport.ts b/src/edge-agent/protocols/other/ProblemReport.ts new file mode 100644 index 000000000..d0225c804 --- /dev/null +++ b/src/edge-agent/protocols/other/ProblemReport.ts @@ -0,0 +1,50 @@ +import { AgentError, DID, Message } from "../../../domain/models"; +import { parseProblemReportBody } from "../../helpers/ProtocolHelpers"; +import { ProtocolType } from "../ProtocolTypes"; +import { ProblemReportBody } from "../types"; + + + + + +export class ProblemReport { + public static type = ProtocolType.ProblemReporting; + + constructor( + public body: ProblemReportBody, + public from: DID, + public to: DID, + public thid?: string, + ) { } + + makeMessage(): Message { + const body = JSON.stringify(this.body); + return new Message( + body, + undefined, + ProblemReport.type, + this.from, + this.to, + [], + this.thid + ); + } + + static fromMessage(fromMessage: Message): ProblemReport { + if ( + fromMessage.piuri !== ProtocolType.DidcommBasicMessage || + !fromMessage.from || + !fromMessage.to + ) { + throw new AgentError.InvalidBasicMessageBodyError( + "Invalid BasicMessage body error." + ); + } + const problemReportBody = parseProblemReportBody(fromMessage); + return new ProblemReport( + problemReportBody, + fromMessage.from, + fromMessage.to + ); + } +} \ No newline at end of file diff --git a/src/edge-agent/protocols/pickup/PickupRunner.ts b/src/edge-agent/protocols/pickup/PickupRunner.ts index aae4722cd..defae0b8f 100644 --- a/src/edge-agent/protocols/pickup/PickupRunner.ts +++ b/src/edge-agent/protocols/pickup/PickupRunner.ts @@ -6,7 +6,8 @@ import { PickupAttachment } from "../types"; type PickupResponse = | { type: "status"; message: Message } - | { type: "delivery"; message: Message }; + | { type: "delivery"; message: Message } + | { type: 'report', message: Message }; export class PickupRunner { private message: PickupResponse; @@ -20,6 +21,9 @@ export class PickupRunner { case ProtocolType.PickupDelivery: this.message = { type: "delivery", message: message }; break; + case ProtocolType.ProblemReporting: + this.message = { type: "report", message: message }; + break; default: throw new AgentError.InvalidPickupDeliveryMessageError(); } @@ -63,6 +67,13 @@ export class PickupRunner { message: await this.mercury.unpackMessage(attachment.data), })) ); + } else if (this.message.type === "report") { + return [ + { + attachmentId: this.message.message.id, + message: this.message.message + } + ] } return []; diff --git a/src/edge-agent/protocols/types.ts b/src/edge-agent/protocols/types.ts index 3947c5abf..b74fe9c69 100644 --- a/src/edge-agent/protocols/types.ts +++ b/src/edge-agent/protocols/types.ts @@ -70,6 +70,14 @@ export interface BasicMessageBody { content: string; } + +export interface ProblemReportBody { + code: string, + comment: string, + args: string[], + escalate_to: string +} + export interface RequestPresentationBody extends PresentationBody { willConfirm?: boolean; proofTypes: ProofTypes[]; diff --git a/tests/agent/protocols/pickup/PickupRunner.test.ts b/tests/agent/protocols/pickup/PickupRunner.test.ts index e30e521b9..055e21585 100644 --- a/tests/agent/protocols/pickup/PickupRunner.test.ts +++ b/tests/agent/protocols/pickup/PickupRunner.test.ts @@ -45,6 +45,27 @@ describe("PickupRunner Tests", () => { expect(response).to.be.an("array").with.length(0); }); + test(`${ProtocolType.PickupDelivery} - 1 problem report message`, async () => { + const runner = new PickupRunner(Messages.Reporting, mercury); + const response = await runner.run(); + expect(response).to.be.an("array").with.length(1); + expect(response[0].message.ack).to.deep.eq(Messages.Reporting.ack); + expect(response[0].message.attachments).to.deep.eq(Messages.Reporting.attachments); + expect(response[0].message.body).to.deep.eq(Messages.Reporting.body); + expect(response[0].message.createdTime).to.deep.eq(Messages.Reporting.createdTime); + expect(response[0].message.direction).to.deep.eq(Messages.Reporting.direction); + expect(response[0].message.extraHeaders).to.deep.eq(Messages.Reporting.extraHeaders); + expect(response[0].message.from).to.deep.eq(Messages.Reporting.from); + expect(response[0].message.fromPrior).to.deep.eq(Messages.Reporting.fromPrior); + expect(response[0].message.id).to.deep.eq(Messages.Reporting.id); + expect(response[0].message.piuri).to.deep.eq(Messages.Reporting.piuri); + expect(response[0].message.pthid).to.deep.eq(Messages.Reporting.pthid); + expect(response[0].message.thid).to.deep.eq(Messages.Reporting.thid); + expect(response[0].message.to).to.deep.eq(Messages.Reporting.to); + }); + + + test(`${ProtocolType.PickupDelivery} - 1 message`, async () => { const runner = new PickupRunner(Messages.PickupDelivery, mercury); const response = await runner.run(); diff --git a/tests/fixtures/messages/index.ts b/tests/fixtures/messages/index.ts index c24faf7e8..3813eecad 100644 --- a/tests/fixtures/messages/index.ts +++ b/tests/fixtures/messages/index.ts @@ -1,5 +1,6 @@ import { base64 } from "multiformats/bases/base64"; import * as Domain from "../../../src/domain"; +import { ProtocolType } from "../../../src/edge-agent/protocols/ProtocolTypes"; // convert raw DIDComm message to domain, handles parsing idiosyncrasies const convertDidcomm = (value: any) => new Domain.Message( @@ -238,3 +239,23 @@ const pickupStatusRaw: any = { }; export const PickupStatus = convertDidcomm(pickupStatusRaw); + + +export const Reporting = convertDidcomm({ + "id": "033642e4-9064-4da2-ac84-6be20b3ec8b8", + "typ": "application/didcomm-plain+json", + "type": ProtocolType.ProblemReporting, + "body": { + "args": [ + 'did:peer:2.Ez6LSmEZPCeaFeA1vwBTZeLvXi6F24ZdEQgmzJCqHaQQojBj8.Vz6MktyGjB1ogYgsu3nt9ncjzXM4mBBGJSU5cPrCScDcC2GrN.SW10' + ], + "comment": "The DID '{1}' is not enroled", + "code": "e.p.req.not_enroll", + "escalate_to": "email@email.com" + }, + "from": "did:peer:2.Ez6LSghwSE437wnDE1pt3X6hVDUQzSjsHzinpX3XFvMjRAm7y.Vz6Mkhh1e5CEYYq6JBUcTZ6Cp2ranCWRrv7Yax3Le4N59R6dd.SeyJ0IjoiZG0iLCJzIjp7InVyaSI6Imh0dHBzOi8vc2l0LXByaXNtLW1lZGlhdG9yLmF0YWxhcHJpc20uaW8iLCJhIjpbImRpZGNvbW0vdjIiXX19.SeyJ0IjoiZG0iLCJzIjp7InVyaSI6IndzczovL3NpdC1wcmlzbS1tZWRpYXRvci5hdGFsYXByaXNtLmlvL3dzIiwiYSI6WyJkaWRjb21tL3YyIl19fQ", + "to": [ + "did:peer:2.Ez6LSrjn3NUEgFDY2wKnxbNfbXLozs8Em5RX6xWkTJn3kqpsL.Vz6MkuK1KvyssRGvzYuerJQQaTANA9hAe3dXt2X31d6Ef9xee.SW10" + ], + "thid": "dcc4af0e-0a9c-4082-98d1-bbdc582002b7" +}) \ No newline at end of file