diff --git a/packages/action-menu/src/ActionMenuApi.ts b/packages/action-menu/src/ActionMenuApi.ts index 913979ff84..c8569894c7 100644 --- a/packages/action-menu/src/ActionMenuApi.ts +++ b/packages/action-menu/src/ActionMenuApi.ts @@ -12,7 +12,7 @@ import { ConnectionService, Dispatcher, MessageSender, - createOutboundDidCommV1Message, + OutboundMessageContext, injectable, } from '@aries-framework/core' @@ -59,8 +59,13 @@ export class ActionMenuApi { connection, }) - const outboundMessage = createOutboundDidCommV1Message(connection, message) - await this.messageSender.sendMessage(this.agentContext, outboundMessage) + const outboundMessageContext = new OutboundMessageContext(message, { + agentContext: this.agentContext, + connection, + associatedRecord: record, + }) + + await this.messageSender.sendMessage(outboundMessageContext) return record } @@ -80,8 +85,13 @@ export class ActionMenuApi { menu: options.menu, }) - const outboundMessage = createOutboundDidCommV1Message(connection, message) - await this.messageSender.sendMessage(this.agentContext, outboundMessage) + const outboundMessageContext = new OutboundMessageContext(message, { + agentContext: this.agentContext, + connection, + associatedRecord: record, + }) + + await this.messageSender.sendMessage(outboundMessageContext) return record } @@ -109,8 +119,13 @@ export class ActionMenuApi { performedAction: options.performedAction, }) - const outboundMessage = createOutboundDidCommV1Message(connection, message) - await this.messageSender.sendMessage(this.agentContext, outboundMessage) + const outboundMessageContext = new OutboundMessageContext(message, { + agentContext: this.agentContext, + connection, + associatedRecord: record, + }) + + await this.messageSender.sendMessage(outboundMessageContext) return record } diff --git a/packages/core/src/agent/Dispatcher.ts b/packages/core/src/agent/Dispatcher.ts index 13321aeff2..acc6bd9a05 100644 --- a/packages/core/src/agent/Dispatcher.ts +++ b/packages/core/src/agent/Dispatcher.ts @@ -1,10 +1,4 @@ -import type { - ConstructableDidCommMessage, - OutboundDidCommV1Message, - OutboundDidCommV1ServiceMessage, - OutboundDidCommV2Message, -} from '../didcomm' -import type { ParsedMessageType } from '../utils/messageType' +import type { AgentMessage } from './AgentMessage' import type { AgentMessageProcessedEvent } from './Events' import type { Handler } from './Handler' import type { InboundMessageContext } from './models/InboundMessageContext' @@ -12,14 +6,14 @@ import type { InboundMessageContext } from './models/InboundMessageContext' import { InjectionSymbols } from '../constants' import { AriesFrameworkError } from '../error/AriesFrameworkError' import { Logger } from '../logger' -import { inject, injectable } from '../plugins' +import { injectable, inject } from '../plugins' import { canHandleMessageType, parseMessageType } from '../utils/messageType' import { ProblemReportMessage } from './../modules/problem-reports/messages/ProblemReportMessage' import { EventEmitter } from './EventEmitter' import { AgentEventTypes } from './Events' import { MessageSender } from './MessageSender' -import { isOutboundDidCommV1Message, isOutboundDidCommV2Message, isOutboundServiceMessage } from './helpers' +import { OutboundMessageContext } from './models' @injectable() class Dispatcher { @@ -43,14 +37,14 @@ class Dispatcher { } public async dispatch(messageContext: InboundMessageContext): Promise { - const message = messageContext.message + const { agentContext, connection, senderKey, recipientKey, message } = messageContext const handler = this.getHandlerForType(message.type) if (!handler) { throw new AriesFrameworkError(`No handler for message type "${message.type}" found`) } - let outboundMessage: OutboundDidCommV1Message | OutboundDidCommV1ServiceMessage | OutboundDidCommV2Message | void + let outboundMessage: OutboundMessageContext | void try { outboundMessage = await handler.handle(messageContext) @@ -59,45 +53,39 @@ class Dispatcher { if (problemReportMessage instanceof ProblemReportMessage && messageContext.connection) { problemReportMessage.setThread({ - threadId: messageContext.message.threadId, + threadId: message.threadId, }) - outboundMessage = { - payload: problemReportMessage, + outboundMessage = new OutboundMessageContext(problemReportMessage, { + agentContext, connection: messageContext.connection, - } + }) } else { this.logger.error(`Error handling message with type ${message.type}`, { message: message.toJSON(), error, - senderKey: messageContext.senderKey?.fingerprint, - recipientKey: messageContext.recipientKey?.fingerprint, - connectionId: messageContext.connection?.id, + senderKey: senderKey?.fingerprint, + recipientKey: recipientKey?.fingerprint, + connectionId: connection?.id, }) throw error } } - if (outboundMessage && isOutboundServiceMessage(outboundMessage)) { - await this.messageSender.sendMessageToService(messageContext.agentContext, { - message: outboundMessage.payload, - service: outboundMessage.service, - senderKey: outboundMessage.senderKey, - returnRoute: true, - }) - } else if (outboundMessage && isOutboundDidCommV1Message(outboundMessage)) { - outboundMessage.sessionId = messageContext.sessionId - await this.messageSender.sendMessage(messageContext.agentContext, outboundMessage) - } else if (outboundMessage && isOutboundDidCommV2Message(outboundMessage)) { - await this.messageSender.sendMessage(messageContext.agentContext, outboundMessage) + if (outboundMessage) { + if (outboundMessage.isOutboundServiceMessage()) { + await this.messageSender.sendMessageToService(outboundMessage) + } else { + outboundMessage.sessionId = messageContext.sessionId + await this.messageSender.sendMessage(outboundMessage) + } } - // Emit event that allows to hook into received messages - this.eventEmitter.emit(messageContext.agentContext, { + this.eventEmitter.emit(agentContext, { type: AgentEventTypes.AgentMessageProcessed, payload: { - message: messageContext.message, - connection: messageContext.connection, + message, + connection, }, }) } @@ -112,8 +100,9 @@ class Dispatcher { } } - public getMessageClassForType(messageType: string): ConstructableDidCommMessage | undefined { + public getMessageClassForType(messageType: string): typeof AgentMessage | undefined { const incomingMessageType = parseMessageType(messageType) + for (const handler of this.handlers) { for (const MessageClass of handler.supportedMessages) { if (canHandleMessageType(MessageClass, incomingMessageType)) return MessageClass @@ -125,11 +114,10 @@ class Dispatcher { * Returns array of message types that dispatcher is able to handle. * Message type format is MTURI specified at https://github.com/hyperledger/aries-rfcs/blob/main/concepts/0003-protocols/README.md#mturi. */ - public get supportedMessageTypes(): ParsedMessageType[] { - return this.handlers.reduce( - (all, cur) => [...all, ...cur.supportedMessages.map((message) => message.type)], - [] - ) + public get supportedMessageTypes() { + return this.handlers + .reduce((all, cur) => [...all, ...cur.supportedMessages], []) + .map((m) => m.type) } /** diff --git a/packages/core/src/agent/Events.ts b/packages/core/src/agent/Events.ts index 3688479795..4e7bb4b076 100644 --- a/packages/core/src/agent/Events.ts +++ b/packages/core/src/agent/Events.ts @@ -1,5 +1,6 @@ import type { ConnectionRecord } from '../modules/connections' import type { AgentMessage } from './AgentMessage' +import type { OutboundMessageContext, OutboundMessageSendStatus } from './models' import type { Observable } from 'rxjs' import { filter } from 'rxjs' @@ -13,6 +14,7 @@ export function filterContextCorrelationId(contextCorrelationId: string) { export enum AgentEventTypes { AgentMessageReceived = 'AgentMessageReceived', AgentMessageProcessed = 'AgentMessageProcessed', + AgentMessageSent = 'AgentMessageSent', } export interface EventMetadata { @@ -41,3 +43,11 @@ export interface AgentMessageProcessedEvent extends BaseEvent { connection?: ConnectionRecord } } + +export interface AgentMessageSentEvent extends BaseEvent { + type: typeof AgentEventTypes.AgentMessageSent + payload: { + message: OutboundMessageContext + status: OutboundMessageSendStatus + } +} diff --git a/packages/core/src/agent/Handler.ts b/packages/core/src/agent/Handler.ts index 5387f3cb24..e736aad3da 100644 --- a/packages/core/src/agent/Handler.ts +++ b/packages/core/src/agent/Handler.ts @@ -1,21 +1,14 @@ -import type { - ConstructableDidCommMessage, - OutboundDidCommV1Message, - OutboundDidCommV1ServiceMessage, - OutboundDidCommV2Message, -} from '../didcomm' -import type { InboundMessageContext } from './models/InboundMessageContext' +import type { ConstructableAgentMessage } from './AgentMessage' +import type { InboundMessageContext, OutboundMessageContext } from './models' export interface Handler { - readonly supportedMessages: readonly ConstructableDidCommMessage[] + readonly supportedMessages: readonly ConstructableAgentMessage[] - handle( - messageContext: InboundMessageContext - ): Promise + handle(messageContext: InboundMessageContext): Promise } /** - * Provides exact typing for the DIDCommMessage in the message context in the `handle` function + * Provides exact typing for the AgentMessage in the message context in the `handle` function * of a handler. It takes all possible types from `supportedMessageTypes` * * @example diff --git a/packages/core/src/agent/MessageReceiver.ts b/packages/core/src/agent/MessageReceiver.ts index 23fc491355..2c94f6596f 100644 --- a/packages/core/src/agent/MessageReceiver.ts +++ b/packages/core/src/agent/MessageReceiver.ts @@ -1,20 +1,18 @@ -import type { DidCommV2Message, EncryptedMessage, PlaintextMessage, SignedMessage } from '../didcomm' -import type { DecryptedMessageContext } from '../didcomm/types' import type { ConnectionRecord } from '../modules/connections' import type { InboundTransport } from '../transport' +import type { EncryptedMessage, PlaintextMessage } from '../types' import type { AgentMessage } from './AgentMessage' +import type { DecryptedMessageContext } from './EnvelopeService' import type { TransportSession } from './TransportService' import type { AgentContext } from './context' import { InjectionSymbols } from '../constants' -import { getPlaintextMessageType, isEncryptedMessage, isPlaintextMessage, isSignedMessage } from '../didcomm/helpers' -import { DidCommMessageVersion } from '../didcomm/types' import { AriesFrameworkError } from '../error' import { Logger } from '../logger' import { ConnectionService } from '../modules/connections' import { ProblemReportError, ProblemReportMessage, ProblemReportReason } from '../modules/problem-reports' -import { V2ProblemReportMessage } from '../modules/problem-reports/messages/V2ProblemReportMessage' import { inject, injectable } from '../plugins' +import { isValidJweStructure } from '../utils/JWE' import { JsonTransformer } from '../utils/JsonTransformer' import { canHandleMessageType, parseMessageType, replaceLegacyDidSovPrefixOnMessage } from '../utils/messageType' @@ -23,8 +21,7 @@ import { EnvelopeService } from './EnvelopeService' import { MessageSender } from './MessageSender' import { TransportService } from './TransportService' import { AgentContextProvider } from './context' -import { createOutboundDidCommV1Message } from './helpers' -import { InboundMessageContext } from './models/InboundMessageContext' +import { InboundMessageContext, OutboundMessageContext } from './models' @injectable() export class MessageReceiver { @@ -81,11 +78,9 @@ export class MessageReceiver { }) try { - if (isEncryptedMessage(inboundMessage)) { - return await this.receiveEncryptedMessage(agentContext, inboundMessage, session) - } else if (isSignedMessage(inboundMessage)) { - return await this.receiveSignedMessage(agentContext, inboundMessage, session) - } else if (isPlaintextMessage(inboundMessage)) { + if (this.isEncryptedMessage(inboundMessage)) { + await this.receiveEncryptedMessage(agentContext, inboundMessage as EncryptedMessage, session) + } else if (this.isPlaintextMessage(inboundMessage)) { await this.receivePlaintextMessage(agentContext, inboundMessage, connection) } else { throw new AriesFrameworkError('Unable to parse incoming message: unrecognized format') @@ -108,39 +103,18 @@ export class MessageReceiver { private async receiveEncryptedMessage( agentContext: AgentContext, - packedMessage: EncryptedMessage, + encryptedMessage: EncryptedMessage, session?: TransportSession ) { - const unpackedMessage = await this.envelopeService.unpackMessage(agentContext, packedMessage) - return this.processUnpackedMessage(agentContext, unpackedMessage, session) - } - - private async receiveSignedMessage( - agentContext: AgentContext, - packedMessage: SignedMessage, - session?: TransportSession - ) { - const unpackedMessage = await this.envelopeService.unpackMessage(agentContext, packedMessage) - return this.processUnpackedMessage(agentContext, unpackedMessage, session) - } - - private async processUnpackedMessage( - agentContext: AgentContext, - unpackedMessage: DecryptedMessageContext, - session?: TransportSession - ) { - const { plaintextMessage, senderKey, recipientKey, didCommVersion } = unpackedMessage + const decryptedMessage = await this.decryptMessage(agentContext, encryptedMessage) + const { plaintextMessage, senderKey, recipientKey } = decryptedMessage this.logger.info( `Received message with type '${plaintextMessage['@type']}', recipient key ${recipientKey?.fingerprint} and sender key ${senderKey?.fingerprint}`, plaintextMessage ) - // DIDComm V2 messaging doesn't require connection - const connection = - didCommVersion === DidCommMessageVersion.V1 - ? await this.findConnectionByMessageKeys(agentContext, unpackedMessage) - : undefined + const connection = await this.findConnectionByMessageKeys(agentContext, decryptedMessage) const message = await this.transformAndValidate(agentContext, plaintextMessage, connection) @@ -180,6 +154,40 @@ export class MessageReceiver { await this.dispatcher.dispatch(messageContext) } + /** + * Decrypt a message using the envelope service. + * + * @param message the received inbound message to decrypt + */ + private async decryptMessage( + agentContext: AgentContext, + message: EncryptedMessage + ): Promise { + try { + return await this.envelopeService.unpackMessage(agentContext, message) + } catch (error) { + this.logger.error('Error while decrypting message', { + error, + encryptedMessage: message, + errorMessage: error instanceof Error ? error.message : error, + }) + throw error + } + } + + private isPlaintextMessage(message: unknown): message is PlaintextMessage { + if (typeof message !== 'object' || message == null) { + return false + } + // If the message has a @type field we assume the message is in plaintext and it is not encrypted. + return '@type' in message + } + + private isEncryptedMessage(message: unknown): message is EncryptedMessage { + // If the message does has valid JWE structure, we can assume the message is encrypted. + return isValidJweStructure(message) + } + private async transformAndValidate( agentContext: AgentContext, plaintextMessage: PlaintextMessage, @@ -189,11 +197,7 @@ export class MessageReceiver { try { message = await this.transformMessage(plaintextMessage) } catch (error) { - if (plaintextMessage['@id'] && connection) { - await this.sendProblemReportMessage(agentContext, error.message, connection, plaintextMessage) - } else if (plaintextMessage.id) { - await this.sendProblemReportMessageV2(agentContext, error.message, plaintextMessage) - } + if (connection) await this.sendProblemReportMessage(agentContext, error.message, connection, plaintextMessage) throw error } return message @@ -206,7 +210,7 @@ export class MessageReceiver { // We only fetch connections that are sent in AuthCrypt mode if (!recipientKey || !senderKey) return null - // Try to find the did records that holds the senderDid and recipient keys + // Try to find the did records that holds the sender and recipient keys return this.connectionService.findByKeys(agentContext, { senderKey, recipientKey, @@ -220,16 +224,9 @@ export class MessageReceiver { */ private async transformMessage(message: PlaintextMessage): Promise { // replace did:sov:BzCbsNYhMrjHiqZDTUASHg;spec prefix for message type with https://didcomm.org - if (message['@type']) { - // replace did:sov:BzCbsNYhMrjHiqZDTUASHg;spec prefix for record type with https://didcomm.org - replaceLegacyDidSovPrefixOnMessage(message) - } - - const messageType = getPlaintextMessageType(message) - if (!messageType) { - throw new AriesFrameworkError(`No type found in the message: ${message}`) - } + replaceLegacyDidSovPrefixOnMessage(message) + const messageType = message['@type'] const MessageClass = this.dispatcher.getMessageClassForType(messageType) if (!MessageClass) { @@ -238,7 +235,7 @@ export class MessageReceiver { }) } - // Cast the plain JSON object to specific instance of Message extended from DIDCommMessage + // Cast the plain JSON object to specific instance of Message extended from AgentMessage let messageTransformed: AgentMessage try { messageTransformed = JsonTransformer.fromJSON(message, MessageClass) @@ -266,10 +263,7 @@ export class MessageReceiver { connection: ConnectionRecord, plaintextMessage: PlaintextMessage ) { - const type = getPlaintextMessageType(message) - if (!type) return - - const messageType = parseMessageType(type) + const messageType = parseMessageType(plaintextMessage['@type']) if (canHandleMessageType(ProblemReportMessage, messageType)) { throw new AriesFrameworkError(`Not sending problem report in response to problem report: {message}`) } @@ -280,34 +274,11 @@ export class MessageReceiver { }, }) problemReportMessage.setThread({ - threadId: plaintextMessage['@id'] as string, + threadId: plaintextMessage['@id'], }) - const outboundMessage = createOutboundDidCommV1Message(connection, problemReportMessage) - if (outboundMessage) { - await this.messageSender.sendDIDCommV1Message(agentContext, outboundMessage) + const outboundMessageContext = new OutboundMessageContext(problemReportMessage, { agentContext, connection }) + if (outboundMessageContext) { + await this.messageSender.sendMessage(outboundMessageContext) } } - - private async sendProblemReportMessageV2( - agentContext: AgentContext, - message: string, - plaintextMessage: PlaintextMessage - ) { - const plainTextMessageV2 = plaintextMessage as unknown as DidCommV2Message - - // Cannot send problem report for message with unknown senderDid or recipient - if (!plainTextMessageV2.from || !plainTextMessageV2.to?.length) return - - const problemReportMessage = new V2ProblemReportMessage({ - parentThreadId: plainTextMessageV2.id, - from: plainTextMessageV2.firstRecipient, - to: plainTextMessageV2.from, - body: { - code: ProblemReportReason.MessageParseFailure, - comment: message, - }, - }) - - await this.messageSender.sendDIDCommV2Message(agentContext, { payload: problemReportMessage }) - } } diff --git a/packages/core/src/agent/MessageSender.ts b/packages/core/src/agent/MessageSender.ts index 941ba10fd4..ac9ac731a2 100644 --- a/packages/core/src/agent/MessageSender.ts +++ b/packages/core/src/agent/MessageSender.ts @@ -1,37 +1,33 @@ -import type { Key } from '../crypto' -import type { EncryptedMessage, OutboundPackage, OutboundPackagePayload } from '../didcomm/types' -import type { OutboundDidCommV1Message } from '../didcomm/versions/v1' -import type { OutboundDidCommV2Message } from '../didcomm/versions/v2' -import type { DidCommV2Message } from '../didcomm/versions/v2/DidCommV2Message' import type { ConnectionRecord } from '../modules/connections' import type { ResolvedDidCommService } from '../modules/didcomm' -import type { DidCommV2Service, DidDocument, DidDocumentService } from '../modules/dids' +import type { DidDocument } from '../modules/dids' import type { OutOfBandRecord } from '../modules/oob/repository' import type { OutboundTransport } from '../transport/OutboundTransport' +import type { OutboundPackage, EncryptedMessage } from '../types' import type { AgentMessage } from './AgentMessage' -import type { PackMessageParams } from './EnvelopeService' +import type { EnvelopeKeys } from './EnvelopeService' +import type { AgentMessageSentEvent } from './Events' import type { TransportSession } from './TransportService' import type { AgentContext } from './context' import { DID_COMM_TRANSPORT_QUEUE, InjectionSymbols } from '../constants' import { ReturnRouteTypes } from '../decorators/transport/TransportDecorator' -import { MessageType } from '../didcomm/types' -import { isDidCommV1OutboundMessage } from '../didcomm/versions/v1' -import { isDidCommV2OutboundMessage } from '../didcomm/versions/v2' import { AriesFrameworkError, MessageSendingError } from '../error' import { Logger } from '../logger' -import { DidCommDocumentService } from '../modules/didcomm/services/DidCommDocumentService' +import { DidCommDocumentService } from '../modules/didcomm' import { getKeyDidMappingByVerificationMethod } from '../modules/dids/domain/key-type' import { didKeyToInstanceOfKey } from '../modules/dids/helpers' import { DidResolverService } from '../modules/dids/services/DidResolverService' -import { OutOfBandRepository } from '../modules/oob/repository' import { inject, injectable } from '../plugins' import { MessageRepository } from '../storage/MessageRepository' import { MessageValidator } from '../utils/MessageValidator' import { getProtocolScheme } from '../utils/uri' import { EnvelopeService } from './EnvelopeService' +import { EventEmitter } from './EventEmitter' +import { AgentEventTypes } from './Events' import { TransportService } from './TransportService' +import { OutboundMessageContext, OutboundMessageSendStatus } from './models' export interface TransportPriorityOptions { schemes: string[] @@ -46,9 +42,8 @@ export class MessageSender { private logger: Logger private didResolverService: DidResolverService private didCommDocumentService: DidCommDocumentService - private outOfBandRepository: OutOfBandRepository + private eventEmitter: EventEmitter public readonly outboundTransports: OutboundTransport[] = [] - public readonly outboundTransportsSchemas: string[] = [] public constructor( envelopeService: EnvelopeService, @@ -57,7 +52,7 @@ export class MessageSender { @inject(InjectionSymbols.Logger) logger: Logger, didResolverService: DidResolverService, didCommDocumentService: DidCommDocumentService, - outOfBandRepository: OutOfBandRepository + eventEmitter: EventEmitter ) { this.envelopeService = envelopeService this.transportService = transportService @@ -65,14 +60,12 @@ export class MessageSender { this.logger = logger this.didResolverService = didResolverService this.didCommDocumentService = didCommDocumentService - this.outOfBandRepository = outOfBandRepository + this.eventEmitter = eventEmitter this.outboundTransports = [] - this.outboundTransportsSchemas = [] } public registerOutboundTransport(outboundTransport: OutboundTransport) { this.outboundTransports.push(outboundTransport) - this.outboundTransportsSchemas.push(...outboundTransport.supportedSchemes) } public async packMessage( @@ -82,7 +75,7 @@ export class MessageSender { message, endpoint, }: { - keys: PackMessageParams + keys: EnvelopeKeys message: AgentMessage endpoint: string } @@ -187,34 +180,24 @@ export class MessageSender { } public async sendMessage( - agentContext: AgentContext, - outboundMessage: OutboundDidCommV1Message | OutboundDidCommV2Message, + outboundMessageContext: OutboundMessageContext, options?: { transportPriority?: TransportPriorityOptions } ) { - if (isDidCommV1OutboundMessage(outboundMessage)) { - return this.sendDIDCommV1Message(agentContext, outboundMessage, options) - } - if (isDidCommV2OutboundMessage(outboundMessage)) { - return this.sendDIDCommV2Message(agentContext, outboundMessage, options) - } - throw new AriesFrameworkError(`Unexpected case`) - } + const { agentContext, connection, outOfBand, sessionId, message } = outboundMessageContext + const errors: Error[] = [] - public async sendDIDCommV1Message( - agentContext: AgentContext, - outboundMessage: OutboundDidCommV1Message, - options?: { - sendingMessageType?: MessageType - transportPriority?: TransportPriorityOptions + if (!connection) { + this.logger.error('Outbound message has no associated connection') + this.emitMessageSentEvent(outboundMessageContext, OutboundMessageSendStatus.Undeliverable) + throw new MessageSendingError('Outbound message has no associated connection', { + outboundMessageContext, + }) } - ) { - const { connection, outOfBand, sessionId, payload } = outboundMessage - const errors: Error[] = [] this.logger.debug('Send outbound message', { - message: payload, + message, connectionId: connection.id, }) @@ -228,10 +211,11 @@ export class MessageSender { session = this.transportService.findSessionByConnectionId(connection.id) } - if (session?.inboundMessage?.hasReturnRouting(payload.threadId)) { - this.logger.debug(`Found session with return routing for message '${payload.id}' (connection '${connection.id}'`) + if (session?.inboundMessage?.hasReturnRouting(message.threadId)) { + this.logger.debug(`Found session with return routing for message '${message.id}' (connection '${connection.id}'`) try { - await this.sendMessageToSession(agentContext, session, payload) + await this.sendMessageToSession(agentContext, session, message) + this.emitMessageSentEvent(outboundMessageContext, OutboundMessageSendStatus.SentToSession) return } catch (error) { errors.push(error) @@ -240,22 +224,46 @@ export class MessageSender { } // Retrieve DIDComm services - const { services, queueService } = await this.retrieveServicesByConnection( - agentContext, - connection, - options?.transportPriority, - outOfBand - ) + let services: ResolvedDidCommService[] = [] + let queueService: ResolvedDidCommService | undefined + + try { + ;({ services, queueService } = await this.retrieveServicesByConnection( + agentContext, + connection, + options?.transportPriority, + outOfBand + )) + } catch (error) { + this.logger.error(`Unable to retrieve services for connection '${connection.id}`) + this.emitMessageSentEvent(outboundMessageContext, OutboundMessageSendStatus.Undeliverable) + throw new MessageSendingError(`Unable to retrieve services for connection '${connection.id}`, { + outboundMessageContext, + cause: error, + }) + } if (!connection.did) { this.logger.error(`Unable to send message using connection '${connection.id}' that doesn't have a did`) + this.emitMessageSentEvent(outboundMessageContext, OutboundMessageSendStatus.Undeliverable) throw new MessageSendingError( `Unable to send message using connection '${connection.id}' that doesn't have a did`, - { outboundMessage } + { outboundMessageContext } ) } - const ourDidDocument = await this.didResolverService.resolveDidDocument(agentContext, connection.did) + let ourDidDocument: DidDocument + try { + ourDidDocument = await this.didResolverService.resolveDidDocument(agentContext, connection.did) + } catch (error) { + this.logger.error(`Unable to resolve DID Document for '${connection.did}`) + this.emitMessageSentEvent(outboundMessageContext, OutboundMessageSendStatus.Undeliverable) + throw new MessageSendingError(`Unable to resolve DID Document for '${connection.did}`, { + outboundMessageContext, + cause: error, + }) + } + const ourAuthenticationKeys = getAuthenticationKeys(ourDidDocument) // TODO We're selecting just the first authentication key. Is it ok? @@ -270,19 +278,24 @@ export class MessageSender { const [firstOurAuthenticationKey] = ourAuthenticationKeys // If the returnRoute is already set we won't override it. This allows to set the returnRoute manually if this is desired. const shouldAddReturnRoute = - payload.transport?.returnRoute === undefined && !this.transportService.hasInboundEndpoint(ourDidDocument) + message.transport?.returnRoute === undefined && !this.transportService.hasInboundEndpoint(ourDidDocument) // Loop trough all available services and try to send the message for await (const service of services) { try { - // Enable return routing if the our did document does not have any inbound endpoint for given senderDid key - await this.sendMessageToService(agentContext, { - message: payload, - service, - senderKey: firstOurAuthenticationKey, - returnRoute: shouldAddReturnRoute, - connectionId: connection.id, - }) + // Enable return routing if the our did document does not have any inbound endpoint for given sender key + await this.sendToService( + new OutboundMessageContext(message, { + agentContext, + serviceParams: { + service, + senderKey: firstOurAuthenticationKey, + returnRoute: shouldAddReturnRoute, + }, + connection, + }) + ) + this.emitMessageSentEvent(outboundMessageContext, OutboundMessageSendStatus.SentToTransport) return } catch (error) { errors.push(error) @@ -307,39 +320,57 @@ export class MessageSender { senderKey: firstOurAuthenticationKey, } - const encryptedMessage = await this.envelopeService.packMessage(agentContext, payload, keys) + const encryptedMessage = await this.envelopeService.packMessage(agentContext, message, keys) await this.messageRepository.add(connection.id, encryptedMessage) + + this.emitMessageSentEvent(outboundMessageContext, OutboundMessageSendStatus.QueuedForPickup) + return } // Message is undeliverable this.logger.error(`Message is undeliverable to connection ${connection.id} (${connection.theirLabel})`, { - message: payload, + message, errors, connection, }) + this.emitMessageSentEvent(outboundMessageContext, OutboundMessageSendStatus.Undeliverable) + throw new MessageSendingError( `Message is undeliverable to connection ${connection.id} (${connection.theirLabel})`, - { outboundMessage } + { outboundMessageContext } ) } - public async sendMessageToService( - agentContext: AgentContext, - { - message, - service, - senderKey, - returnRoute, - connectionId, - }: { - message: AgentMessage - service: ResolvedDidCommService - senderKey: Key - returnRoute?: boolean - connectionId?: string + public async sendMessageToService(outboundMessageContext: OutboundMessageContext) { + try { + await this.sendToService(outboundMessageContext) + this.emitMessageSentEvent(outboundMessageContext, OutboundMessageSendStatus.SentToTransport) + } catch (error) { + this.logger.error( + `Message is undeliverable to service with id ${outboundMessageContext.serviceParams?.service.id}: ${error.message}`, + { + message: outboundMessageContext.message, + error, + } + ) + this.emitMessageSentEvent(outboundMessageContext, OutboundMessageSendStatus.Undeliverable) + + throw new MessageSendingError( + `Message is undeliverable to service with id ${outboundMessageContext.serviceParams?.service.id}: ${error.message}`, + { outboundMessageContext } + ) } - ) { + } + + private async sendToService(outboundMessageContext: OutboundMessageContext) { + const { agentContext, message, serviceParams, connection } = outboundMessageContext + + if (!serviceParams) { + throw new AriesFrameworkError('No service parameters found in outbound message context') + } + const { service, senderKey, returnRoute } = serviceParams + if (this.outboundTransports.length === 0) { throw new AriesFrameworkError('Agent has no outbound transport!') } @@ -376,7 +407,7 @@ export class MessageSender { const outboundPackage = await this.packMessage(agentContext, { message, keys, endpoint: service.serviceEndpoint }) outboundPackage.endpoint = service.serviceEndpoint - outboundPackage.connectionId = connectionId + outboundPackage.connectionId = connection?.id for (const transport of this.outboundTransports) { const protocolScheme = getProtocolScheme(service.serviceEndpoint) if (!protocolScheme) { @@ -386,7 +417,9 @@ export class MessageSender { return } } - throw new AriesFrameworkError(`Unable to send message to service: ${service.serviceEndpoint}`) + throw new MessageSendingError(`Unable to send message to service: ${service.serviceEndpoint}`, { + outboundMessageContext, + }) } private async retrieveServicesByConnection( @@ -454,201 +487,15 @@ export class MessageSender { return { services, queueService } } - public async sendDIDCommV2Message( - agentContext: AgentContext, - outboundMessage: OutboundDidCommV2Message, - options?: { - sendingMessageType?: MessageType - transportPriority?: TransportPriorityOptions - } - ) { - const { payload: message } = outboundMessage - const sendingMessageType = options?.sendingMessageType || MessageType.Encrypted - - // recipient is not specified -> send to defaultTransport - const recipient = message.firstRecipient - if (!recipient) { - throw new AriesFrameworkError(`Unable to send message. Message doesn't contain recipient DID.`) - } - - // find service transport supported for both senderDid and receiver - const senderToRecipientService = await this.findCommonSupportedServices( - agentContext, - recipient, - message.senderDid, - options?.transportPriority - ) - if (!senderToRecipientService) { - this.logger.error( - `Unable to send message ${message.id} because there is no any commonly supported service between sender and recipient` - ) - return - } - - if (sendingMessageType === MessageType.Plain) { - // send message plaintext - return await this.sendDIDCommV2PlaintextMessage(agentContext, message, senderToRecipientService) - } - - if (sendingMessageType === MessageType.Signed) { - // send message signed - return await this.sendDIDCommV2SignedMessage(agentContext, message, senderToRecipientService) - } - - if (sendingMessageType === MessageType.Encrypted) { - // send message encrypted - return await this.sendDIDCommV2EncryptedMessage(agentContext, message, senderToRecipientService) - } - } - - public async findCommonSupportedServices( - agentContext: AgentContext, - recipient: string, - sender?: string, - transportPriority?: TransportPriorityOptions - ): Promise { - if (!sender) return undefined - - const { didDocument: senderDidDocument } = await this.didResolverService.resolve(agentContext, sender) - - const { didDocument: recipientDidDocument } = await this.didResolverService.resolve(agentContext, recipient) - if (!recipientDidDocument) { - throw new AriesFrameworkError(`Unable to resolve did document for did '${recipient}'`) - } - - const senderServices = senderDidDocument?.service || [] - const recipientServices = recipientDidDocument?.service || [] - - const senderTransports = senderServices.length - ? senderServices.map((service) => service.protocolScheme) - : this.outboundTransportsSchemas - - const supportedTransports = transportPriority - ? [...transportPriority.schemes, ...senderTransports] - : senderTransports - - // Sort services according to supported transports - const priority = supportedTransports.map((transport) => transport.toString()) - - const services = recipientServices.sort(function (a, b) { - return priority.indexOf(a.protocolScheme) - priority.indexOf(b.protocolScheme) - }) - - const commonServices = services.filter((service) => { - if (priority.includes(service.protocolScheme)) return service + private emitMessageSentEvent(outboundMessageContext: OutboundMessageContext, status: OutboundMessageSendStatus) { + const { agentContext } = outboundMessageContext + this.eventEmitter.emit(agentContext, { + type: AgentEventTypes.AgentMessageSent, + payload: { + message: outboundMessageContext, + status, + }, }) - - return commonServices - } - - private async sendDIDCommV2PlaintextMessage( - agentContext: AgentContext, - message: DidCommV2Message, - services: DidDocumentService[] - ) { - this.logger.debug(`Sending plaintext message ${message.id}`) - const recipientDid = message.firstRecipient - return this.sendOutboundDIDCommV2Message(agentContext, message, services, recipientDid) - } - - private async sendDIDCommV2SignedMessage( - agentContext: AgentContext, - message: DidCommV2Message, - services: DidDocumentService[] - ) { - this.logger.debug(`Sending JWS message ${message.id}`) - - const recipientDid = message.firstRecipient - - const pack = async (message: DidCommV2Message, service: DidDocumentService) => { - if (!message.from) { - throw new AriesFrameworkError(`Unable to send message signed. Message doesn't contain sender DID.`) - } - const params = { signByDID: message.from, serviceId: service?.id, type: MessageType.Signed } - return this.envelopeService.packMessage(agentContext, message, params) - } - - return this.sendOutboundDIDCommV2Message(agentContext, message, services, recipientDid, pack) - } - - private async sendDIDCommV2EncryptedMessage( - agentContext: AgentContext, - message: DidCommV2Message, - services: DidDocumentService[] - ) { - const recipientDid = message.firstRecipient - if (!recipientDid) { - throw new AriesFrameworkError(`Unable to send message encrypted. Message doesn't contain recipient DID.`) - } - this.logger.debug(`Sending JWE message ${message.id}`) - - const pack = async (message: DidCommV2Message, service: DidDocumentService) => { - return await this.encryptDIDCommV2Message(agentContext, message, service) - } - - return this.sendOutboundDIDCommV2Message(agentContext, message, services, recipientDid, pack) - } - - private async encryptDIDCommV2Message( - agentContext: AgentContext, - message: DidCommV2Message, - service: DidDocumentService, - forward?: boolean - ) { - const recipientDid = message.firstRecipient - if (!recipientDid) { - throw new AriesFrameworkError(`Unable to send message encrypted. Message doesn't contain recipient DID.`) - } - - const params: PackMessageParams = { - toDid: recipientDid, - fromDid: message.from, - signByDid: undefined, - serviceId: service?.id, - wrapIntoForward: forward, - type: MessageType.Encrypted, - } - return this.envelopeService.packMessage(agentContext, message, params) - } - - public async sendOutboundDIDCommV2Message( - agentContext: AgentContext, - message: DidCommV2Message, - services: DidDocumentService[], - recipientDid?: string, - packMessage?: (message: DidCommV2Message, service: DidDocumentService) => Promise - ) { - for (const service of services) { - try { - this.logger.info(`Sending message to ${service.serviceEndpoint}. Transport ${service.protocolScheme}`) - - const payload = packMessage ? await packMessage(message, service) : { ...message } - const outboundPackage = { payload, recipientDid, endpoint: service.serviceEndpoint } - - this.logger.trace(`Sending outbound message to transport:`, { - transport: service.protocolScheme, - outboundPackage, - }) - - for (const outboundTransport of this.outboundTransports) { - if (outboundTransport.supportedSchemes.includes(service.protocolScheme)) { - await outboundTransport.sendMessage(outboundPackage) - break - } - } - - this.logger.info( - `Message sent ${message.id} to ${service.serviceEndpoint}. Transport ${service.protocolScheme}` - ) - return - } catch (error) { - this.logger.warn(`Unable to send message to ${service.serviceEndpoint}. Transport failure `, { - errors: error, - }) - // ignore and try another transport - } - } - this.logger.error(`Unable to send message ${message.id} through any commonly supported transport.`) } } diff --git a/packages/core/src/agent/__tests__/MessageSender.test.ts b/packages/core/src/agent/__tests__/MessageSender.test.ts index 64975c8c45..35d566c3b0 100644 --- a/packages/core/src/agent/__tests__/MessageSender.test.ts +++ b/packages/core/src/agent/__tests__/MessageSender.test.ts @@ -1,41 +1,49 @@ -import type { EncryptedMessage } from '../../didcomm/types' -import type { OutboundDidCommV1Message } from '../../didcomm/versions/v1' +/* eslint-disable @typescript-eslint/ban-ts-comment */ import type { ConnectionRecord } from '../../modules/connections' import type { ResolvedDidCommService } from '../../modules/didcomm' import type { DidDocumentService } from '../../modules/dids' import type { MessageRepository } from '../../storage/MessageRepository' import type { OutboundTransport } from '../../transport' +import type { EncryptedMessage } from '../../types' +import type { AgentMessageSentEvent } from '../Events' + +import { Subject } from 'rxjs' import { TestMessage } from '../../../tests/TestMessage' -import { getAgentConfig, getAgentContext, getMockConnection, mockFunction } from '../../../tests/helpers' +import { + agentDependencies, + getAgentConfig, + getAgentContext, + getMockConnection, + mockFunction, +} from '../../../tests/helpers' import testLogger from '../../../tests/logger' import { Key, KeyType } from '../../crypto' import { ReturnRouteTypes } from '../../decorators/transport/TransportDecorator' import { DidCommDocumentService } from '../../modules/didcomm' -import { DidDocument, DidResolverService, VerificationMethod } from '../../modules/dids' +import { DidResolverService, DidDocument, VerificationMethod } from '../../modules/dids' import { DidCommV1Service } from '../../modules/dids/domain/service/DidCommV1Service' import { verkeyToInstanceOfKey } from '../../modules/dids/helpers' -import { OutOfBandRepository } from '../../modules/oob' import { InMemoryMessageRepository } from '../../storage/InMemoryMessageRepository' import { EnvelopeService as EnvelopeServiceImpl } from '../EnvelopeService' +import { EventEmitter } from '../EventEmitter' +import { AgentEventTypes } from '../Events' import { MessageSender } from '../MessageSender' import { TransportService } from '../TransportService' -import { createOutboundDidCommV1Message } from '../helpers' +import { OutboundMessageContext, OutboundMessageSendStatus } from '../models' import { DummyTransportSession } from './stubs' jest.mock('../TransportService') -jest.mock('../didcomm/EnvelopeService') +jest.mock('../EnvelopeService') jest.mock('../../modules/dids/services/DidResolverService') jest.mock('../../modules/didcomm/services/DidCommDocumentService') -jest.mock('../../modules/oob/repository/OutOfBandRepository') const logger = testLogger const TransportServiceMock = TransportService as jest.MockedClass const DidResolverServiceMock = DidResolverService as jest.Mock const DidCommDocumentServiceMock = DidCommDocumentService as jest.Mock -const OutOfBandRepositoryMock = OutOfBandRepository as jest.Mock class DummyHttpOutboundTransport implements OutboundTransport { public start(): Promise { @@ -77,7 +85,6 @@ describe('MessageSender', () => { iv: 'base64url', ciphertext: 'base64url', tag: 'base64url', - recipients: [], } const enveloperService = new EnvelopeService() @@ -85,7 +92,7 @@ describe('MessageSender', () => { const didResolverService = new DidResolverServiceMock() const didCommDocumentService = new DidCommDocumentServiceMock() - const outOfBandRepository = new OutOfBandRepositoryMock() + const eventEmitter = new EventEmitter(agentDependencies, new Subject()) const didResolverServiceResolveMock = mockFunction(didResolverService.resolveDidDocument) const didResolverServiceResolveDidServicesMock = mockFunction(didCommDocumentService.resolveServicesFromDid) @@ -127,15 +134,18 @@ describe('MessageSender', () => { let outboundTransport: OutboundTransport let messageRepository: MessageRepository let connection: ConnectionRecord - let outboundMessage: OutboundDidCommV1Message + let outboundMessageContext: OutboundMessageContext const agentConfig = getAgentConfig('MessageSender') const agentContext = getAgentContext() + const eventListenerMock = jest.fn() describe('sendMessage', () => { beforeEach(() => { TransportServiceMock.mockClear() DidResolverServiceMock.mockClear() + eventEmitter.on(AgentEventTypes.AgentMessageSent, eventListenerMock) + outboundTransport = new DummyHttpOutboundTransport() messageRepository = new InMemoryMessageRepository(agentConfig.logger) messageSender = new MessageSender( @@ -145,7 +155,7 @@ describe('MessageSender', () => { logger, didResolverService, didCommDocumentService, - outOfBandRepository + eventEmitter ) connection = getMockConnection({ id: 'test-123', @@ -153,7 +163,7 @@ describe('MessageSender', () => { theirDid: 'did:peer:1theirdid', theirLabel: 'Test 123', }) - outboundMessage = createOutboundDidCommV1Message(connection, new TestMessage()) + outboundMessageContext = new OutboundMessageContext(new TestMessage(), { agentContext, connection }) envelopeServicePackMessageMock.mockReturnValue(Promise.resolve(encryptedMessage)) transportServiceHasInboundEndpoint.mockReturnValue(true) @@ -169,13 +179,25 @@ describe('MessageSender', () => { }) afterEach(() => { + eventEmitter.off(AgentEventTypes.AgentMessageSent, eventListenerMock) + jest.resetAllMocks() }) test('throw error when there is no outbound transport', async () => { - await expect(messageSender.sendMessage(agentContext, outboundMessage)).rejects.toThrow( + await expect(messageSender.sendMessage(outboundMessageContext)).rejects.toThrow( /Message is undeliverable to connection/ ) + expect(eventListenerMock).toHaveBeenCalledWith({ + type: AgentEventTypes.AgentMessageSent, + metadata: { + contextCorrelationId: 'mock', + }, + payload: { + message: outboundMessageContext, + status: OutboundMessageSendStatus.Undeliverable, + }, + }) }) test('throw error when there is no service or queue', async () => { @@ -184,9 +206,19 @@ describe('MessageSender', () => { didResolverServiceResolveMock.mockResolvedValue(getMockDidDocument({ service: [] })) didResolverServiceResolveDidServicesMock.mockResolvedValue([]) - await expect(messageSender.sendMessage(agentContext, outboundMessage)).rejects.toThrow( + await expect(messageSender.sendMessage(outboundMessageContext)).rejects.toThrow( `Message is undeliverable to connection test-123 (Test 123)` ) + expect(eventListenerMock).toHaveBeenCalledWith({ + type: AgentEventTypes.AgentMessageSent, + metadata: { + contextCorrelationId: 'mock', + }, + payload: { + message: outboundMessageContext, + status: OutboundMessageSendStatus.Undeliverable, + }, + }) }) test('call send message when session send method fails', async () => { @@ -197,7 +229,18 @@ describe('MessageSender', () => { messageSender.registerOutboundTransport(outboundTransport) const sendMessageSpy = jest.spyOn(outboundTransport, 'sendMessage') - await messageSender.sendMessage(agentContext, outboundMessage) + await messageSender.sendMessage(outboundMessageContext) + + expect(eventListenerMock).toHaveBeenCalledWith({ + type: AgentEventTypes.AgentMessageSent, + metadata: { + contextCorrelationId: 'mock', + }, + payload: { + message: outboundMessageContext, + status: OutboundMessageSendStatus.SentToTransport, + }, + }) expect(sendMessageSpy).toHaveBeenCalledWith({ connectionId: 'test-123', @@ -213,7 +256,18 @@ describe('MessageSender', () => { const sendMessageSpy = jest.spyOn(outboundTransport, 'sendMessage') - await messageSender.sendMessage(agentContext, outboundMessage) + await messageSender.sendMessage(outboundMessageContext) + + expect(eventListenerMock).toHaveBeenCalledWith({ + type: AgentEventTypes.AgentMessageSent, + metadata: { + contextCorrelationId: 'mock', + }, + payload: { + message: outboundMessageContext, + status: OutboundMessageSendStatus.SentToTransport, + }, + }) expect(didResolverServiceResolveDidServicesMock).toHaveBeenCalledWith(agentContext, connection.theirDid) expect(sendMessageSpy).toHaveBeenCalledWith({ @@ -232,9 +286,20 @@ describe('MessageSender', () => { new Error(`Unable to resolve did document for did '${connection.theirDid}': notFound`) ) - await expect(messageSender.sendMessage(agentContext, outboundMessage)).rejects.toThrowError( - `Unable to resolve did document for did '${connection.theirDid}': notFound` + await expect(messageSender.sendMessage(outboundMessageContext)).rejects.toThrowError( + `Unable to resolve DID Document for '${connection.did}` ) + + expect(eventListenerMock).toHaveBeenCalledWith({ + type: AgentEventTypes.AgentMessageSent, + metadata: { + contextCorrelationId: 'mock', + }, + payload: { + message: outboundMessageContext, + status: OutboundMessageSendStatus.Undeliverable, + }, + }) }) test('call send message when session send method fails with missing keys', async () => { @@ -244,7 +309,18 @@ describe('MessageSender', () => { messageSender.registerOutboundTransport(outboundTransport) const sendMessageSpy = jest.spyOn(outboundTransport, 'sendMessage') - await messageSender.sendMessage(agentContext, outboundMessage) + await messageSender.sendMessage(outboundMessageContext) + + expect(eventListenerMock).toHaveBeenCalledWith({ + type: AgentEventTypes.AgentMessageSent, + metadata: { + contextCorrelationId: 'mock', + }, + payload: { + message: outboundMessageContext, + status: OutboundMessageSendStatus.SentToTransport, + }, + }) expect(sendMessageSpy).toHaveBeenCalledWith({ connectionId: 'test-123', @@ -261,7 +337,24 @@ describe('MessageSender', () => { const sendMessageSpy = jest.spyOn(outboundTransport, 'sendMessage') const sendMessageToServiceSpy = jest.spyOn(messageSender, 'sendMessageToService') - await messageSender.sendMessage(agentContext, { ...outboundMessage, sessionId: 'session-123' }) + const contextWithSessionId = new OutboundMessageContext(outboundMessageContext.message, { + agentContext: outboundMessageContext.agentContext, + connection: outboundMessageContext.connection, + sessionId: 'session-123', + }) + + await messageSender.sendMessage(contextWithSessionId) + + expect(eventListenerMock).toHaveBeenCalledWith({ + type: AgentEventTypes.AgentMessageSent, + metadata: { + contextCorrelationId: 'mock', + }, + payload: { + message: contextWithSessionId, + status: OutboundMessageSendStatus.SentToSession, + }, + }) expect(session.send).toHaveBeenCalledTimes(1) expect(session.send).toHaveBeenNthCalledWith(1, encryptedMessage) @@ -273,64 +366,119 @@ describe('MessageSender', () => { test('call send message on session when there is a session for a given connection', async () => { messageSender.registerOutboundTransport(outboundTransport) const sendMessageSpy = jest.spyOn(outboundTransport, 'sendMessage') - const sendMessageToServiceSpy = jest.spyOn(messageSender, 'sendMessageToService') + //@ts-ignore + const sendToServiceSpy = jest.spyOn(messageSender, 'sendToService') - await messageSender.sendMessage(agentContext, outboundMessage) + await messageSender.sendMessage(outboundMessageContext) - const [[, sendMessage]] = sendMessageToServiceSpy.mock.calls + //@ts-ignore + const [[sendMessage]] = sendToServiceSpy.mock.calls + expect(eventListenerMock).toHaveBeenCalledWith({ + type: AgentEventTypes.AgentMessageSent, + metadata: { + contextCorrelationId: 'mock', + }, + payload: { + message: outboundMessageContext, + status: OutboundMessageSendStatus.SentToTransport, + }, + }) expect(sendMessage).toMatchObject({ - connectionId: 'test-123', - message: outboundMessage.payload, - returnRoute: false, - service: { - serviceEndpoint: firstDidCommService.serviceEndpoint, + connection: { + id: 'test-123', + }, + message: outboundMessageContext.message, + serviceParams: { + returnRoute: false, + service: { + serviceEndpoint: firstDidCommService.serviceEndpoint, + }, }, }) - expect(sendMessage.senderKey.publicKeyBase58).toEqual('EoGusetSxDJktp493VCyh981nUnzMamTRjvBaHZAy68d') - expect(sendMessage.service.recipientKeys.map((key) => key.publicKeyBase58)).toEqual([ + //@ts-ignore + expect(sendMessage.serviceParams.senderKey.publicKeyBase58).toEqual( + 'EoGusetSxDJktp493VCyh981nUnzMamTRjvBaHZAy68d' + ) + + //@ts-ignore + expect(sendMessage.serviceParams.service.recipientKeys.map((key) => key.publicKeyBase58)).toEqual([ 'EoGusetSxDJktp493VCyh981nUnzMamTRjvBaHZAy68d', ]) - expect(sendMessageToServiceSpy).toHaveBeenCalledTimes(1) + expect(sendToServiceSpy).toHaveBeenCalledTimes(1) expect(sendMessageSpy).toHaveBeenCalledTimes(1) }) - test('calls sendMessageToService with payload and endpoint from second DidComm service when the first fails', async () => { + test('calls sendToService with payload and endpoint from second DidComm service when the first fails', async () => { messageSender.registerOutboundTransport(outboundTransport) const sendMessageSpy = jest.spyOn(outboundTransport, 'sendMessage') - const sendMessageToServiceSpy = jest.spyOn(messageSender, 'sendMessageToService') + //@ts-ignore + const sendToServiceSpy = jest.spyOn(messageSender, 'sendToService') // Simulate the case when the first call fails sendMessageSpy.mockRejectedValueOnce(new Error()) - await messageSender.sendMessage(agentContext, outboundMessage) + await messageSender.sendMessage(outboundMessageContext) + + expect(eventListenerMock).toHaveBeenCalledWith({ + type: AgentEventTypes.AgentMessageSent, + metadata: { + contextCorrelationId: 'mock', + }, + payload: { + message: outboundMessageContext, + status: OutboundMessageSendStatus.SentToTransport, + }, + }) + + //@ts-ignore + const [, [sendMessage]] = sendToServiceSpy.mock.calls - const [, [, sendMessage]] = sendMessageToServiceSpy.mock.calls expect(sendMessage).toMatchObject({ - connectionId: 'test-123', - message: outboundMessage.payload, - returnRoute: false, - service: { - serviceEndpoint: secondDidCommService.serviceEndpoint, + agentContext, + connection: { + id: 'test-123', + }, + message: outboundMessageContext.message, + serviceParams: { + returnRoute: false, + service: { + serviceEndpoint: secondDidCommService.serviceEndpoint, + }, }, }) - expect(sendMessage.senderKey.publicKeyBase58).toEqual('EoGusetSxDJktp493VCyh981nUnzMamTRjvBaHZAy68d') - expect(sendMessage.service.recipientKeys.map((key) => key.publicKeyBase58)).toEqual([ + //@ts-ignore + expect(sendMessage.serviceParams.senderKey.publicKeyBase58).toEqual( + 'EoGusetSxDJktp493VCyh981nUnzMamTRjvBaHZAy68d' + ) + //@ts-ignore + expect(sendMessage.serviceParams.service.recipientKeys.map((key) => key.publicKeyBase58)).toEqual([ 'EoGusetSxDJktp493VCyh981nUnzMamTRjvBaHZAy68d', ]) - expect(sendMessageToServiceSpy).toHaveBeenCalledTimes(2) + expect(sendToServiceSpy).toHaveBeenCalledTimes(2) expect(sendMessageSpy).toHaveBeenCalledTimes(2) }) test('throw error when message endpoint is not supported by outbound transport schemes', async () => { messageSender.registerOutboundTransport(new DummyWsOutboundTransport()) - await expect(messageSender.sendMessage(agentContext, outboundMessage)).rejects.toThrow( + await expect(messageSender.sendMessage(outboundMessageContext)).rejects.toThrow( /Message is undeliverable to connection/ ) + + expect(eventListenerMock).toHaveBeenCalledWith({ + type: AgentEventTypes.AgentMessageSent, + metadata: { + contextCorrelationId: 'mock', + }, + payload: { + message: outboundMessageContext, + status: OutboundMessageSendStatus.Undeliverable, + }, + }) }) }) @@ -352,34 +500,66 @@ describe('MessageSender', () => { logger, didResolverService, didCommDocumentService, - outOfBandRepository + eventEmitter ) + eventEmitter.on(AgentEventTypes.AgentMessageSent, eventListenerMock) + envelopeServicePackMessageMock.mockReturnValue(Promise.resolve(encryptedMessage)) }) afterEach(() => { jest.resetAllMocks() + eventEmitter.off(AgentEventTypes.AgentMessageSent, eventListenerMock) }) test('throws error when there is no outbound transport', async () => { - await expect( - messageSender.sendMessageToService(agentContext, { - message: new TestMessage(), + outboundMessageContext = new OutboundMessageContext(new TestMessage(), { + agentContext, + serviceParams: { senderKey, service, - }) - ).rejects.toThrow(`Agent has no outbound transport!`) + }, + }) + await expect(messageSender.sendMessageToService(outboundMessageContext)).rejects.toThrow( + `Agent has no outbound transport!` + ) + + expect(eventListenerMock).toHaveBeenCalledWith({ + type: AgentEventTypes.AgentMessageSent, + metadata: { + contextCorrelationId: 'mock', + }, + payload: { + message: outboundMessageContext, + status: OutboundMessageSendStatus.Undeliverable, + }, + }) }) test('calls send message with payload and endpoint from DIDComm service', async () => { messageSender.registerOutboundTransport(outboundTransport) const sendMessageSpy = jest.spyOn(outboundTransport, 'sendMessage') - await messageSender.sendMessageToService(agentContext, { - message: new TestMessage(), - senderKey, - service, + outboundMessageContext = new OutboundMessageContext(new TestMessage(), { + agentContext, + serviceParams: { + senderKey, + service, + }, + }) + + await messageSender.sendMessageToService(outboundMessageContext) + + expect(eventListenerMock).toHaveBeenCalledWith({ + type: AgentEventTypes.AgentMessageSent, + metadata: { + contextCorrelationId: 'mock', + }, + payload: { + message: outboundMessageContext, + status: OutboundMessageSendStatus.SentToTransport, + }, }) expect(sendMessageSpy).toHaveBeenCalledWith({ @@ -397,10 +577,25 @@ describe('MessageSender', () => { const message = new TestMessage() message.setReturnRouting(ReturnRouteTypes.all) - await messageSender.sendMessageToService(agentContext, { - message, - senderKey, - service, + outboundMessageContext = new OutboundMessageContext(message, { + agentContext, + serviceParams: { + senderKey, + service, + }, + }) + + await messageSender.sendMessageToService(outboundMessageContext) + + expect(eventListenerMock).toHaveBeenCalledWith({ + type: AgentEventTypes.AgentMessageSent, + metadata: { + contextCorrelationId: 'mock', + }, + payload: { + message: outboundMessageContext, + status: OutboundMessageSendStatus.SentToTransport, + }, }) expect(sendMessageSpy).toHaveBeenCalledWith({ @@ -413,13 +608,27 @@ describe('MessageSender', () => { test('throw error when message endpoint is not supported by outbound transport schemes', async () => { messageSender.registerOutboundTransport(new DummyWsOutboundTransport()) - await expect( - messageSender.sendMessageToService(agentContext, { - message: new TestMessage(), + outboundMessageContext = new OutboundMessageContext(new TestMessage(), { + agentContext, + serviceParams: { senderKey, service, - }) - ).rejects.toThrow(/Unable to send message to service/) + }, + }) + + await expect(messageSender.sendMessageToService(outboundMessageContext)).rejects.toThrow( + /Unable to send message to service/ + ) + expect(eventListenerMock).toHaveBeenCalledWith({ + type: AgentEventTypes.AgentMessageSent, + metadata: { + contextCorrelationId: 'mock', + }, + payload: { + message: outboundMessageContext, + status: OutboundMessageSendStatus.Undeliverable, + }, + }) }) }) @@ -434,7 +643,7 @@ describe('MessageSender', () => { logger, didResolverService, didCommDocumentService, - outOfBandRepository + eventEmitter ) connection = getMockConnection() diff --git a/packages/core/src/agent/helpers.ts b/packages/core/src/agent/helpers.ts deleted file mode 100644 index 999aba9c82..0000000000 --- a/packages/core/src/agent/helpers.ts +++ /dev/null @@ -1,61 +0,0 @@ -import type { Key } from '../crypto' -import type { - DidCommV1Message, - DidCommV2Message, - OutboundDidCommV1Message, - OutboundDidCommV1ServiceMessage, - OutboundDidCommV2Message, -} from '../didcomm' -import type { ConnectionRecord } from '../modules/connections' -import type { ResolvedDidCommService } from '../modules/didcomm' -import type { OutOfBandRecord } from '../modules/oob/repository' - -import { DidCommMessageVersion } from '../didcomm/types' - -export function createOutboundDidCommV1Message( - connection: ConnectionRecord, - payload: T, - outOfBand?: OutOfBandRecord -): OutboundDidCommV1Message { - return { - connection, - outOfBand, - payload, - } -} - -export function createOutboundServiceMessage(options: { - payload: T - service: ResolvedDidCommService - senderKey: Key -}): OutboundDidCommV1ServiceMessage { - return options -} - -export function createOutboundDidCommV2Message( - payload: T -): OutboundDidCommV2Message { - return { - payload, - } -} - -export function isOutboundServiceMessage( - message: OutboundDidCommV1Message | OutboundDidCommV1ServiceMessage | OutboundDidCommV2Message -): message is OutboundDidCommV1ServiceMessage { - const service = (message as OutboundDidCommV1ServiceMessage).service - - return service !== undefined -} - -export function isOutboundDidCommV1Message( - message: OutboundDidCommV1Message | OutboundDidCommV1ServiceMessage | OutboundDidCommV2Message -): message is OutboundDidCommV1Message { - return message.payload.didCommVersion === DidCommMessageVersion.V1 -} - -export function isOutboundDidCommV2Message( - message: OutboundDidCommV1Message | OutboundDidCommV1ServiceMessage | OutboundDidCommV2Message -): message is OutboundDidCommV2Message { - return message.payload.didCommVersion === DidCommMessageVersion.V2 -} diff --git a/packages/core/src/agent/models/OutboundMessageContext.ts b/packages/core/src/agent/models/OutboundMessageContext.ts new file mode 100644 index 0000000000..e61929a47d --- /dev/null +++ b/packages/core/src/agent/models/OutboundMessageContext.ts @@ -0,0 +1,76 @@ +import type { Key } from '../../crypto' +import type { ConnectionRecord } from '../../modules/connections' +import type { ResolvedDidCommService } from '../../modules/didcomm' +import type { OutOfBandRecord } from '../../modules/oob' +import type { BaseRecord } from '../../storage/BaseRecord' +import type { AgentMessage } from '../AgentMessage' +import type { AgentContext } from '../context' + +import { AriesFrameworkError } from '../../error' + +export interface ServiceMessageParams { + senderKey: Key + service: ResolvedDidCommService + returnRoute?: boolean +} + +export interface OutboundMessageContextParams { + agentContext: AgentContext + associatedRecord?: BaseRecord + connection?: ConnectionRecord + serviceParams?: ServiceMessageParams + outOfBand?: OutOfBandRecord + sessionId?: string +} + +export class OutboundMessageContext { + public message: T + public connection?: ConnectionRecord + public serviceParams?: ServiceMessageParams + public outOfBand?: OutOfBandRecord + public associatedRecord?: BaseRecord + public sessionId?: string + public readonly agentContext: AgentContext + + public constructor(message: T, context: OutboundMessageContextParams) { + this.message = message + this.connection = context.connection + this.sessionId = context.sessionId + this.outOfBand = context.outOfBand + this.serviceParams = context.serviceParams + this.associatedRecord = context.associatedRecord + this.agentContext = context.agentContext + } + + /** + * Assert the outbound message has a ready connection associated with it. + * + * @throws {AriesFrameworkError} if there is no connection or the connection is not ready + */ + public assertReadyConnection(): ConnectionRecord { + if (!this.connection) { + throw new AriesFrameworkError(`No connection associated with outgoing message ${this.message.type}`) + } + + // Make sure connection is ready + this.connection.assertReady() + + return this.connection + } + + public isOutboundServiceMessage(): boolean { + return this.serviceParams?.service !== undefined + } + + public toJSON() { + return { + message: this.message, + outOfBand: this.outOfBand, + associatedRecord: this.associatedRecord, + sessionId: this.sessionId, + serviceParams: this.serviceParams, + agentContext: this.agentContext.toJSON(), + connection: this.connection, + } + } +} diff --git a/packages/core/src/agent/models/OutboundMessageSendStatus.ts b/packages/core/src/agent/models/OutboundMessageSendStatus.ts new file mode 100644 index 0000000000..6fdb4f7f68 --- /dev/null +++ b/packages/core/src/agent/models/OutboundMessageSendStatus.ts @@ -0,0 +1,6 @@ +export enum OutboundMessageSendStatus { + SentToSession = 'SentToSession', + SentToTransport = 'SentToTransport', + QueuedForPickup = 'QueuedForPickup', + Undeliverable = 'Undeliverable', +} diff --git a/packages/core/src/agent/models/index.ts b/packages/core/src/agent/models/index.ts index 3a9ffdf3ca..1383036898 100644 --- a/packages/core/src/agent/models/index.ts +++ b/packages/core/src/agent/models/index.ts @@ -1,2 +1,4 @@ export * from './features' export * from './InboundMessageContext' +export * from './OutboundMessageContext' +export * from './OutboundMessageSendStatus' diff --git a/packages/core/src/error/MessageSendingError.ts b/packages/core/src/error/MessageSendingError.ts index 0a0244f4c3..6d0ddc46aa 100644 --- a/packages/core/src/error/MessageSendingError.ts +++ b/packages/core/src/error/MessageSendingError.ts @@ -1,14 +1,14 @@ -import type { OutboundDidCommV1Message, OutboundDidCommV2Message } from '../didcomm' +import type { OutboundMessageContext } from '../agent/models' import { AriesFrameworkError } from './AriesFrameworkError' export class MessageSendingError extends AriesFrameworkError { - public outboundMessage: OutboundDidCommV1Message | OutboundDidCommV2Message + public outboundMessageContext: OutboundMessageContext public constructor( message: string, - { outboundMessage, cause }: { outboundMessage: OutboundDidCommV1Message | OutboundDidCommV2Message; cause?: Error } + { outboundMessageContext, cause }: { outboundMessageContext: OutboundMessageContext; cause?: Error } ) { super(message, { cause }) - this.outboundMessage = outboundMessage + this.outboundMessageContext = outboundMessageContext } } diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index f654b9bebd..3e1204ee73 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -10,7 +10,6 @@ export { EventEmitter } from './agent/EventEmitter' export { FeatureRegistry } from './agent/FeatureRegistry' export { Handler, HandlerInboundMessage } from './agent/Handler' export * from './agent/models' -export * from './agent/helpers' export { AgentConfig } from './agent/AgentConfig' export { AgentMessage } from './agent/AgentMessage' export { Dispatcher } from './agent/Dispatcher' diff --git a/packages/core/src/modules/basic-messages/BasicMessagesApi.ts b/packages/core/src/modules/basic-messages/BasicMessagesApi.ts index e18090cfe4..17bb32d080 100644 --- a/packages/core/src/modules/basic-messages/BasicMessagesApi.ts +++ b/packages/core/src/modules/basic-messages/BasicMessagesApi.ts @@ -4,7 +4,7 @@ import type { BasicMessageRecord } from './repository/BasicMessageRecord' import { AgentContext } from '../../agent' import { Dispatcher } from '../../agent/Dispatcher' import { MessageSender } from '../../agent/MessageSender' -import { createOutboundDidCommV1Message } from '../../agent/helpers' +import { OutboundMessageContext } from '../../agent/models' import { injectable } from '../../plugins' import { ConnectionService } from '../connections' @@ -49,10 +49,13 @@ export class BasicMessagesApi { message, connection ) - const outboundMessage = createOutboundDidCommV1Message(connection, basicMessage) - outboundMessage.associatedRecord = basicMessageRecord + const outboundMessageContext = new OutboundMessageContext(basicMessage, { + agentContext: this.agentContext, + connection, + associatedRecord: basicMessageRecord, + }) - await this.messageSender.sendMessage(this.agentContext, outboundMessage) + await this.messageSender.sendMessage(outboundMessageContext) return basicMessageRecord } diff --git a/packages/core/src/modules/basic-messages/__tests__/basic-messages.e2e.test.ts b/packages/core/src/modules/basic-messages/__tests__/basic-messages.e2e.test.ts index 96117f8b91..f4b29d8631 100644 --- a/packages/core/src/modules/basic-messages/__tests__/basic-messages.e2e.test.ts +++ b/packages/core/src/modules/basic-messages/__tests__/basic-messages.e2e.test.ts @@ -95,18 +95,20 @@ describe('Basic Messages E2E', () => { if (!isDidCommV1OutboundMessage(thrownError.outboundMessage)) return testLogger.test('Error thrown includes the outbound message and recently created record id') - expect(thrownError.outboundMessage.associatedRecord).toBeInstanceOf(BasicMessageRecord) - expect(thrownError.outboundMessage.payload).toBeInstanceOf(BasicMessage) - expect((thrownError.outboundMessage.payload as BasicMessage).content).toBe('Hello undeliverable') + expect(thrownError.outboundMessageContext.associatedRecord).toBeInstanceOf(BasicMessageRecord) + expect(thrownError.outboundMessageContext.message).toBeInstanceOf(BasicMessage) + expect((thrownError.outboundMessageContext.message as BasicMessage).content).toBe('Hello undeliverable') testLogger.test('Created record can be found and deleted by id') - const storedRecord = await aliceAgent.basicMessages.getById(thrownError.outboundMessage.associatedRecord!.id) + const storedRecord = await aliceAgent.basicMessages.getById( + thrownError.outboundMessageContext.associatedRecord!.id + ) expect(storedRecord).toBeInstanceOf(BasicMessageRecord) expect(storedRecord.content).toBe('Hello undeliverable') await aliceAgent.basicMessages.deleteById(storedRecord.id) await expect( - aliceAgent.basicMessages.getById(thrownError.outboundMessage.associatedRecord!.id) + aliceAgent.basicMessages.getById(thrownError.outboundMessageContext.associatedRecord!.id) ).rejects.toThrowError(RecordNotFoundError) } spy.mockClear() diff --git a/packages/core/src/modules/connections/ConnectionsApi.ts b/packages/core/src/modules/connections/ConnectionsApi.ts index 04d8556071..a384132463 100644 --- a/packages/core/src/modules/connections/ConnectionsApi.ts +++ b/packages/core/src/modules/connections/ConnectionsApi.ts @@ -7,13 +7,13 @@ import type { Routing } from './services' import { AgentContext } from '../../agent' import { Dispatcher } from '../../agent/Dispatcher' import { MessageSender } from '../../agent/MessageSender' -import { createOutboundDidCommV1Message, createOutboundDidCommV2Message } from '../../agent/helpers' +import { OutboundMessageContext } from '../../agent/models' import { ReturnRouteTypes } from '../../decorators/transport/TransportDecorator' import { AriesFrameworkError } from '../../error' import { injectable } from '../../plugins' import { DidResolverService } from '../dids' import { DidRepository } from '../dids/repository' -import { OutOfBandService } from '../oob/protocols/v1/OutOfBandService' +import { OutOfBandService } from '../oob/OutOfBandService' import { RoutingService } from '../routing/services/RoutingService' import { ConnectionsModuleConfig } from './ConnectionsModuleConfig' @@ -25,11 +25,12 @@ import { DidExchangeCompleteHandler, DidExchangeRequestHandler, DidExchangeResponseHandler, + TrustPingMessageHandler, + TrustPingResponseMessageHandler, } from './handlers' import { HandshakeProtocol } from './models' -import { TrustPingService } from './protocols/trust-ping/v1/TrustPingService' -import { V2TrustPingService } from './protocols/trust-ping/v2/V2TrustPingService' import { ConnectionService } from './services/ConnectionService' +import { TrustPingService } from './services/TrustPingService' @injectable() export class ConnectionsApi { @@ -43,7 +44,6 @@ export class ConnectionsApi { private outOfBandService: OutOfBandService private messageSender: MessageSender private trustPingService: TrustPingService - private v2TrustPingService: V2TrustPingService private routingService: RoutingService private didRepository: DidRepository private didResolverService: DidResolverService @@ -55,7 +55,6 @@ export class ConnectionsApi { connectionService: ConnectionService, outOfBandService: OutOfBandService, trustPingService: TrustPingService, - v2TrustPingService: V2TrustPingService, routingService: RoutingService, didRepository: DidRepository, didResolverService: DidResolverService, @@ -67,7 +66,6 @@ export class ConnectionsApi { this.connectionService = connectionService this.outOfBandService = outOfBandService this.trustPingService = trustPingService - this.v2TrustPingService = v2TrustPingService this.routingService = routingService this.didRepository = didRepository this.messageSender = messageSender @@ -116,8 +114,12 @@ export class ConnectionsApi { } const { message, connectionRecord } = result - const outboundMessage = createOutboundDidCommV1Message(connectionRecord, message, outOfBandRecord) - await this.messageSender.sendMessage(this.agentContext, outboundMessage) + const outboundMessageContext = new OutboundMessageContext(message, { + agentContext: this.agentContext, + connection: connectionRecord, + outOfBand: outOfBandRecord, + }) + await this.messageSender.sendMessage(outboundMessageContext) return connectionRecord } @@ -142,24 +144,30 @@ export class ConnectionsApi { throw new AriesFrameworkError(`Out-of-band record ${connectionRecord.outOfBandId} not found.`) } - let outboundMessage + let outboundMessageContext if (connectionRecord.protocol === HandshakeProtocol.DidExchange) { const message = await this.didExchangeProtocol.createResponse( this.agentContext, connectionRecord, outOfBandRecord ) - outboundMessage = createOutboundDidCommV1Message(connectionRecord, message) + outboundMessageContext = new OutboundMessageContext(message, { + agentContext: this.agentContext, + connection: connectionRecord, + }) } else { const { message } = await this.connectionService.createResponse( this.agentContext, connectionRecord, outOfBandRecord ) - outboundMessage = createOutboundDidCommV1Message(connectionRecord, message) + outboundMessageContext = new OutboundMessageContext(message, { + agentContext: this.agentContext, + connection: connectionRecord, + }) } - await this.messageSender.sendMessage(this.agentContext, outboundMessage) + await this.messageSender.sendMessage(outboundMessageContext) return connectionRecord } @@ -173,7 +181,7 @@ export class ConnectionsApi { public async acceptResponse(connectionId: string): Promise { const connectionRecord = await this.connectionService.getById(this.agentContext, connectionId) - let outboundMessage + let outboundMessageContext if (connectionRecord.protocol === HandshakeProtocol.DidExchange) { if (!connectionRecord.outOfBandId) { throw new AriesFrameworkError(`Connection ${connectionRecord.id} does not have outOfBandId!`) @@ -192,7 +200,10 @@ export class ConnectionsApi { // Disable return routing as we don't want to receive a response for this message over the same channel // This has led to long timeouts as not all clients actually close an http socket if there is no response message message.setReturnRouting(ReturnRouteTypes.none) - outboundMessage = createOutboundDidCommV1Message(connectionRecord, message) + outboundMessageContext = new OutboundMessageContext(message, { + agentContext: this.agentContext, + connection: connectionRecord, + }) } else { const { message } = await this.connectionService.createTrustPing(this.agentContext, connectionRecord, { responseRequested: false, @@ -200,10 +211,13 @@ export class ConnectionsApi { // Disable return routing as we don't want to receive a response for this message over the same channel // This has led to long timeouts as not all clients actually close an http socket if there is no response message message.setReturnRouting(ReturnRouteTypes.none) - outboundMessage = createOutboundDidCommV1Message(connectionRecord, message) + outboundMessageContext = new OutboundMessageContext(message, { + agentContext: this.agentContext, + connection: connectionRecord, + }) } - await this.messageSender.sendMessage(this.agentContext, outboundMessage) + await this.messageSender.sendMessage(outboundMessageContext) return connectionRecord } @@ -261,21 +275,6 @@ export class ConnectionsApi { await this.connectionService.update(this.agentContext, record) } - - public async sendPing(connectionId: string) { - const connection = await this.getById(connectionId) - if (connection.isDidCommV1Connection) { - const message = await this.trustPingService.createPing() - const outboundMessage = createOutboundDidCommV1Message(connection, message) - await this.messageSender.sendMessage(this.agentContext, outboundMessage) - } - if (connection.isDidCommV2Connection) { - const message = await this.v2TrustPingService.createPing(this.agentContext, connection) - const outboundMessage = createOutboundDidCommV2Message(message) - await this.messageSender.sendMessage(this.agentContext, outboundMessage) - } - } - /** * Gets the known connection types for the record matching the given connectionId * @param connectionId @@ -366,6 +365,8 @@ export class ConnectionsApi { new ConnectionResponseHandler(this.connectionService, this.outOfBandService, this.didResolverService, this.config) ) dispatcher.registerHandler(new AckMessageHandler(this.connectionService)) + dispatcher.registerHandler(new TrustPingMessageHandler(this.trustPingService, this.connectionService)) + dispatcher.registerHandler(new TrustPingResponseMessageHandler(this.trustPingService)) dispatcher.registerHandler( new DidExchangeRequestHandler( diff --git a/packages/core/src/modules/connections/handlers/ConnectionRequestHandler.ts b/packages/core/src/modules/connections/handlers/ConnectionRequestHandler.ts index 12555467cf..77056ed0fd 100644 --- a/packages/core/src/modules/connections/handlers/ConnectionRequestHandler.ts +++ b/packages/core/src/modules/connections/handlers/ConnectionRequestHandler.ts @@ -1,11 +1,11 @@ import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' import type { DidRepository } from '../../dids/repository' -import type { OutOfBandService } from '../../oob/protocols/v1/OutOfBandService' +import type { OutOfBandService } from '../../oob/OutOfBandService' import type { RoutingService } from '../../routing/services/RoutingService' import type { ConnectionsModuleConfig } from '../ConnectionsModuleConfig' import type { ConnectionService } from '../services/ConnectionService' -import { createOutboundDidCommV1Message } from '../../../agent/helpers' +import { OutboundMessageContext } from '../../../agent/models' import { AriesFrameworkError } from '../../../error/AriesFrameworkError' import { ConnectionRequestMessage } from '../messages' @@ -69,7 +69,11 @@ export class ConnectionRequestHandler implements Handler { outOfBandRecord, routing ) - return createOutboundDidCommV1Message(connectionRecord, message, outOfBandRecord) + return new OutboundMessageContext(message, { + agentContext: messageContext.agentContext, + connection: connectionRecord, + outOfBand: outOfBandRecord, + }) } } } diff --git a/packages/core/src/modules/connections/handlers/ConnectionResponseHandler.ts b/packages/core/src/modules/connections/handlers/ConnectionResponseHandler.ts index 0497203368..28794676c6 100644 --- a/packages/core/src/modules/connections/handlers/ConnectionResponseHandler.ts +++ b/packages/core/src/modules/connections/handlers/ConnectionResponseHandler.ts @@ -1,10 +1,10 @@ import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' import type { DidResolverService } from '../../dids' -import type { OutOfBandService } from '../../oob/protocols/v1/OutOfBandService' +import type { OutOfBandService } from '../../oob/OutOfBandService' import type { ConnectionsModuleConfig } from '../ConnectionsModuleConfig' import type { ConnectionService } from '../services/ConnectionService' -import { createOutboundDidCommV1Message } from '../../../agent/helpers' +import { OutboundMessageContext } from '../../../agent/models' import { ReturnRouteTypes } from '../../../decorators/transport/TransportDecorator' import { AriesFrameworkError } from '../../../error' import { ConnectionResponseMessage } from '../messages' @@ -83,7 +83,7 @@ export class ConnectionResponseHandler implements Handler { // Disable return routing as we don't want to receive a response for this message over the same channel // This has led to long timeouts as not all clients actually close an http socket if there is no response message message.setReturnRouting(ReturnRouteTypes.none) - return createOutboundDidCommV1Message(connection, message) + return new OutboundMessageContext(message, { agentContext: messageContext.agentContext, connection }) } } } diff --git a/packages/core/src/modules/connections/handlers/DidExchangeRequestHandler.ts b/packages/core/src/modules/connections/handlers/DidExchangeRequestHandler.ts index 39e482c6ec..309d351726 100644 --- a/packages/core/src/modules/connections/handlers/DidExchangeRequestHandler.ts +++ b/packages/core/src/modules/connections/handlers/DidExchangeRequestHandler.ts @@ -1,11 +1,11 @@ import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' import type { DidRepository } from '../../dids/repository' -import type { OutOfBandService } from '../../oob/protocols/v1/OutOfBandService' +import type { OutOfBandService } from '../../oob/OutOfBandService' import type { RoutingService } from '../../routing/services/RoutingService' import type { ConnectionsModuleConfig } from '../ConnectionsModuleConfig' import type { DidExchangeProtocol } from '../DidExchangeProtocol' -import { createOutboundDidCommV1Message } from '../../../agent/helpers' +import { OutboundMessageContext } from '../../../agent/models' import { AriesFrameworkError } from '../../../error/AriesFrameworkError' import { OutOfBandState } from '../../oob/domain/OutOfBandState' import { DidExchangeRequestMessage } from '../messages' @@ -85,7 +85,11 @@ export class DidExchangeRequestHandler implements Handler { outOfBandRecord, routing ) - return createOutboundDidCommV1Message(connectionRecord, message, outOfBandRecord) + return new OutboundMessageContext(message, { + agentContext: messageContext.agentContext, + connection: connectionRecord, + outOfBand: outOfBandRecord, + }) } } } diff --git a/packages/core/src/modules/connections/handlers/DidExchangeResponseHandler.ts b/packages/core/src/modules/connections/handlers/DidExchangeResponseHandler.ts index 7eb5feaf34..3f71f85251 100644 --- a/packages/core/src/modules/connections/handlers/DidExchangeResponseHandler.ts +++ b/packages/core/src/modules/connections/handlers/DidExchangeResponseHandler.ts @@ -1,11 +1,11 @@ import type { Handler, HandlerInboundMessage } from '../../../agent/Handler' import type { DidResolverService } from '../../dids' -import type { OutOfBandService } from '../../oob/protocols/v1/OutOfBandService' +import type { OutOfBandService } from '../../oob/OutOfBandService' import type { ConnectionsModuleConfig } from '../ConnectionsModuleConfig' import type { DidExchangeProtocol } from '../DidExchangeProtocol' import type { ConnectionService } from '../services' -import { createOutboundDidCommV1Message } from '../../../agent/helpers' +import { OutboundMessageContext } from '../../../agent/models' import { ReturnRouteTypes } from '../../../decorators/transport/TransportDecorator' import { AriesFrameworkError } from '../../../error' import { OutOfBandState } from '../../oob/domain/OutOfBandState' @@ -35,13 +35,13 @@ export class DidExchangeResponseHandler implements Handler { } public async handle(messageContext: HandlerInboundMessage) { - const { recipientKey, senderKey, message } = messageContext + const { agentContext, recipientKey, senderKey, message } = messageContext if (!recipientKey || !senderKey) { - throw new AriesFrameworkError('Unable to process connection response without senderDid key or recipient key') + throw new AriesFrameworkError('Unable to process connection response without sender key or recipient key') } - const connectionRecord = await this.connectionService.getByThreadId(messageContext.agentContext, message.threadId) + const connectionRecord = await this.connectionService.getByThreadId(agentContext, message.threadId) if (!connectionRecord) { throw new AriesFrameworkError(`Connection for thread ID ${message.threadId} not found!`) } @@ -50,10 +50,7 @@ export class DidExchangeResponseHandler implements Handler { throw new AriesFrameworkError(`Connection record ${connectionRecord.id} has no 'did'`) } - const ourDidDocument = await this.didResolverService.resolveDidDocument( - messageContext.agentContext, - connectionRecord.did - ) + const ourDidDocument = await this.didResolverService.resolveDidDocument(agentContext, connectionRecord.did) if (!ourDidDocument) { throw new AriesFrameworkError(`Did document for did ${connectionRecord.did} was not resolved`) } @@ -77,10 +74,7 @@ export class DidExchangeResponseHandler implements Handler { throw new AriesFrameworkError(`Connection ${connectionRecord.id} does not have outOfBandId!`) } - const outOfBandRecord = await this.outOfBandService.findById( - messageContext.agentContext, - connectionRecord.outOfBandId - ) + const outOfBandRecord = await this.outOfBandService.findById(agentContext, connectionRecord.outOfBandId) if (!outOfBandRecord) { throw new AriesFrameworkError( @@ -91,7 +85,7 @@ export class DidExchangeResponseHandler implements Handler { // TODO // // A connection request message is the only case when I can use the connection record found - // only based on recipient key without checking that `theirKey` is equal to senderDid key. + // only based on recipient key without checking that `theirKey` is equal to sender key. // // The question is if we should do it here in this way or rather somewhere else to keep // responsibility of all handlers aligned. @@ -103,19 +97,15 @@ export class DidExchangeResponseHandler implements Handler { // In AATH we have a separate step to send the complete. So for now we'll only do it // if auto accept is enabled if (connection.autoAcceptConnection ?? this.connectionsModuleConfig.autoAcceptConnections) { - const message = await this.didExchangeProtocol.createComplete( - messageContext.agentContext, - connection, - outOfBandRecord - ) + const message = await this.didExchangeProtocol.createComplete(agentContext, connection, outOfBandRecord) // Disable return routing as we don't want to receive a response for this message over the same channel // This has led to long timeouts as not all clients actually close an http socket if there is no response message message.setReturnRouting(ReturnRouteTypes.none) if (!outOfBandRecord.reusable) { - await this.outOfBandService.updateState(messageContext.agentContext, outOfBandRecord, OutOfBandState.Done) + await this.outOfBandService.updateState(agentContext, outOfBandRecord, OutOfBandState.Done) } - return createOutboundDidCommV1Message(connection, message) + return new OutboundMessageContext(message, { agentContext, connection }) } } } diff --git a/packages/core/src/modules/connections/services/TrustPingService.ts b/packages/core/src/modules/connections/services/TrustPingService.ts new file mode 100644 index 0000000000..17032e089e --- /dev/null +++ b/packages/core/src/modules/connections/services/TrustPingService.ts @@ -0,0 +1,25 @@ +import type { InboundMessageContext } from '../../../agent/models/InboundMessageContext' +import type { TrustPingMessage } from '../messages' +import type { ConnectionRecord } from '../repository/ConnectionRecord' + +import { OutboundMessageContext } from '../../../agent/models' +import { injectable } from '../../../plugins' +import { TrustPingResponseMessage } from '../messages' + +@injectable() +export class TrustPingService { + public processPing({ message, agentContext }: InboundMessageContext, connection: ConnectionRecord) { + if (message.responseRequested) { + const response = new TrustPingResponseMessage({ + threadId: message.id, + }) + + return new OutboundMessageContext(response, { agentContext, connection }) + } + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + public processPingResponse(inboundMessage: InboundMessageContext) { + // TODO: handle ping response message + } +} diff --git a/packages/core/src/modules/credentials/CredentialsApi.ts b/packages/core/src/modules/credentials/CredentialsApi.ts index 10f418d87f..3eefc4857a 100644 --- a/packages/core/src/modules/credentials/CredentialsApi.ts +++ b/packages/core/src/modules/credentials/CredentialsApi.ts @@ -1,4 +1,4 @@ -import type { DidCommV1Message } from '../../didcomm' +import type { AgentMessage } from '../../agent/AgentMessage' import type { Query } from '../../storage/StorageService' import type { DeleteCredentialOptions } from './CredentialServiceOptions' import type { @@ -27,7 +27,7 @@ import type { CredentialService } from './services/CredentialService' import { AgentContext } from '../../agent' import { MessageSender } from '../../agent/MessageSender' -import { createOutboundDidCommV1Message } from '../../agent/helpers' +import { OutboundMessageContext } from '../../agent/models' import { InjectionSymbols } from '../../constants' import { ServiceDecorator } from '../../decorators/service/ServiceDecorator' import { AriesFrameworkError } from '../../error' @@ -69,7 +69,7 @@ export interface CredentialsApi): Promise<{ - message: DidCommV1Message + message: AgentMessage credentialRecord: CredentialExchangeRecord }> @@ -181,10 +181,14 @@ export class CredentialsApi< this.logger.debug('We have a message (sending outbound): ', message) // send the message here - const outbound = createOutboundDidCommV1Message(connection, message) + const outboundMessageContext = new OutboundMessageContext(message, { + agentContext: this.agentContext, + connection, + associatedRecord: credentialRecord, + }) this.logger.debug('In proposeCredential: Send Proposal to Issuer') - await this.messageSender.sendMessage(this.agentContext, outbound) + await this.messageSender.sendMessage(outboundMessageContext) return credentialRecord } @@ -218,8 +222,12 @@ export class CredentialsApi< // send the message const connection = await this.connectionService.getById(this.agentContext, credentialRecord.connectionId) - const outbound = createOutboundDidCommV1Message(connection, message) - await this.messageSender.sendMessage(this.agentContext, outbound) + const outboundMessageContext = new OutboundMessageContext(message, { + agentContext: this.agentContext, + connection, + associatedRecord: credentialRecord, + }) + await this.messageSender.sendMessage(outboundMessageContext) return credentialRecord } @@ -252,8 +260,12 @@ export class CredentialsApi< }) const connection = await this.connectionService.getById(this.agentContext, credentialRecord.connectionId) - const outboundMessage = createOutboundDidCommV1Message(connection, message) - await this.messageSender.sendMessage(this.agentContext, outboundMessage) + const outboundMessageContext = new OutboundMessageContext(message, { + agentContext: this.agentContext, + connection, + associatedRecord: credentialRecord, + }) + await this.messageSender.sendMessage(outboundMessageContext) return credentialRecord } @@ -279,8 +291,12 @@ export class CredentialsApi< }) this.logger.debug('Offer Message successfully created; message= ', message) - const outboundMessage = createOutboundDidCommV1Message(connection, message) - await this.messageSender.sendMessage(this.agentContext, outboundMessage) + const outboundMessageContext = new OutboundMessageContext(message, { + agentContext: this.agentContext, + connection, + associatedRecord: credentialRecord, + }) + await this.messageSender.sendMessage(outboundMessageContext) return credentialRecord } @@ -311,8 +327,12 @@ export class CredentialsApi< autoAcceptCredential: options.autoAcceptCredential, }) - const outboundMessage = createOutboundDidCommV1Message(connection, message) - await this.messageSender.sendMessage(this.agentContext, outboundMessage) + const outboundMessageContext = new OutboundMessageContext(message, { + agentContext: this.agentContext, + connection, + associatedRecord: credentialRecord, + }) + await this.messageSender.sendMessage(outboundMessageContext) return credentialRecord } @@ -342,12 +362,16 @@ export class CredentialsApi< associatedRecordId: credentialRecord.id, }) - await this.messageSender.sendMessageToService(this.agentContext, { - message, - service: recipientService.resolvedDidCommService, - senderKey: ourService.resolvedDidCommService.recipientKeys[0], - returnRoute: true, - }) + await this.messageSender.sendMessageToService( + new OutboundMessageContext(message, { + agentContext: this.agentContext, + serviceParams: { + service: recipientService.resolvedDidCommService, + senderKey: ourService.resolvedDidCommService.recipientKeys[0], + returnRoute: true, + }, + }) + ) return credentialRecord } @@ -388,8 +412,12 @@ export class CredentialsApi< } const connection = await this.connectionService.getById(this.agentContext, credentialRecord.connectionId) - const outboundMessage = createOutboundDidCommV1Message(connection, message) - await this.messageSender.sendMessage(this.agentContext, outboundMessage) + const outboundMessageContext = new OutboundMessageContext(message, { + agentContext: this.agentContext, + connection, + associatedRecord: credentialRecord, + }) + await this.messageSender.sendMessage(outboundMessageContext) return credentialRecord } @@ -401,7 +429,7 @@ export class CredentialsApi< * @returns The credential record and credential offer message */ public async createOffer(options: CreateOfferOptions): Promise<{ - message: DidCommV1Message + message: AgentMessage credentialRecord: CredentialExchangeRecord }> { const service = this.getService(options.protocolVersion) @@ -447,8 +475,12 @@ export class CredentialsApi< // Use connection if present if (credentialRecord.connectionId) { const connection = await this.connectionService.getById(this.agentContext, credentialRecord.connectionId) - const outboundMessage = createOutboundDidCommV1Message(connection, message) - await this.messageSender.sendMessage(this.agentContext, outboundMessage) + const outboundMessageContext = new OutboundMessageContext(message, { + agentContext: this.agentContext, + connection, + associatedRecord: credentialRecord, + }) + await this.messageSender.sendMessage(outboundMessageContext) return credentialRecord } @@ -464,12 +496,16 @@ export class CredentialsApi< associatedRecordId: credentialRecord.id, }) - await this.messageSender.sendMessageToService(this.agentContext, { - message, - service: recipientService.resolvedDidCommService, - senderKey: ourService.resolvedDidCommService.recipientKeys[0], - returnRoute: true, - }) + await this.messageSender.sendMessageToService( + new OutboundMessageContext(message, { + agentContext: this.agentContext, + serviceParams: { + service: recipientService.resolvedDidCommService, + senderKey: ourService.resolvedDidCommService.recipientKeys[0], + returnRoute: true, + }, + }) + ) return credentialRecord } @@ -506,9 +542,13 @@ export class CredentialsApi< if (credentialRecord.connectionId) { const connection = await this.connectionService.getById(this.agentContext, credentialRecord.connectionId) - const outboundMessage = createOutboundDidCommV1Message(connection, message) + const outboundMessageContext = new OutboundMessageContext(message, { + agentContext: this.agentContext, + connection, + associatedRecord: credentialRecord, + }) - await this.messageSender.sendMessage(this.agentContext, outboundMessage) + await this.messageSender.sendMessage(outboundMessageContext) return credentialRecord } @@ -517,12 +557,16 @@ export class CredentialsApi< const recipientService = credentialMessage.service const ourService = requestMessage.service - await this.messageSender.sendMessageToService(this.agentContext, { - message, - service: recipientService.resolvedDidCommService, - senderKey: ourService.resolvedDidCommService.recipientKeys[0], - returnRoute: true, - }) + await this.messageSender.sendMessageToService( + new OutboundMessageContext(message, { + agentContext: this.agentContext, + serviceParams: { + service: recipientService.resolvedDidCommService, + senderKey: ourService.resolvedDidCommService.recipientKeys[0], + returnRoute: true, + }, + }) + ) return credentialRecord } @@ -552,8 +596,12 @@ export class CredentialsApi< problemReportMessage.setThread({ threadId: credentialRecord.threadId, }) - const outboundMessage = createOutboundDidCommV1Message(connection, problemReportMessage) - await this.messageSender.sendMessage(this.agentContext, outboundMessage) + const outboundMessageContext = new OutboundMessageContext(problemReportMessage, { + agentContext: this.agentContext, + connection, + associatedRecord: credentialRecord, + }) + await this.messageSender.sendMessage(outboundMessageContext) return credentialRecord } diff --git a/packages/core/src/modules/credentials/protocol/v1/handlers/V1IssueCredentialHandler.ts b/packages/core/src/modules/credentials/protocol/v1/handlers/V1IssueCredentialHandler.ts index 252ad843e2..e30f82e101 100644 --- a/packages/core/src/modules/credentials/protocol/v1/handlers/V1IssueCredentialHandler.ts +++ b/packages/core/src/modules/credentials/protocol/v1/handlers/V1IssueCredentialHandler.ts @@ -4,7 +4,7 @@ import type { DidCommMessageRepository } from '../../../../../storage' import type { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' import type { V1CredentialService } from '../V1CredentialService' -import { createOutboundDidCommV1Message, createOutboundServiceMessage } from '../../../../../agent/helpers' +import { OutboundMessageContext } from '../../../../../agent/models' import { V1IssueCredentialMessage, V1RequestCredentialMessage } from '../messages' export class V1IssueCredentialHandler implements Handler { @@ -51,15 +51,21 @@ export class V1IssueCredentialHandler implements Handler { }) if (messageContext.connection) { - return createOutboundDidCommV1Message(messageContext.connection, message) + return new OutboundMessageContext(message, { + agentContext: messageContext.agentContext, + connection: messageContext.connection, + associatedRecord: credentialRecord, + }) } else if (messageContext.message.service && requestMessage.service) { const recipientService = messageContext.message.service const ourService = requestMessage.service - return createOutboundServiceMessage({ - payload: message, - service: recipientService.resolvedDidCommService, - senderKey: ourService.resolvedDidCommService.recipientKeys[0], + return new OutboundMessageContext(message, { + agentContext: messageContext.agentContext, + serviceParams: { + service: recipientService.resolvedDidCommService, + senderKey: ourService.resolvedDidCommService.recipientKeys[0], + }, }) } diff --git a/packages/core/src/modules/credentials/protocol/v1/handlers/V1OfferCredentialHandler.ts b/packages/core/src/modules/credentials/protocol/v1/handlers/V1OfferCredentialHandler.ts index 0702dfd7d7..bb85fd199a 100644 --- a/packages/core/src/modules/credentials/protocol/v1/handlers/V1OfferCredentialHandler.ts +++ b/packages/core/src/modules/credentials/protocol/v1/handlers/V1OfferCredentialHandler.ts @@ -5,7 +5,7 @@ import type { RoutingService } from '../../../../routing/services/RoutingService import type { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' import type { V1CredentialService } from '../V1CredentialService' -import { createOutboundDidCommV1Message, createOutboundServiceMessage } from '../../../../../agent/helpers' +import { OutboundMessageContext } from '../../../../../agent/models' import { ServiceDecorator } from '../../../../../decorators/service/ServiceDecorator' import { DidCommMessageRole } from '../../../../../storage' import { V1OfferCredentialMessage } from '../messages' @@ -50,7 +50,11 @@ export class V1OfferCredentialHandler implements Handler { if (messageContext.connection) { const { message } = await this.credentialService.acceptOffer(messageContext.agentContext, { credentialRecord }) - return createOutboundDidCommV1Message(messageContext.connection, message) + return new OutboundMessageContext(message, { + agentContext: messageContext.agentContext, + connection: messageContext.connection, + associatedRecord: credentialRecord, + }) } else if (messageContext.message.service) { const routing = await this.routingService.getRouting(messageContext.agentContext) const ourService = new ServiceDecorator({ @@ -77,10 +81,12 @@ export class V1OfferCredentialHandler implements Handler { associatedRecordId: credentialRecord.id, }) - return createOutboundServiceMessage({ - payload: message, - service: recipientService.resolvedDidCommService, - senderKey: ourService.resolvedDidCommService.recipientKeys[0], + return new OutboundMessageContext(message, { + agentContext: messageContext.agentContext, + serviceParams: { + service: recipientService.resolvedDidCommService, + senderKey: ourService.resolvedDidCommService.recipientKeys[0], + }, }) } diff --git a/packages/core/src/modules/credentials/protocol/v1/handlers/V1ProposeCredentialHandler.ts b/packages/core/src/modules/credentials/protocol/v1/handlers/V1ProposeCredentialHandler.ts index de8616eca3..f427a97670 100644 --- a/packages/core/src/modules/credentials/protocol/v1/handlers/V1ProposeCredentialHandler.ts +++ b/packages/core/src/modules/credentials/protocol/v1/handlers/V1ProposeCredentialHandler.ts @@ -3,7 +3,7 @@ import type { Logger } from '../../../../../logger' import type { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' import type { V1CredentialService } from '../V1CredentialService' -import { createOutboundDidCommV1Message } from '../../../../../agent/helpers' +import { OutboundMessageContext } from '../../../../../agent/models' import { V1ProposeCredentialMessage } from '../messages' export class V1ProposeCredentialHandler implements Handler { @@ -47,6 +47,10 @@ export class V1ProposeCredentialHandler implements Handler { credentialRecord, }) - return createOutboundDidCommV1Message(messageContext.connection, message) + return new OutboundMessageContext(message, { + agentContext: messageContext.agentContext, + connection: messageContext.connection, + associatedRecord: credentialRecord, + }) } } diff --git a/packages/core/src/modules/credentials/protocol/v1/handlers/V1RequestCredentialHandler.ts b/packages/core/src/modules/credentials/protocol/v1/handlers/V1RequestCredentialHandler.ts index 605abdff44..ba85e05ae8 100644 --- a/packages/core/src/modules/credentials/protocol/v1/handlers/V1RequestCredentialHandler.ts +++ b/packages/core/src/modules/credentials/protocol/v1/handlers/V1RequestCredentialHandler.ts @@ -4,7 +4,7 @@ import type { DidCommMessageRepository } from '../../../../../storage' import type { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' import type { V1CredentialService } from '../V1CredentialService' -import { createOutboundDidCommV1Message, createOutboundServiceMessage } from '../../../../../agent/helpers' +import { OutboundMessageContext } from '../../../../../agent/models' import { DidCommMessageRole } from '../../../../../storage' import { V1RequestCredentialMessage } from '../messages' @@ -50,7 +50,11 @@ export class V1RequestCredentialHandler implements Handler { }) if (messageContext.connection) { - return createOutboundDidCommV1Message(messageContext.connection, message) + return new OutboundMessageContext(message, { + agentContext: messageContext.agentContext, + connection: messageContext.connection, + associatedRecord: credentialRecord, + }) } else if (messageContext.message.service && offerMessage?.service) { const recipientService = messageContext.message.service const ourService = offerMessage.service @@ -64,10 +68,12 @@ export class V1RequestCredentialHandler implements Handler { associatedRecordId: credentialRecord.id, }) - return createOutboundServiceMessage({ - payload: message, - service: recipientService.resolvedDidCommService, - senderKey: ourService.resolvedDidCommService.recipientKeys[0], + return new OutboundMessageContext(message, { + agentContext: messageContext.agentContext, + serviceParams: { + service: recipientService.resolvedDidCommService, + senderKey: ourService.resolvedDidCommService.recipientKeys[0], + }, }) } diff --git a/packages/core/src/modules/credentials/protocol/v2/handlers/V2IssueCredentialHandler.ts b/packages/core/src/modules/credentials/protocol/v2/handlers/V2IssueCredentialHandler.ts index 00c2df2425..308be12bc1 100644 --- a/packages/core/src/modules/credentials/protocol/v2/handlers/V2IssueCredentialHandler.ts +++ b/packages/core/src/modules/credentials/protocol/v2/handlers/V2IssueCredentialHandler.ts @@ -5,7 +5,7 @@ import type { DidCommMessageRepository } from '../../../../../storage' import type { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' import type { V2CredentialService } from '../V2CredentialService' -import { createOutboundDidCommV1Message, createOutboundServiceMessage } from '../../../../../agent/helpers' +import { OutboundMessageContext } from '../../../../../agent/models' import { V2IssueCredentialMessage } from '../messages/V2IssueCredentialMessage' import { V2RequestCredentialMessage } from '../messages/V2RequestCredentialMessage' @@ -54,15 +54,21 @@ export class V2IssueCredentialHandler implements Handler { credentialRecord, }) if (messageContext.connection) { - return createOutboundDidCommV1Message(messageContext.connection, message) + return new OutboundMessageContext(message, { + agentContext: messageContext.agentContext, + connection: messageContext.connection, + associatedRecord: credentialRecord, + }) } else if (requestMessage?.service && messageContext.message.service) { const recipientService = messageContext.message.service const ourService = requestMessage.service - return createOutboundServiceMessage({ - payload: message, - service: recipientService.resolvedDidCommService, - senderKey: ourService.resolvedDidCommService.recipientKeys[0], + return new OutboundMessageContext(message, { + agentContext: messageContext.agentContext, + serviceParams: { + service: recipientService.resolvedDidCommService, + senderKey: ourService.resolvedDidCommService.recipientKeys[0], + }, }) } diff --git a/packages/core/src/modules/credentials/protocol/v2/handlers/V2OfferCredentialHandler.ts b/packages/core/src/modules/credentials/protocol/v2/handlers/V2OfferCredentialHandler.ts index 0553d5a6e9..d1d4248661 100644 --- a/packages/core/src/modules/credentials/protocol/v2/handlers/V2OfferCredentialHandler.ts +++ b/packages/core/src/modules/credentials/protocol/v2/handlers/V2OfferCredentialHandler.ts @@ -6,7 +6,7 @@ import type { RoutingService } from '../../../../routing/services/RoutingService import type { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' import type { V2CredentialService } from '../V2CredentialService' -import { createOutboundDidCommV1Message, createOutboundServiceMessage } from '../../../../../agent/helpers' +import { OutboundMessageContext } from '../../../../../agent/models' import { ServiceDecorator } from '../../../../../decorators/service/ServiceDecorator' import { DidCommMessageRole } from '../../../../../storage' import { V2OfferCredentialMessage } from '../messages/V2OfferCredentialMessage' @@ -54,7 +54,11 @@ export class V2OfferCredentialHandler implements Handler { const { message } = await this.credentialService.acceptOffer(messageContext.agentContext, { credentialRecord, }) - return createOutboundDidCommV1Message(messageContext.connection, message) + return new OutboundMessageContext(message, { + agentContext: messageContext.agentContext, + connection: messageContext.connection, + associatedRecord: credentialRecord, + }) } else if (offerMessage?.service) { const routing = await this.routingService.getRouting(messageContext.agentContext) const ourService = new ServiceDecorator({ @@ -77,10 +81,12 @@ export class V2OfferCredentialHandler implements Handler { associatedRecordId: credentialRecord.id, }) - return createOutboundServiceMessage({ - payload: message, - service: recipientService.resolvedDidCommService, - senderKey: ourService.resolvedDidCommService.recipientKeys[0], + return new OutboundMessageContext(message, { + agentContext: messageContext.agentContext, + serviceParams: { + service: recipientService.resolvedDidCommService, + senderKey: ourService.resolvedDidCommService.recipientKeys[0], + }, }) } diff --git a/packages/core/src/modules/credentials/protocol/v2/handlers/V2ProposeCredentialHandler.ts b/packages/core/src/modules/credentials/protocol/v2/handlers/V2ProposeCredentialHandler.ts index 76774296c7..d536303a33 100644 --- a/packages/core/src/modules/credentials/protocol/v2/handlers/V2ProposeCredentialHandler.ts +++ b/packages/core/src/modules/credentials/protocol/v2/handlers/V2ProposeCredentialHandler.ts @@ -4,7 +4,7 @@ import type { Logger } from '../../../../../logger' import type { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' import type { V2CredentialService } from '../V2CredentialService' -import { createOutboundDidCommV1Message } from '../../../../../agent/helpers' +import { OutboundMessageContext } from '../../../../../agent/models' import { V2ProposeCredentialMessage } from '../messages/V2ProposeCredentialMessage' export class V2ProposeCredentialHandler implements Handler { @@ -44,6 +44,10 @@ export class V2ProposeCredentialHandler implements Handler { const { message } = await this.credentialService.acceptProposal(messageContext.agentContext, { credentialRecord }) - return createOutboundDidCommV1Message(messageContext.connection, message) + return new OutboundMessageContext(message, { + agentContext: messageContext.agentContext, + connection: messageContext.connection, + associatedRecord: credentialRecord, + }) } } diff --git a/packages/core/src/modules/credentials/protocol/v2/handlers/V2RequestCredentialHandler.ts b/packages/core/src/modules/credentials/protocol/v2/handlers/V2RequestCredentialHandler.ts index ec2e5f1b09..72b436174d 100644 --- a/packages/core/src/modules/credentials/protocol/v2/handlers/V2RequestCredentialHandler.ts +++ b/packages/core/src/modules/credentials/protocol/v2/handlers/V2RequestCredentialHandler.ts @@ -5,7 +5,7 @@ import type { DidCommMessageRepository } from '../../../../../storage' import type { CredentialExchangeRecord } from '../../../repository' import type { V2CredentialService } from '../V2CredentialService' -import { createOutboundDidCommV1Message, createOutboundServiceMessage } from '../../../../../agent/helpers' +import { OutboundMessageContext } from '../../../../../agent/models' import { DidCommMessageRole } from '../../../../../storage' import { V2OfferCredentialMessage } from '../messages/V2OfferCredentialMessage' import { V2RequestCredentialMessage } from '../messages/V2RequestCredentialMessage' @@ -56,7 +56,11 @@ export class V2RequestCredentialHandler implements Handler { }) if (messageContext.connection) { - return createOutboundDidCommV1Message(messageContext.connection, message) + return new OutboundMessageContext(message, { + agentContext: messageContext.agentContext, + connection: messageContext.connection, + associatedRecord: credentialRecord, + }) } else if (messageContext.message.service && offerMessage?.service) { const recipientService = messageContext.message.service const ourService = offerMessage.service @@ -69,10 +73,12 @@ export class V2RequestCredentialHandler implements Handler { role: DidCommMessageRole.Sender, }) - return createOutboundServiceMessage({ - payload: message, - service: recipientService.resolvedDidCommService, - senderKey: ourService.resolvedDidCommService.recipientKeys[0], + return new OutboundMessageContext(message, { + agentContext: messageContext.agentContext, + serviceParams: { + service: recipientService.resolvedDidCommService, + senderKey: ourService.resolvedDidCommService.recipientKeys[0], + }, }) } diff --git a/packages/core/src/modules/discover-features/DiscoverFeaturesApi.ts b/packages/core/src/modules/discover-features/DiscoverFeaturesApi.ts index caa710e163..94e376f08d 100644 --- a/packages/core/src/modules/discover-features/DiscoverFeaturesApi.ts +++ b/packages/core/src/modules/discover-features/DiscoverFeaturesApi.ts @@ -13,7 +13,7 @@ import { catchError, filter, map, takeUntil, timeout } from 'rxjs/operators' import { AgentContext } from '../../agent' import { EventEmitter } from '../../agent/EventEmitter' import { MessageSender } from '../../agent/MessageSender' -import { createOutboundDidCommV1Message } from '../../agent/helpers' +import { OutboundMessageContext } from '../../agent/models' import { InjectionSymbols } from '../../constants' import { AriesFrameworkError } from '../../error' import { inject, injectable } from '../../plugins' @@ -103,7 +103,10 @@ export class DiscoverFeaturesApi< comment: options.comment, }) - const outbound = createOutboundDidCommV1Message(connection, queryMessage) + const outboundMessageContext = new OutboundMessageContext(queryMessage, { + agentContext: this.agentContext, + connection, + }) const replaySubject = new ReplaySubject(1) if (options.awaitDisclosures) { @@ -125,7 +128,7 @@ export class DiscoverFeaturesApi< .subscribe(replaySubject) } - await this.messageSender.sendMessage(this.agentContext, outbound) + await this.messageSender.sendMessage(outboundMessageContext) return { features: options.awaitDisclosures ? await firstValueFrom(replaySubject) : undefined } } @@ -151,7 +154,10 @@ export class DiscoverFeaturesApi< threadId: options.threadId, }) - const outbound = createOutboundDidCommV1Message(connection, disclosuresMessage) - await this.messageSender.sendMessage(this.agentContext, outbound) + const outboundMessageContext = new OutboundMessageContext(disclosuresMessage, { + agentContext: this.agentContext, + connection, + }) + await this.messageSender.sendMessage(outboundMessageContext) } } diff --git a/packages/core/src/modules/discover-features/protocol/v1/handlers/V1QueryMessageHandler.ts b/packages/core/src/modules/discover-features/protocol/v1/handlers/V1QueryMessageHandler.ts index 403bbcb727..9a5bacf74c 100644 --- a/packages/core/src/modules/discover-features/protocol/v1/handlers/V1QueryMessageHandler.ts +++ b/packages/core/src/modules/discover-features/protocol/v1/handlers/V1QueryMessageHandler.ts @@ -1,7 +1,7 @@ import type { Handler, HandlerInboundMessage } from '../../../../../agent/Handler' import type { V1DiscoverFeaturesService } from '../V1DiscoverFeaturesService' -import { createOutboundDidCommV1Message } from '../../../../../agent/helpers' +import { OutboundMessageContext } from '../../../../../agent/models' import { V1QueryMessage } from '../messages' export class V1QueryMessageHandler implements Handler { @@ -18,7 +18,10 @@ export class V1QueryMessageHandler implements Handler { const discloseMessage = await this.discoverFeaturesService.processQuery(inboundMessage) if (discloseMessage) { - return createOutboundDidCommV1Message(connection, discloseMessage.message) + return new OutboundMessageContext(discloseMessage.message, { + agentContext: inboundMessage.agentContext, + connection, + }) } } } diff --git a/packages/core/src/modules/discover-features/protocol/v2/handlers/V2QueriesMessageHandler.ts b/packages/core/src/modules/discover-features/protocol/v2/handlers/V2QueriesMessageHandler.ts index 3a76d052dd..8664dd2240 100644 --- a/packages/core/src/modules/discover-features/protocol/v2/handlers/V2QueriesMessageHandler.ts +++ b/packages/core/src/modules/discover-features/protocol/v2/handlers/V2QueriesMessageHandler.ts @@ -1,7 +1,7 @@ import type { Handler, HandlerInboundMessage } from '../../../../../agent/Handler' import type { V2DiscoverFeaturesService } from '../V2DiscoverFeaturesService' -import { createOutboundDidCommV1Message } from '../../../../../agent/helpers' +import { OutboundMessageContext } from '../../../../../agent/models' import { V2QueriesMessage } from '../messages' export class V2QueriesMessageHandler implements Handler { @@ -18,7 +18,10 @@ export class V2QueriesMessageHandler implements Handler { const discloseMessage = await this.discoverFeaturesService.processQuery(inboundMessage) if (discloseMessage) { - return createOutboundDidCommV1Message(connection, discloseMessage.message) + return new OutboundMessageContext(discloseMessage.message, { + agentContext: inboundMessage.agentContext, + connection, + }) } } } diff --git a/packages/core/src/modules/oob/OutOfBandApi.ts b/packages/core/src/modules/oob/OutOfBandApi.ts index cef0b1aae1..f66ad984d3 100644 --- a/packages/core/src/modules/oob/OutOfBandApi.ts +++ b/packages/core/src/modules/oob/OutOfBandApi.ts @@ -1,8 +1,9 @@ +import type { AgentMessage } from '../../agent/AgentMessage' import type { AgentMessageReceivedEvent } from '../../agent/Events' import type { Key } from '../../crypto' -import type { Attachment } from '../../decorators/attachment/v1/Attachment' -import type { DidCommV1Message, PlaintextMessage } from '../../didcomm' +import type { Attachment } from '../../decorators/attachment/Attachment' import type { Query } from '../../storage/StorageService' +import type { PlaintextMessage } from '../../types' import type { ConnectionInvitationMessage, ConnectionRecord, Routing } from '../connections' import type { HandshakeReusedEvent } from './domain/OutOfBandEvents' @@ -13,10 +14,9 @@ import { Dispatcher } from '../../agent/Dispatcher' import { EventEmitter } from '../../agent/EventEmitter' import { filterContextCorrelationId, AgentEventTypes } from '../../agent/Events' import { MessageSender } from '../../agent/MessageSender' -import { createOutboundDidCommV1Message } from '../../agent/helpers' +import { OutboundMessageContext } from '../../agent/models' import { InjectionSymbols } from '../../constants' import { ServiceDecorator } from '../../decorators/service/ServiceDecorator' -import { getPlaintextMessageType } from '../../didcomm' import { AriesFrameworkError } from '../../error' import { Logger } from '../../logger' import { inject, injectable } from '../../plugins' @@ -30,23 +30,20 @@ import { DidKey } from '../dids' import { didKeyToVerkey } from '../dids/helpers' import { RoutingService } from '../routing/services/RoutingService' +import { OutOfBandService } from './OutOfBandService' import { OutOfBandDidCommService } from './domain/OutOfBandDidCommService' import { OutOfBandEventTypes } from './domain/OutOfBandEvents' import { OutOfBandRole } from './domain/OutOfBandRole' import { OutOfBandState } from './domain/OutOfBandState' +import { HandshakeReuseHandler } from './handlers' +import { HandshakeReuseAcceptedHandler } from './handlers/HandshakeReuseAcceptedHandler' import { convertToNewInvitation, convertToOldInvitation } from './helpers' -import { OutOfBandService } from './protocols/v1/OutOfBandService' -import { HandshakeReuseHandler } from './protocols/v1/handlers' -import { HandshakeReuseAcceptedHandler } from './protocols/v1/handlers/HandshakeReuseAcceptedHandler' -import { OutOfBandInvitation } from './protocols/v1/messages' -import { V2OutOfBandService } from './protocols/v2/V2OutOfBandService' -import { OutOfBandInvitation as V2OutOfBandInvitation } from './protocols/v2/messages' +import { OutOfBandInvitation } from './messages' import { OutOfBandRecord } from './repository/OutOfBandRecord' const didCommProfiles = ['didcomm/aip1', 'didcomm/aip2;env=rfc19'] export interface CreateOutOfBandInvitationConfig { - version?: 'v1' | 'v2' label?: string alias?: string // alias for a connection record to be created imageUrl?: string @@ -54,7 +51,7 @@ export interface CreateOutOfBandInvitationConfig { goal?: string handshake?: boolean handshakeProtocols?: HandshakeProtocol[] - messages?: DidCommV1Message[] + messages?: AgentMessage[] multiUseInvitation?: boolean autoAcceptConnection?: boolean routing?: Routing @@ -83,7 +80,6 @@ export interface ReceiveOutOfBandInvitationConfig { @injectable() export class OutOfBandApi { private outOfBandService: OutOfBandService - private v2OutOfBandService: V2OutOfBandService private routingService: RoutingService private connectionsApi: ConnectionsApi private didCommMessageRepository: DidCommMessageRepository @@ -98,7 +94,6 @@ export class OutOfBandApi { dispatcher: Dispatcher, didCommDocumentService: DidCommDocumentService, outOfBandService: OutOfBandService, - v2OutOfBandService: V2OutOfBandService, routingService: RoutingService, connectionsApi: ConnectionsApi, didCommMessageRepository: DidCommMessageRepository, @@ -112,7 +107,6 @@ export class OutOfBandApi { this.agentContext = agentContext this.logger = logger this.outOfBandService = outOfBandService - this.v2OutOfBandService = v2OutOfBandService this.routingService = routingService this.connectionsApi = connectionsApi this.didCommMessageRepository = didCommMessageRepository @@ -131,21 +125,12 @@ export class OutOfBandApi { * * If `config` parameter contains `messages` it adds them to `requests~attach` attribute. * - * Agent role: senderDid (inviter) + * Agent role: sender (inviter) * * @param config configuration of how out-of-band invitation should be created * @returns out-of-band record */ - public async createInvitation(config: CreateOutOfBandInvitationConfig = {}): Promise<{ - outOfBandInvitation: OutOfBandInvitation | V2OutOfBandInvitation - outOfBandRecord?: OutOfBandRecord - }> { - if (config.version === 'v2') { - const outOfBandInvitation = await this.v2OutOfBandService.createInvitation(this.agentContext) - // TODO: create and store record? - return { outOfBandInvitation } - } - + public async createInvitation(config: CreateOutOfBandInvitationConfig = {}): Promise { const multiUseInvitation = config.multiUseInvitation ?? false const handshake = config.handshake ?? true const customHandshakeProtocols = config.handshakeProtocols @@ -236,7 +221,7 @@ export class OutOfBandApi { await this.outOfBandService.save(this.agentContext, outOfBandRecord) this.outOfBandService.emitStateChangedEvent(this.agentContext, outOfBandRecord, null) - return { outOfBandInvitation, outOfBandRecord: outOfBandRecord } + return outOfBandRecord } /** @@ -244,23 +229,20 @@ export class OutOfBandApi { * but it also converts out-of-band invitation message to an "legacy" invitation message defined * in RFC 0160: Connection Protocol and returns it together with out-of-band record. * - * Agent role: senderDid (inviter) + * Agent role: sender (inviter) * * @param config configuration of how a connection invitation should be created * @returns out-of-band record and connection invitation */ public async createLegacyInvitation(config: CreateLegacyInvitationConfig = {}) { - const { outOfBandRecord: outOfBandRecord } = await this.createInvitation({ + const outOfBandRecord = await this.createInvitation({ ...config, handshakeProtocols: [HandshakeProtocol.Connections], }) - if (!outOfBandRecord) { - throw new AriesFrameworkError('Unable to create Out-of-Band invitation.') - } return { outOfBandRecord, invitation: convertToOldInvitation(outOfBandRecord.outOfBandInvitation) } } - public async createLegacyConnectionlessInvitation(config: { + public async createLegacyConnectionlessInvitation(config: { recordId: string message: Message domain: string @@ -300,6 +282,7 @@ export class OutOfBandApi { */ public async receiveInvitationFromUrl(invitationUrl: string, config: ReceiveOutOfBandInvitationConfig = {}) { const message = await this.parseInvitationShortUrl(invitationUrl) + return this.receiveInvitation(message, config) } @@ -310,7 +293,7 @@ export class OutOfBandApi { * * @returns OutOfBandInvitation */ - public parseInvitation(invitationUrl: string): OutOfBandInvitation | V2OutOfBandInvitation { + public parseInvitation(invitationUrl: string): OutOfBandInvitation { return parseInvitationUrl(invitationUrl) } @@ -322,7 +305,7 @@ export class OutOfBandApi { * * @returns OutOfBandInvitation */ - public async parseInvitationShortUrl(invitation: string): Promise { + public async parseInvitationShortUrl(invitation: string): Promise { return await parseInvitationShortUrl(invitation, this.agentContext.config.agentDependencies) } @@ -344,26 +327,22 @@ export class OutOfBandApi { * @returns out-of-band record and connection record if one has been created. */ public async receiveInvitation( - invitation: OutOfBandInvitation | ConnectionInvitationMessage | V2OutOfBandInvitation, + invitation: OutOfBandInvitation | ConnectionInvitationMessage, config: ReceiveOutOfBandInvitationConfig = {} - ): Promise<{ outOfBandRecord?: OutOfBandRecord; connectionRecord?: ConnectionRecord }> { + ): Promise<{ outOfBandRecord: OutOfBandRecord; connectionRecord?: ConnectionRecord }> { + // Convert to out of band invitation if needed + const outOfBandInvitation = + invitation instanceof OutOfBandInvitation ? invitation : convertToNewInvitation(invitation) + + const { handshakeProtocols } = outOfBandInvitation + const { routing } = config + const autoAcceptInvitation = config.autoAcceptInvitation ?? true const autoAcceptConnection = config.autoAcceptConnection ?? true const reuseConnection = config.reuseConnection ?? false const label = config.label ?? this.agentContext.config.label const alias = config.alias const imageUrl = config.imageUrl ?? this.agentContext.config.connectionImageUrl - const { routing } = config - - if (invitation instanceof V2OutOfBandInvitation) { - return this.v2OutOfBandService.acceptInvitation(this.agentContext, invitation) - } - - // Convert to out of band invitation if needed - const outOfBandInvitation = - invitation instanceof OutOfBandInvitation ? invitation : convertToNewInvitation(invitation) - - const { handshakeProtocols } = outOfBandInvitation const messages = outOfBandInvitation.getRequests() @@ -441,7 +420,7 @@ export class OutOfBandApi { */ public async acceptInvitation( outOfBandId: string, - config?: { + config: { autoAcceptConnection?: boolean reuseConnection?: boolean label?: string @@ -454,7 +433,7 @@ export class OutOfBandApi { const outOfBandRecord = await this.outOfBandService.getById(this.agentContext, outOfBandId) const { outOfBandInvitation } = outOfBandRecord - const { label, alias, imageUrl, autoAcceptConnection, reuseConnection, routing } = config || {} + const { label, alias, imageUrl, autoAcceptConnection, reuseConnection, routing } = config const { handshakeProtocols } = outOfBandInvitation const services = outOfBandInvitation.getServices() const messages = outOfBandInvitation.getRequests() @@ -664,7 +643,7 @@ export class OutOfBandApi { private async emitWithConnection(connectionRecord: ConnectionRecord, messages: PlaintextMessage[]) { const supportedMessageTypes = this.dispatcher.supportedMessageTypes const plaintextMessage = messages.find((message) => { - const parsedMessageType = parseMessageType(getPlaintextMessageType(message)) + const parsedMessageType = parseMessageType(message['@type']) return supportedMessageTypes.find((type) => supportsIncomingMessageType(parsedMessageType, type)) }) @@ -691,7 +670,7 @@ export class OutOfBandApi { const supportedMessageTypes = this.dispatcher.supportedMessageTypes const plaintextMessage = messages.find((message) => { - const parsedMessageType = parseMessageType(getPlaintextMessageType(message)) + const parsedMessageType = parseMessageType(message['@type']) return supportedMessageTypes.find((type) => supportsIncomingMessageType(parsedMessageType, type)) }) @@ -769,8 +748,11 @@ export class OutOfBandApi { ) ) - const outbound = createOutboundDidCommV1Message(connectionRecord, reuseMessage) - await this.messageSender.sendMessage(this.agentContext, outbound) + const outboundMessageContext = new OutboundMessageContext(reuseMessage, { + agentContext: this.agentContext, + connection: connectionRecord, + }) + await this.messageSender.sendMessage(outboundMessageContext) return reuseAcceptedEventPromise } diff --git a/packages/core/src/modules/oob/protocols/v1/handlers/HandshakeReuseHandler.ts b/packages/core/src/modules/oob/protocols/v1/handlers/HandshakeReuseHandler.ts index aff7a273a2..43ec159d2e 100644 --- a/packages/core/src/modules/oob/protocols/v1/handlers/HandshakeReuseHandler.ts +++ b/packages/core/src/modules/oob/protocols/v1/handlers/HandshakeReuseHandler.ts @@ -1,8 +1,8 @@ -import type { Handler } from '../../../../../agent/Handler' -import type { InboundMessageContext } from '../../../../../agent/models/InboundMessageContext' +import type { Handler } from '../../../agent/Handler' +import type { InboundMessageContext } from '../../../agent/models/InboundMessageContext' import type { OutOfBandService } from '../OutOfBandService' -import { createOutboundDidCommV1Message } from '../../../../../agent/helpers' +import { OutboundMessageContext } from '../../../agent/models' import { HandshakeReuseMessage } from '../messages/HandshakeReuseMessage' export class HandshakeReuseHandler implements Handler { @@ -17,6 +17,9 @@ export class HandshakeReuseHandler implements Handler { const connectionRecord = messageContext.assertReadyConnection() const handshakeReuseAcceptedMessage = await this.outOfBandService.processHandshakeReuse(messageContext) - return createOutboundDidCommV1Message(connectionRecord, handshakeReuseAcceptedMessage) + return new OutboundMessageContext(handshakeReuseAcceptedMessage, { + agentContext: messageContext.agentContext, + connection: connectionRecord, + }) } } diff --git a/packages/core/src/modules/proofs/ProofsApi.ts b/packages/core/src/modules/proofs/ProofsApi.ts index 8686af8b15..1bec0aec43 100644 --- a/packages/core/src/modules/proofs/ProofsApi.ts +++ b/packages/core/src/modules/proofs/ProofsApi.ts @@ -1,4 +1,4 @@ -import type { DidCommV1Message } from '../../didcomm' +import type { AgentMessage } from '../../agent/AgentMessage' import type { Query } from '../../storage/StorageService' import type { ProofService } from './ProofService' import type { @@ -37,7 +37,7 @@ import { AgentConfig } from '../../agent/AgentConfig' import { Dispatcher } from '../../agent/Dispatcher' import { MessageSender } from '../../agent/MessageSender' import { AgentContext } from '../../agent/context/AgentContext' -import { createOutboundDidCommV1Message } from '../../agent/helpers' +import { OutboundMessageContext } from '../../agent/models' import { InjectionSymbols } from '../../constants' import { ServiceDecorator } from '../../decorators/service/ServiceDecorator' import { AriesFrameworkError } from '../../error' @@ -68,7 +68,7 @@ export interface ProofsApi): Promise<{ - message: DidCommV1Message + message: AgentMessage proofRecord: ProofExchangeRecord }> @@ -185,8 +185,12 @@ export class ProofsApi< const { message, proofRecord } = await service.createProposal(this.agentContext, proposalOptions) - const outbound = createOutboundDidCommV1Message(connection, message) - await this.messageSender.sendMessage(this.agentContext, outbound) + const outboundMessageContext = new OutboundMessageContext(message, { + agentContext: this.agentContext, + connection, + associatedRecord: proofRecord, + }) + await this.messageSender.sendMessage(outboundMessageContext) return proofRecord } @@ -234,8 +238,12 @@ export class ProofsApi< const { message } = await service.createRequestAsResponse(this.agentContext, requestOptions) - const outboundMessage = createOutboundDidCommV1Message(connection, message) - await this.messageSender.sendMessage(this.agentContext, outboundMessage) + const outboundMessageContext = new OutboundMessageContext(message, { + agentContext: this.agentContext, + connection, + associatedRecord: proofRecord, + }) + await this.messageSender.sendMessage(outboundMessageContext) return proofRecord } @@ -264,8 +272,12 @@ export class ProofsApi< } const { message, proofRecord } = await service.createRequest(this.agentContext, createProofRequest) - const outboundMessage = createOutboundDidCommV1Message(connection, message) - await this.messageSender.sendMessage(this.agentContext, outboundMessage) + const outboundMessageContext = new OutboundMessageContext(message, { + agentContext: this.agentContext, + connection, + associatedRecord: proofRecord, + }) + await this.messageSender.sendMessage(outboundMessageContext) return proofRecord } @@ -301,8 +313,12 @@ export class ProofsApi< // Assert connection.assertReady() - const outboundMessage = createOutboundDidCommV1Message(connection, message) - await this.messageSender.sendMessage(this.agentContext, outboundMessage) + const outboundMessageContext = new OutboundMessageContext(message, { + agentContext: this.agentContext, + connection, + associatedRecord: proofRecord, + }) + await this.messageSender.sendMessage(outboundMessageContext) return proofRecord } @@ -327,12 +343,16 @@ export class ProofsApi< role: DidCommMessageRole.Sender, }) - await this.messageSender.sendMessageToService(this.agentContext, { - message, - service: recipientService.resolvedDidCommService, - senderKey: message.service.resolvedDidCommService.recipientKeys[0], - returnRoute: true, - }) + await this.messageSender.sendMessageToService( + new OutboundMessageContext(message, { + agentContext: this.agentContext, + serviceParams: { + service: recipientService.resolvedDidCommService, + senderKey: message.service.resolvedDidCommService.recipientKeys[0], + returnRoute: true, + }, + }) + ) return proofRecord } @@ -352,7 +372,7 @@ export class ProofsApi< * @returns the message itself and the proof record associated with the sent request message */ public async createRequest(options: CreateProofRequestOptions): Promise<{ - message: DidCommV1Message + message: AgentMessage proofRecord: ProofExchangeRecord }> { const service = this.getService(options.protocolVersion) @@ -405,20 +425,28 @@ export class ProofsApi< // Assert connection.assertReady() - const outboundMessage = createOutboundDidCommV1Message(connection, message) - await this.messageSender.sendMessage(this.agentContext, outboundMessage) + const outboundMessageContext = new OutboundMessageContext(message, { + agentContext: this.agentContext, + connection, + associatedRecord: proofRecord, + }) + await this.messageSender.sendMessage(outboundMessageContext) } // Use ~service decorator otherwise else if (requestMessage?.service && presentationMessage?.service) { const recipientService = presentationMessage?.service const ourService = requestMessage.service - await this.messageSender.sendMessageToService(this.agentContext, { - message, - service: recipientService.resolvedDidCommService, - senderKey: ourService.resolvedDidCommService.recipientKeys[0], - returnRoute: true, - }) + await this.messageSender.sendMessageToService( + new OutboundMessageContext(message, { + agentContext: this.agentContext, + serviceParams: { + service: recipientService.resolvedDidCommService, + senderKey: ourService.resolvedDidCommService.recipientKeys[0], + returnRoute: true, + }, + }) + ) } // Cannot send message without credentialId or ~service decorator else { @@ -497,8 +525,12 @@ export class ProofsApi< description: message, }) - const outboundMessage = createOutboundDidCommV1Message(connection, problemReport) - await this.messageSender.sendMessage(this.agentContext, outboundMessage) + const outboundMessageContext = new OutboundMessageContext(problemReport, { + agentContext: this.agentContext, + connection, + associatedRecord: record, + }) + await this.messageSender.sendMessage(outboundMessageContext) return record } diff --git a/packages/core/src/modules/proofs/protocol/v1/handlers/V1PresentationHandler.ts b/packages/core/src/modules/proofs/protocol/v1/handlers/V1PresentationHandler.ts index 8cf3539d42..c3fcd713d2 100644 --- a/packages/core/src/modules/proofs/protocol/v1/handlers/V1PresentationHandler.ts +++ b/packages/core/src/modules/proofs/protocol/v1/handlers/V1PresentationHandler.ts @@ -5,7 +5,7 @@ import type { ProofResponseCoordinator } from '../../../ProofResponseCoordinator import type { ProofExchangeRecord } from '../../../repository' import type { V1ProofService } from '../V1ProofService' -import { createOutboundDidCommV1Message, createOutboundServiceMessage } from '../../../../../agent/helpers' +import { OutboundMessageContext } from '../../../../../agent/models' import { V1PresentationMessage, V1RequestPresentationMessage } from '../messages' export class V1PresentationHandler implements Handler { @@ -55,15 +55,21 @@ export class V1PresentationHandler implements Handler { }) if (messageContext.connection) { - return createOutboundDidCommV1Message(messageContext.connection, message) + return new OutboundMessageContext(message, { + agentContext: messageContext.agentContext, + connection: messageContext.connection, + associatedRecord: proofRecord, + }) } else if (requestMessage?.service && presentationMessage?.service) { const recipientService = presentationMessage?.service const ourService = requestMessage?.service - return createOutboundServiceMessage({ - payload: message, - service: recipientService.resolvedDidCommService, - senderKey: ourService.resolvedDidCommService.recipientKeys[0], + return new OutboundMessageContext(message, { + agentContext: messageContext.agentContext, + serviceParams: { + service: recipientService.resolvedDidCommService, + senderKey: ourService.resolvedDidCommService.recipientKeys[0], + }, }) } diff --git a/packages/core/src/modules/proofs/protocol/v1/handlers/V1ProposePresentationHandler.ts b/packages/core/src/modules/proofs/protocol/v1/handlers/V1ProposePresentationHandler.ts index e3b15e741c..4e9d437669 100644 --- a/packages/core/src/modules/proofs/protocol/v1/handlers/V1ProposePresentationHandler.ts +++ b/packages/core/src/modules/proofs/protocol/v1/handlers/V1ProposePresentationHandler.ts @@ -8,7 +8,7 @@ import type { ProofRequestFromProposalOptions } from '../../../models/ProofServi import type { ProofExchangeRecord } from '../../../repository/ProofExchangeRecord' import type { V1ProofService } from '../V1ProofService' -import { createOutboundDidCommV1Message } from '../../../../../agent/helpers' +import { OutboundMessageContext } from '../../../../../agent/models' import { AriesFrameworkError } from '../../../../../error' import { V1ProposePresentationMessage } from '../messages' @@ -99,6 +99,10 @@ export class V1ProposePresentationHandler implements Handler { willConfirm: true, }) - return createOutboundDidCommV1Message(messageContext.connection, message) + return new OutboundMessageContext(message, { + agentContext: messageContext.agentContext, + connection: messageContext.connection, + associatedRecord: proofRecord, + }) } } diff --git a/packages/core/src/modules/proofs/protocol/v1/handlers/V1RequestPresentationHandler.ts b/packages/core/src/modules/proofs/protocol/v1/handlers/V1RequestPresentationHandler.ts index 9beadbf1e1..5a0455baba 100644 --- a/packages/core/src/modules/proofs/protocol/v1/handlers/V1RequestPresentationHandler.ts +++ b/packages/core/src/modules/proofs/protocol/v1/handlers/V1RequestPresentationHandler.ts @@ -11,7 +11,7 @@ import type { import type { ProofExchangeRecord } from '../../../repository/ProofExchangeRecord' import type { V1ProofService } from '../V1ProofService' -import { createOutboundDidCommV1Message, createOutboundServiceMessage } from '../../../../../agent/helpers' +import { OutboundMessageContext } from '../../../../../agent/models' import { ServiceDecorator } from '../../../../../decorators/service/ServiceDecorator' import { AriesFrameworkError } from '../../../../../error' import { DidCommMessageRole } from '../../../../../storage' @@ -96,7 +96,11 @@ export class V1RequestPresentationHandler implements Handler { }) if (messageContext.connection) { - return createOutboundDidCommV1Message(messageContext.connection, message) + return new OutboundMessageContext(message, { + agentContext: messageContext.agentContext, + connection: messageContext.connection, + associatedRecord: proofRecord, + }) } else if (requestMessage.service) { const routing = await this.routingService.getRouting(messageContext.agentContext) message.service = new ServiceDecorator({ @@ -111,10 +115,12 @@ export class V1RequestPresentationHandler implements Handler { associatedRecordId: proofRecord.id, role: DidCommMessageRole.Sender, }) - return createOutboundServiceMessage({ - payload: message, - service: recipientService.resolvedDidCommService, - senderKey: message.service.resolvedDidCommService.recipientKeys[0], + return new OutboundMessageContext(message, { + agentContext: messageContext.agentContext, + serviceParams: { + service: recipientService.resolvedDidCommService, + senderKey: message.service.resolvedDidCommService.recipientKeys[0], + }, }) } diff --git a/packages/core/src/modules/proofs/protocol/v2/handlers/V2PresentationHandler.ts b/packages/core/src/modules/proofs/protocol/v2/handlers/V2PresentationHandler.ts index 921f3146d4..c812b6e8bd 100644 --- a/packages/core/src/modules/proofs/protocol/v2/handlers/V2PresentationHandler.ts +++ b/packages/core/src/modules/proofs/protocol/v2/handlers/V2PresentationHandler.ts @@ -5,7 +5,7 @@ import type { ProofResponseCoordinator } from '../../../ProofResponseCoordinator import type { ProofExchangeRecord } from '../../../repository' import type { V2ProofService } from '../V2ProofService' -import { createOutboundDidCommV1Message, createOutboundServiceMessage } from '../../../../../agent/helpers' +import { OutboundMessageContext } from '../../../../../agent/models' import { V2PresentationMessage, V2RequestPresentationMessage } from '../messages' export class V2PresentationHandler implements Handler { @@ -55,15 +55,21 @@ export class V2PresentationHandler implements Handler { }) if (messageContext.connection) { - return createOutboundDidCommV1Message(messageContext.connection, message) + return new OutboundMessageContext(message, { + agentContext: messageContext.agentContext, + connection: messageContext.connection, + associatedRecord: proofRecord, + }) } else if (requestMessage?.service && presentationMessage?.service) { const recipientService = presentationMessage?.service const ourService = requestMessage?.service - return createOutboundServiceMessage({ - payload: message, - service: recipientService.resolvedDidCommService, - senderKey: ourService.resolvedDidCommService.recipientKeys[0], + return new OutboundMessageContext(message, { + agentContext: messageContext.agentContext, + serviceParams: { + service: recipientService.resolvedDidCommService, + senderKey: ourService.resolvedDidCommService.recipientKeys[0], + }, }) } diff --git a/packages/core/src/modules/proofs/protocol/v2/handlers/V2ProposePresentationHandler.ts b/packages/core/src/modules/proofs/protocol/v2/handlers/V2ProposePresentationHandler.ts index d939fb1339..e822ed7a32 100644 --- a/packages/core/src/modules/proofs/protocol/v2/handlers/V2ProposePresentationHandler.ts +++ b/packages/core/src/modules/proofs/protocol/v2/handlers/V2ProposePresentationHandler.ts @@ -11,7 +11,7 @@ import type { import type { ProofExchangeRecord } from '../../../repository/ProofExchangeRecord' import type { V2ProofService } from '../V2ProofService' -import { createOutboundDidCommV1Message } from '../../../../../agent/helpers' +import { OutboundMessageContext } from '../../../../../agent/models' import { AriesFrameworkError } from '../../../../../error/AriesFrameworkError' import { V2ProposalPresentationMessage } from '../messages/V2ProposalPresentationMessage' @@ -90,6 +90,10 @@ export class V2ProposePresentationHandler 0 ? new MessageDeliveryMessage({ threadId: messageContext.message.threadId, @@ -93,7 +93,7 @@ export class V2MessagePickupService { messageCount: 0, }) - return createOutboundDidCommV1Message(connection, outboundMessage) + return new OutboundMessageContext(outboundMessageContext, { agentContext: messageContext.agentContext, connection }) } public async processMessagesReceived(messageContext: InboundMessageContext) { @@ -113,7 +113,7 @@ export class V2MessagePickupService { messageCount: await this.messageRepository.getAvailableMessageCount(connection.id), }) - return createOutboundDidCommV1Message(connection, statusMessage) + return new OutboundMessageContext(statusMessage, { agentContext: messageContext.agentContext, connection }) } protected registerHandlers() { diff --git a/packages/core/src/modules/routing/protocol/pickup/v2/handlers/MessageDeliveryHandler.ts b/packages/core/src/modules/routing/protocol/pickup/v2/handlers/MessageDeliveryHandler.ts index f907db3ba2..fb9db1d25b 100644 --- a/packages/core/src/modules/routing/protocol/pickup/v2/handlers/MessageDeliveryHandler.ts +++ b/packages/core/src/modules/routing/protocol/pickup/v2/handlers/MessageDeliveryHandler.ts @@ -2,7 +2,7 @@ import type { Handler } from '../../../../../../agent/Handler' import type { InboundMessageContext } from '../../../../../../agent/models/InboundMessageContext' import type { MediationRecipientService } from '../../../../services' -import { createOutboundDidCommV1Message } from '../../../../../../agent/helpers' +import { OutboundMessageContext } from '../../../../../../agent/models' import { MessageDeliveryMessage } from '../messages/MessageDeliveryMessage' export class MessageDeliveryHandler implements Handler { @@ -18,7 +18,10 @@ export class MessageDeliveryHandler implements Handler { const deliveryReceivedMessage = await this.mediationRecipientService.processDelivery(messageContext) if (deliveryReceivedMessage) { - return createOutboundDidCommV1Message(connection, deliveryReceivedMessage) + return new OutboundMessageContext(deliveryReceivedMessage, { + agentContext: messageContext.agentContext, + connection, + }) } } } diff --git a/packages/core/src/modules/routing/protocol/pickup/v2/handlers/StatusHandler.ts b/packages/core/src/modules/routing/protocol/pickup/v2/handlers/StatusHandler.ts index 0eb63f38a5..163f1a9c4f 100644 --- a/packages/core/src/modules/routing/protocol/pickup/v2/handlers/StatusHandler.ts +++ b/packages/core/src/modules/routing/protocol/pickup/v2/handlers/StatusHandler.ts @@ -2,7 +2,7 @@ import type { Handler } from '../../../../../../agent/Handler' import type { InboundMessageContext } from '../../../../../../agent/models/InboundMessageContext' import type { MediationRecipientService } from '../../../../services' -import { createOutboundDidCommV1Message } from '../../../../../../agent/helpers' +import { OutboundMessageContext } from '../../../../../../agent/models' import { StatusMessage } from '../messages' export class StatusHandler implements Handler { @@ -18,7 +18,10 @@ export class StatusHandler implements Handler { const deliveryRequestMessage = await this.mediatorRecipientService.processStatus(messageContext) if (deliveryRequestMessage) { - return createOutboundDidCommV1Message(connection, deliveryRequestMessage) + return new OutboundMessageContext(deliveryRequestMessage, { + agentContext: messageContext.agentContext, + connection, + }) } } } diff --git a/packages/core/src/modules/routing/services/MediationRecipientService.ts b/packages/core/src/modules/routing/services/MediationRecipientService.ts index 2ce6324736..f05f66e20f 100644 --- a/packages/core/src/modules/routing/services/MediationRecipientService.ts +++ b/packages/core/src/modules/routing/services/MediationRecipientService.ts @@ -1,9 +1,9 @@ import type { AgentContext } from '../../../agent' +import type { AgentMessage } from '../../../agent/AgentMessage' import type { AgentMessageReceivedEvent } from '../../../agent/Events' import type { InboundMessageContext } from '../../../agent/models/InboundMessageContext' -import type { DidCommV1Message } from '../../../didcomm' -import type { EncryptedMessage } from '../../../didcomm/types' import type { Query } from '../../../storage/StorageService' +import type { EncryptedMessage } from '../../../types' import type { ConnectionRecord } from '../../connections' import type { Routing } from '../../connections/services/ConnectionService' import type { MediationStateChangedEvent, KeylistUpdatedEvent } from '../RoutingEvents' @@ -17,7 +17,7 @@ import { filter, first, timeout } from 'rxjs/operators' import { EventEmitter } from '../../../agent/EventEmitter' import { filterContextCorrelationId, AgentEventTypes } from '../../../agent/Events' import { MessageSender } from '../../../agent/MessageSender' -import { createOutboundDidCommV1Message } from '../../../agent/helpers' +import { OutboundMessageContext } from '../../../agent/models' import { Key, KeyType } from '../../../crypto' import { AriesFrameworkError } from '../../../error' import { injectable } from '../../../plugins' @@ -209,8 +209,8 @@ export class MediationRecipientService { ) .subscribe(subject) - const outboundMessage = createOutboundDidCommV1Message(connection, message) - await this.messageSender.sendMessage(agentContext, outboundMessage) + const outboundMessageContext = new OutboundMessageContext(message, { agentContext, connection }) + await this.messageSender.sendMessage(outboundMessageContext) const keylistUpdate = await firstValueFrom(subject) return keylistUpdate.payload.mediationRecord @@ -297,8 +297,10 @@ export class MediationRecipientService { const websocketSchemes = ['ws', 'wss'] await this.messageSender.sendMessage( - messageContext.agentContext, - createOutboundDidCommV1Message(connectionRecord, message), + new OutboundMessageContext(message, { + agentContext: messageContext.agentContext, + connection: connectionRecord, + }), { transportPriority: { schemes: websocketSchemes, @@ -464,7 +466,7 @@ export class MediationRecipientService { } } -export interface MediationProtocolMsgReturnType { +export interface MediationProtocolMsgReturnType { message: MessageType mediationRecord: MediationRecord } diff --git a/packages/core/src/modules/routing/services/__tests__/V2MessagePickupService.test.ts b/packages/core/src/modules/routing/services/__tests__/V2MessagePickupService.test.ts index 1bed5feaf2..b4130c2a9e 100644 --- a/packages/core/src/modules/routing/services/__tests__/V2MessagePickupService.test.ts +++ b/packages/core/src/modules/routing/services/__tests__/V2MessagePickupService.test.ts @@ -61,17 +61,17 @@ describe('V2MessagePickupService', () => { const messageContext = new InboundMessageContext(statusRequest, { connection: mockConnection, agentContext }) - const { connection, payload } = await pickupService.processStatusRequest(messageContext) + const { connection, message } = await pickupService.processStatusRequest(messageContext) expect(connection).toEqual(mockConnection) - expect(payload).toEqual( + expect(message).toEqual( new StatusMessage({ - id: payload.id, + id: message.id, threadId: statusRequest.threadId, messageCount: 0, }) ) - expect(messageRepository.getAvailableMessageCount).toHaveBeenCalledWith(connection.id) + expect(messageRepository.getAvailableMessageCount).toHaveBeenCalledWith(mockConnection.id) }) test('multiple messages in queue', async () => { @@ -80,17 +80,17 @@ describe('V2MessagePickupService', () => { const messageContext = new InboundMessageContext(statusRequest, { connection: mockConnection, agentContext }) - const { connection, payload } = await pickupService.processStatusRequest(messageContext) + const { connection, message } = await pickupService.processStatusRequest(messageContext) expect(connection).toEqual(mockConnection) - expect(payload).toEqual( + expect(message).toEqual( new StatusMessage({ - id: payload.id, + id: message.id, threadId: statusRequest.threadId, messageCount: 5, }) ) - expect(messageRepository.getAvailableMessageCount).toHaveBeenCalledWith(connection.id) + expect(messageRepository.getAvailableMessageCount).toHaveBeenCalledWith(mockConnection.id) }) test('status request specifying recipient key', async () => { @@ -116,17 +116,17 @@ describe('V2MessagePickupService', () => { const messageContext = new InboundMessageContext(deliveryRequest, { connection: mockConnection, agentContext }) - const { connection, payload } = await pickupService.processDeliveryRequest(messageContext) + const { connection, message } = await pickupService.processDeliveryRequest(messageContext) expect(connection).toEqual(mockConnection) - expect(payload).toEqual( + expect(message).toEqual( new StatusMessage({ - id: payload.id, + id: message.id, threadId: deliveryRequest.threadId, messageCount: 0, }) ) - expect(messageRepository.takeFromQueue).toHaveBeenCalledWith(connection.id, 10, true) + expect(messageRepository.takeFromQueue).toHaveBeenCalledWith(mockConnection.id, 10, true) }) test('less messages in queue than limit', async () => { @@ -136,13 +136,13 @@ describe('V2MessagePickupService', () => { const messageContext = new InboundMessageContext(deliveryRequest, { connection: mockConnection, agentContext }) - const { connection, payload } = await pickupService.processDeliveryRequest(messageContext) + const { connection, message } = await pickupService.processDeliveryRequest(messageContext) expect(connection).toEqual(mockConnection) - expect(payload).toBeInstanceOf(MessageDeliveryMessage) - expect(payload.threadId).toEqual(deliveryRequest.threadId) - expect(payload.appendedAttachments?.length).toEqual(3) - expect(payload.appendedAttachments).toEqual( + expect(message).toBeInstanceOf(MessageDeliveryMessage) + expect(message.threadId).toEqual(deliveryRequest.threadId) + expect(message.appendedAttachments?.length).toEqual(3) + expect(message.appendedAttachments).toEqual( expect.arrayContaining( queuedMessages.map((msg) => expect.objectContaining({ @@ -153,7 +153,7 @@ describe('V2MessagePickupService', () => { ) ) ) - expect(messageRepository.takeFromQueue).toHaveBeenCalledWith(connection.id, 10, true) + expect(messageRepository.takeFromQueue).toHaveBeenCalledWith(mockConnection.id, 10, true) }) test('more messages in queue than limit', async () => { @@ -163,13 +163,13 @@ describe('V2MessagePickupService', () => { const messageContext = new InboundMessageContext(deliveryRequest, { connection: mockConnection, agentContext }) - const { connection, payload } = await pickupService.processDeliveryRequest(messageContext) + const { connection, message } = await pickupService.processDeliveryRequest(messageContext) expect(connection).toEqual(mockConnection) - expect(payload).toBeInstanceOf(MessageDeliveryMessage) - expect(payload.threadId).toEqual(deliveryRequest.threadId) - expect(payload.appendedAttachments?.length).toEqual(2) - expect(payload.appendedAttachments).toEqual( + expect(message).toBeInstanceOf(MessageDeliveryMessage) + expect(message.threadId).toEqual(deliveryRequest.threadId) + expect(message.appendedAttachments?.length).toEqual(2) + expect(message.appendedAttachments).toEqual( expect.arrayContaining( queuedMessages.slice(0, 2).map((msg) => expect.objectContaining({ @@ -180,7 +180,7 @@ describe('V2MessagePickupService', () => { ) ) ) - expect(messageRepository.takeFromQueue).toHaveBeenCalledWith(connection.id, 2, true) + expect(messageRepository.takeFromQueue).toHaveBeenCalledWith(mockConnection.id, 2, true) }) test('delivery request specifying recipient key', async () => { @@ -210,18 +210,18 @@ describe('V2MessagePickupService', () => { const messageContext = new InboundMessageContext(messagesReceived, { connection: mockConnection, agentContext }) - const { connection, payload } = await pickupService.processMessagesReceived(messageContext) + const { connection, message } = await pickupService.processMessagesReceived(messageContext) expect(connection).toEqual(mockConnection) - expect(payload).toEqual( + expect(message).toEqual( new StatusMessage({ - id: payload.id, + id: message.id, threadId: messagesReceived.threadId, messageCount: 4, }) ) - expect(messageRepository.getAvailableMessageCount).toHaveBeenCalledWith(connection.id) - expect(messageRepository.takeFromQueue).toHaveBeenCalledWith(connection.id, 2) + expect(messageRepository.getAvailableMessageCount).toHaveBeenCalledWith(mockConnection.id) + expect(messageRepository.takeFromQueue).toHaveBeenCalledWith(mockConnection.id, 2) }) test('all messages have been received', async () => { @@ -234,19 +234,19 @@ describe('V2MessagePickupService', () => { const messageContext = new InboundMessageContext(messagesReceived, { connection: mockConnection, agentContext }) - const { connection, payload } = await pickupService.processMessagesReceived(messageContext) + const { connection, message } = await pickupService.processMessagesReceived(messageContext) expect(connection).toEqual(mockConnection) - expect(payload).toEqual( + expect(message).toEqual( new StatusMessage({ - id: payload.id, + id: message.id, threadId: messagesReceived.threadId, messageCount: 0, }) ) - expect(messageRepository.getAvailableMessageCount).toHaveBeenCalledWith(connection.id) - expect(messageRepository.takeFromQueue).toHaveBeenCalledWith(connection.id, 2) + expect(messageRepository.getAvailableMessageCount).toHaveBeenCalledWith(mockConnection.id) + expect(messageRepository.takeFromQueue).toHaveBeenCalledWith(mockConnection.id, 2) }) }) }) diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index 676ac2d747..6d409b000e 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -1,8 +1,14 @@ +import type { AgentMessage } from './agent/AgentMessage' +import type { Key } from './crypto' import type { Logger } from './logger' +import type { ConnectionRecord } from './modules/connections' import type { AutoAcceptCredential } from './modules/credentials/models/CredentialAutoAcceptType' +import type { ResolvedDidCommService } from './modules/didcomm' import type { IndyPoolConfig } from './modules/ledger/IndyPool' +import type { OutOfBandRecord } from './modules/oob/repository' import type { AutoAcceptProof } from './modules/proofs' import type { MediatorPickupStrategy } from './modules/routing' +import type { BaseRecord } from './storage/BaseRecord' export enum KeyDerivationMethod { /** default value in indy-sdk. Will be used when no value is provided */ @@ -37,6 +43,13 @@ export interface WalletExportImportConfig { path: string } +export type EncryptedMessage = { + protected: string + iv: unknown + ciphertext: unknown + tag: unknown +} + export enum DidCommMimeType { V0 = 'application/ssi-agent-wire', V1 = 'application/didcomm-envelope-enc', @@ -75,6 +88,18 @@ export interface InitConfig { } export type ProtocolVersion = `${number}.${number}` +export interface PlaintextMessage { + '@type': string + '@id': string + [key: string]: unknown +} + +export interface OutboundPackage { + payload: EncryptedMessage + responseRequested?: boolean + endpoint?: string + connectionId?: string +} export type JsonValue = string | number | boolean | null | JsonObject | JsonArray export type JsonArray = Array diff --git a/packages/core/tests/multi-protocol-version.test.ts b/packages/core/tests/multi-protocol-version.test.ts index 04c755c6ca..07b7b057f0 100644 --- a/packages/core/tests/multi-protocol-version.test.ts +++ b/packages/core/tests/multi-protocol-version.test.ts @@ -5,10 +5,10 @@ import { filter, firstValueFrom, Subject, timeout } from 'rxjs' import { SubjectInboundTransport } from '../../../tests/transport/SubjectInboundTransport' import { SubjectOutboundTransport } from '../../../tests/transport/SubjectOutboundTransport' -import { parseMessageType, MessageSender, Dispatcher, IsValidMessageType, DidCommV1Message } from '../src' +import { parseMessageType, MessageSender, Dispatcher, AgentMessage, IsValidMessageType } from '../src' import { Agent } from '../src/agent/Agent' import { AgentEventTypes } from '../src/agent/Events' -import { createOutboundDidCommV1Message } from '../src/agent/helpers' +import { OutboundMessageContext } from '../src/agent/models' import { getAgentOptions } from './helpers' @@ -56,10 +56,7 @@ describe('multi version protocols', () => { bobAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) await bobAgent.initialize() - const { outOfBandRecord } = await aliceAgent.oob.createInvitation() - if (!outOfBandRecord) throw new Error('Unable to create out of band invitation') - - const { outOfBandInvitation, id } = outOfBandRecord + const { outOfBandInvitation, id } = await aliceAgent.oob.createInvitation() let { connectionRecord: bobConnection } = await bobAgent.oob.receiveInvitation(outOfBandInvitation, { autoAcceptConnection: true, autoAcceptInvitation: true, @@ -88,8 +85,7 @@ describe('multi version protocols', () => { ) await bobMessageSender.sendMessage( - bobAgent.context, - createOutboundDidCommV1Message(bobConnection, new TestMessageV11()) + new OutboundMessageContext(new TestMessageV11(), { agentContext: bobAgent.context, connection: bobConnection }) ) // Wait for the agent message processed event to be called @@ -106,8 +102,7 @@ describe('multi version protocols', () => { ) await bobMessageSender.sendMessage( - bobAgent.context, - createOutboundDidCommV1Message(bobConnection, new TestMessageV15()) + new OutboundMessageContext(new TestMessageV15(), { agentContext: bobAgent.context, connection: bobConnection }) ) await agentMessageV15ProcessedPromise @@ -115,7 +110,7 @@ describe('multi version protocols', () => { }) }) -class TestMessageV11 extends DidCommV1Message { +class TestMessageV11 extends AgentMessage { public constructor() { super() this.id = this.generateId() @@ -126,7 +121,7 @@ class TestMessageV11 extends DidCommV1Message { public static readonly type = parseMessageType('https://didcomm.org/custom-protocol/1.1/test-message') } -class TestMessageV13 extends DidCommV1Message { +class TestMessageV13 extends AgentMessage { public constructor() { super() this.id = this.generateId() @@ -137,7 +132,7 @@ class TestMessageV13 extends DidCommV1Message { public static readonly type = parseMessageType('https://didcomm.org/custom-protocol/1.3/test-message') } -class TestMessageV15 extends DidCommV1Message { +class TestMessageV15 extends AgentMessage { public constructor() { super() this.id = this.generateId() diff --git a/packages/question-answer/src/QuestionAnswerApi.ts b/packages/question-answer/src/QuestionAnswerApi.ts index bdbd002859..777e3ff12f 100644 --- a/packages/question-answer/src/QuestionAnswerApi.ts +++ b/packages/question-answer/src/QuestionAnswerApi.ts @@ -4,7 +4,7 @@ import type { Query } from '@aries-framework/core' import { AgentContext, ConnectionService, - createOutboundDidCommV1Message, + OutboundMessageContext, Dispatcher, injectable, MessageSender, @@ -63,8 +63,13 @@ export class QuestionAnswerApi { detail: config?.detail, } ) - const outboundMessage = createOutboundDidCommV1Message(connection, questionMessage) - await this.messageSender.sendMessage(this.agentContext, outboundMessage) + const outboundMessageContext = new OutboundMessageContext(questionMessage, { + agentContext: this.agentContext, + connection, + associatedRecord: questionAnswerRecord, + }) + + await this.messageSender.sendMessage(outboundMessageContext) return questionAnswerRecord } @@ -87,8 +92,13 @@ export class QuestionAnswerApi { const connection = await this.connectionService.getById(this.agentContext, questionRecord.connectionId) - const outboundMessage = createOutboundDidCommV1Message(connection, answerMessage) - await this.messageSender.sendMessage(this.agentContext, outboundMessage) + const outboundMessageContext = new OutboundMessageContext(answerMessage, { + agentContext: this.agentContext, + connection, + associatedRecord: questionAnswerRecord, + }) + + await this.messageSender.sendMessage(outboundMessageContext) return questionAnswerRecord } diff --git a/samples/extension-module/dummy/DummyApi.ts b/samples/extension-module/dummy/DummyApi.ts index eeb50b469b..dded85085f 100644 --- a/samples/extension-module/dummy/DummyApi.ts +++ b/samples/extension-module/dummy/DummyApi.ts @@ -1,7 +1,14 @@ import type { DummyRecord } from './repository/DummyRecord' import type { Query } from '@aries-framework/core' -import { AgentContext, ConnectionService, Dispatcher, injectable, MessageSender } from '@aries-framework/core' +import { + OutboundMessageContext, + AgentContext, + ConnectionService, + Dispatcher, + injectable, + MessageSender, +} from '@aries-framework/core' import { DummyRequestHandler, DummyResponseHandler } from './handlers' import { DummyState } from './repository' @@ -37,9 +44,11 @@ export class DummyApi { */ public async request(connectionId: string) { const connection = await this.connectionService.getById(this.agentContext, connectionId) - const { record, message: payload } = await this.dummyService.createRequest(this.agentContext, connection) + const { record, message } = await this.dummyService.createRequest(this.agentContext, connection) - await this.messageSender.sendMessage(this.agentContext, { connection, payload }) + await this.messageSender.sendMessage( + new OutboundMessageContext(message, { agentContext: this.agentContext, connection }) + ) await this.dummyService.updateState(this.agentContext, record, DummyState.RequestSent) @@ -56,9 +65,11 @@ export class DummyApi { const record = await this.dummyService.getById(this.agentContext, dummyId) const connection = await this.connectionService.getById(this.agentContext, record.connectionId) - const payload = await this.dummyService.createResponse(this.agentContext, record) + const message = await this.dummyService.createResponse(this.agentContext, record) - await this.messageSender.sendMessage(this.agentContext, { connection, payload }) + await this.messageSender.sendMessage( + new OutboundMessageContext(message, { agentContext: this.agentContext, connection, associatedRecord: record }) + ) await this.dummyService.updateState(this.agentContext, record, DummyState.ResponseSent) diff --git a/samples/extension-module/dummy/handlers/DummyRequestHandler.ts b/samples/extension-module/dummy/handlers/DummyRequestHandler.ts index 86d12f06e6..7b0de4ea72 100644 --- a/samples/extension-module/dummy/handlers/DummyRequestHandler.ts +++ b/samples/extension-module/dummy/handlers/DummyRequestHandler.ts @@ -1,7 +1,7 @@ import type { DummyService } from '../services' import type { Handler, HandlerInboundMessage } from '@aries-framework/core' -import { createOutboundDidCommV1Message } from '@aries-framework/core' +import { OutboundMessageContext } from '@aries-framework/core' import { DummyRequestMessage } from '../messages' @@ -18,7 +18,7 @@ export class DummyRequestHandler implements Handler { const responseMessage = await this.dummyService.processRequest(inboundMessage) if (responseMessage) { - return createOutboundDidCommV1Message(connection, responseMessage) + return new OutboundMessageContext(responseMessage, { agentContext: inboundMessage.agentContext, connection }) } } }