From 4800700e9f138f02e67c93e8882f45d723dd22cb Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Mon, 4 Jul 2022 10:50:45 -0300 Subject: [PATCH] feat(oob): allow to append attachments to invitations (#926) Signed-off-by: Ariel Gentile --- .../attachment/AttachmentExtension.ts | 2 +- packages/core/src/index.ts | 1 + .../ConnectionInvitationMessage.test.ts | 11 ++++ .../messages/ConnectionInvitationMessage.ts | 4 ++ .../core/src/modules/oob/OutOfBandModule.ts | 5 ++ .../oob/__tests__/OutOfBandInvitation.test.ts | 65 +++++++++++++++++++ .../src/modules/oob/__tests__/helpers.test.ts | 48 +++++++++++++- packages/core/src/modules/oob/helpers.ts | 3 + .../oob/messages/OutOfBandInvitation.ts | 2 + 9 files changed, 139 insertions(+), 2 deletions(-) diff --git a/packages/core/src/decorators/attachment/AttachmentExtension.ts b/packages/core/src/decorators/attachment/AttachmentExtension.ts index 67ab28d578..565e203da8 100644 --- a/packages/core/src/decorators/attachment/AttachmentExtension.ts +++ b/packages/core/src/decorators/attachment/AttachmentExtension.ts @@ -8,7 +8,7 @@ import { Attachment } from './Attachment' export function AttachmentDecorated(Base: T) { class AttachmentDecoratorExtension extends Base { /** - * The ~attach decorator is required for appending attachments to a preview + * The ~attach decorator is required for appending attachments to a message */ @Expose({ name: '~attach' }) @Type(() => Attachment) diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 2dddd8b966..036a70959b 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -23,6 +23,7 @@ export { InjectionSymbols } from './constants' export type { Wallet } from './wallet/Wallet' export type { TransportSession } from './agent/TransportService' export { TransportService } from './agent/TransportService' +export { Attachment } from './decorators/attachment/Attachment' import { parseInvitationUrl } from './utils/parseInvitation' import { uuid } from './utils/uuid' diff --git a/packages/core/src/modules/connections/__tests__/ConnectionInvitationMessage.test.ts b/packages/core/src/modules/connections/__tests__/ConnectionInvitationMessage.test.ts index 256bed4371..839419b48f 100644 --- a/packages/core/src/modules/connections/__tests__/ConnectionInvitationMessage.test.ts +++ b/packages/core/src/modules/connections/__tests__/ConnectionInvitationMessage.test.ts @@ -1,6 +1,7 @@ import { validateOrReject } from 'class-validator' import { parseUrl } from 'query-string' +import { Attachment } from '../../../decorators/attachment/Attachment' import { ClassValidationError } from '../../../error/ClassValidationError' import { JsonEncoder } from '../../../utils/JsonEncoder' import { JsonTransformer } from '../../../utils/JsonTransformer' @@ -71,6 +72,16 @@ describe('ConnectionInvitationMessage', () => { serviceEndpoint: 'https://example.com', label: 'test', imageUrl: 'test-image-path', + appendedAttachments: [ + new Attachment({ + id: 'test-attachment', + data: { + json: { + value: 'test', + }, + }, + }), + ], }) const invitationUrl = invitation.toUrl({ diff --git a/packages/core/src/modules/connections/messages/ConnectionInvitationMessage.ts b/packages/core/src/modules/connections/messages/ConnectionInvitationMessage.ts index 66067a15ce..95e77a2e64 100644 --- a/packages/core/src/modules/connections/messages/ConnectionInvitationMessage.ts +++ b/packages/core/src/modules/connections/messages/ConnectionInvitationMessage.ts @@ -1,3 +1,5 @@ +import type { Attachment } from '../../../decorators/attachment/Attachment' + import { Transform } from 'class-transformer' import { ArrayNotEmpty, IsArray, IsOptional, IsString, IsUrl, ValidateIf } from 'class-validator' import { parseUrl } from 'query-string' @@ -12,6 +14,7 @@ export interface BaseInvitationOptions { id?: string label: string imageUrl?: string + appendedAttachments?: Attachment[] } export interface InlineInvitationOptions { @@ -41,6 +44,7 @@ export class ConnectionInvitationMessage extends AgentMessage { this.id = options.id || this.generateId() this.label = options.label this.imageUrl = options.imageUrl + this.appendedAttachments = options.appendedAttachments if (isDidInvitation(options)) { this.did = options.did diff --git a/packages/core/src/modules/oob/OutOfBandModule.ts b/packages/core/src/modules/oob/OutOfBandModule.ts index 0db493fa29..2bb731d97a 100644 --- a/packages/core/src/modules/oob/OutOfBandModule.ts +++ b/packages/core/src/modules/oob/OutOfBandModule.ts @@ -1,5 +1,6 @@ import type { AgentMessage } from '../../agent/AgentMessage' import type { AgentMessageReceivedEvent } from '../../agent/Events' +import type { Attachment } from '../../decorators/attachment/Attachment' import type { Logger } from '../../logger' import type { ConnectionRecord, Routing, ConnectionInvitationMessage } from '../../modules/connections' import type { DependencyManager } from '../../plugins' @@ -54,6 +55,7 @@ export interface CreateOutOfBandInvitationConfig { multiUseInvitation?: boolean autoAcceptConnection?: boolean routing?: Routing + appendedAttachments?: Attachment[] } export interface CreateLegacyInvitationConfig { @@ -134,6 +136,8 @@ export class OutOfBandModule { const messages = config.messages && config.messages.length > 0 ? config.messages : undefined const label = config.label ?? this.agentConfig.label const imageUrl = config.imageUrl ?? this.agentConfig.connectionImageUrl + const appendedAttachments = + config.appendedAttachments && config.appendedAttachments.length > 0 ? config.appendedAttachments : undefined if (!handshake && !messages) { throw new AriesFrameworkError( @@ -182,6 +186,7 @@ export class OutOfBandModule { accept: didCommProfiles, services, handshakeProtocols, + appendedAttachments, } const outOfBandInvitation = new OutOfBandInvitation(options) diff --git a/packages/core/src/modules/oob/__tests__/OutOfBandInvitation.test.ts b/packages/core/src/modules/oob/__tests__/OutOfBandInvitation.test.ts index 2a607be68a..d6fbb79137 100644 --- a/packages/core/src/modules/oob/__tests__/OutOfBandInvitation.test.ts +++ b/packages/core/src/modules/oob/__tests__/OutOfBandInvitation.test.ts @@ -1,5 +1,6 @@ import type { ClassValidationError } from '../../../error/ClassValidationError' +import { Attachment } from '../../../decorators/attachment/Attachment' import { JsonEncoder } from '../../../utils/JsonEncoder' import { JsonTransformer } from '../../../utils/JsonTransformer' import { OutOfBandInvitation } from '../messages/OutOfBandInvitation' @@ -86,6 +87,70 @@ describe('OutOfBandInvitation', () => { expect(invitation).toBeInstanceOf(OutOfBandInvitation) }) + test('create an instance of `OutOfBandInvitation` from JSON object with appended attachments', () => { + const json = { + '@type': 'https://didcomm.org/out-of-band/1.1/invitation', + '@id': '69212a3a-d068-4f9d-a2dd-4741bca89af3', + label: 'Faber College', + goal_code: 'issue-vc', + goal: 'To issue a Faber College Graduate credential', + handshake_protocols: ['https://didcomm.org/didexchange/1.0', 'https://didcomm.org/connections/1.0'], + services: ['did:sov:LjgpST2rjsoxYegQDRm7EL'], + '~attach': [ + { + '@id': 'view-1', + 'mime-type': 'image/png', + filename: 'IMG1092348.png', + lastmod_time: '2018-12-24 18:24:07Z', + description: 'view from doorway, facing east, with lights off', + data: { + base64: 'dmlldyBmcm9tIGRvb3J3YXksIGZhY2luZyBlYXN0LCB3aXRoIGxpZ2h0cyBvZmY=', + }, + }, + { + '@id': 'view-2', + 'mime-type': 'image/png', + filename: 'IMG1092349.png', + lastmod_time: '2018-12-24 18:25:49Z', + description: 'view with lamp in the background', + data: { + base64: 'dmlldyB3aXRoIGxhbXAgaW4gdGhlIGJhY2tncm91bmQ=', + }, + }, + ], + } + + const invitation = OutOfBandInvitation.fromJson(json) + expect(invitation).toBeDefined() + expect(invitation).toBeInstanceOf(OutOfBandInvitation) + expect(invitation.appendedAttachments).toBeDefined() + expect(invitation.appendedAttachments?.length).toEqual(2) + expect(invitation.getAppendedAttachmentById('view-1')).toEqual( + new Attachment({ + id: 'view-1', + mimeType: 'image/png', + filename: 'IMG1092348.png', + lastmodTime: new Date('2018-12-24 18:24:07Z'), + description: 'view from doorway, facing east, with lights off', + data: { + base64: 'dmlldyBmcm9tIGRvb3J3YXksIGZhY2luZyBlYXN0LCB3aXRoIGxpZ2h0cyBvZmY=', + }, + }) + ) + expect(invitation.getAppendedAttachmentById('view-2')).toEqual( + new Attachment({ + id: 'view-2', + mimeType: 'image/png', + filename: 'IMG1092349.png', + lastmodTime: new Date('2018-12-24 18:25:49Z'), + description: 'view with lamp in the background', + data: { + base64: 'dmlldyB3aXRoIGxhbXAgaW4gdGhlIGJhY2tncm91bmQ=', + }, + }) + ) + }) + test('throw validation error when services attribute is empty', () => { const json = { '@type': 'https://didcomm.org/out-of-band/1.1/invitation', diff --git a/packages/core/src/modules/oob/__tests__/helpers.test.ts b/packages/core/src/modules/oob/__tests__/helpers.test.ts index e1920f4cae..cc501335b6 100644 --- a/packages/core/src/modules/oob/__tests__/helpers.test.ts +++ b/packages/core/src/modules/oob/__tests__/helpers.test.ts @@ -1,3 +1,4 @@ +import { Attachment } from '../../../decorators/attachment/Attachment' import { JsonTransformer } from '../../../utils' import { ConnectionInvitationMessage } from '../../connections' import { DidCommV1Service } from '../../dids' @@ -13,10 +14,23 @@ describe('convertToNewInvitation', () => { recipientKeys: ['8HH5gYEeNc3z7PYXmd54d4x6qAfCNrqQqEB3nS7Zfu7K'], serviceEndpoint: 'https://my-agent.com', routingKeys: ['6fioC1zcDPyPEL19pXRS2E4iJ46zH7xP6uSgAaPdwDrx'], + appendedAttachments: [ + new Attachment({ + id: 'attachment-1', + mimeType: 'text/plain', + description: 'attachment description', + filename: 'test.jpg', + data: { + json: { + text: 'sample', + value: 1, + }, + }, + }), + ], }) const oobInvitation = convertToNewInvitation(connectionInvitation) - expect(oobInvitation).toMatchObject({ id: 'd88ff8fd-6c43-4683-969e-11a87a572cf2', imageUrl: 'https://my-image.com', @@ -29,6 +43,15 @@ describe('convertToNewInvitation', () => { serviceEndpoint: 'https://my-agent.com', }, ], + appendedAttachments: [ + { + id: 'attachment-1', + description: 'attachment description', + filename: 'test.jpg', + mimeType: 'text/plain', + data: { json: { text: 'sample', value: 1 } }, + }, + ], }) }) @@ -38,6 +61,20 @@ describe('convertToNewInvitation', () => { imageUrl: 'https://my-image.com', label: 'a-label', did: 'did:sov:a-did', + appendedAttachments: [ + new Attachment({ + id: 'attachment-1', + mimeType: 'text/plain', + description: 'attachment description', + filename: 'test.jpg', + data: { + json: { + text: 'sample', + value: 1, + }, + }, + }), + ], }) const oobInvitation = convertToNewInvitation(connectionInvitation) @@ -47,6 +84,15 @@ describe('convertToNewInvitation', () => { imageUrl: 'https://my-image.com', label: 'a-label', services: ['did:sov:a-did'], + appendedAttachments: [ + { + id: 'attachment-1', + description: 'attachment description', + filename: 'test.jpg', + mimeType: 'text/plain', + data: { json: { text: 'sample', value: 1 } }, + }, + ], }) }) diff --git a/packages/core/src/modules/oob/helpers.ts b/packages/core/src/modules/oob/helpers.ts index f8a8a38d0b..e3677ee76d 100644 --- a/packages/core/src/modules/oob/helpers.ts +++ b/packages/core/src/modules/oob/helpers.ts @@ -26,6 +26,7 @@ export function convertToNewInvitation(oldInvitation: ConnectionInvitationMessag id: oldInvitation.id, label: oldInvitation.label, imageUrl: oldInvitation.imageUrl, + appendedAttachments: oldInvitation.appendedAttachments, accept: ['didcomm/aip1', 'didcomm/aip2;env=rfc19'], services: [service], handshakeProtocols: [HandshakeProtocol.Connections], @@ -45,6 +46,7 @@ export function convertToOldInvitation(newInvitation: OutOfBandInvitation) { label: newInvitation.label, did: service, imageUrl: newInvitation.imageUrl, + appendedAttachments: newInvitation.appendedAttachments, } } else { options = { @@ -54,6 +56,7 @@ export function convertToOldInvitation(newInvitation: OutOfBandInvitation) { routingKeys: service.routingKeys?.map(didKeyToVerkey), serviceEndpoint: service.serviceEndpoint, imageUrl: newInvitation.imageUrl, + appendedAttachments: newInvitation.appendedAttachments, } } diff --git a/packages/core/src/modules/oob/messages/OutOfBandInvitation.ts b/packages/core/src/modules/oob/messages/OutOfBandInvitation.ts index db7967dc76..f07af89292 100644 --- a/packages/core/src/modules/oob/messages/OutOfBandInvitation.ts +++ b/packages/core/src/modules/oob/messages/OutOfBandInvitation.ts @@ -26,6 +26,7 @@ export interface OutOfBandInvitationOptions { handshakeProtocols?: HandshakeProtocol[] services: Array imageUrl?: string + appendedAttachments?: Attachment[] } export class OutOfBandInvitation extends AgentMessage { @@ -41,6 +42,7 @@ export class OutOfBandInvitation extends AgentMessage { this.handshakeProtocols = options.handshakeProtocols this.services = options.services this.imageUrl = options.imageUrl + this.appendedAttachments = options.appendedAttachments } }