Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(oob): allow to append attachments to invitations #926

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { Attachment } from './Attachment'
export function AttachmentDecorated<T extends BaseMessageConstructor>(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)
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down
Original file line number Diff line number Diff line change
@@ -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'
Expand Down Expand Up @@ -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({
Expand Down
Original file line number Diff line number Diff line change
@@ -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'
Expand All @@ -12,6 +14,7 @@ export interface BaseInvitationOptions {
id?: string
label: string
imageUrl?: string
appendedAttachments?: Attachment[]
}

export interface InlineInvitationOptions {
Expand Down Expand Up @@ -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
Expand Down
5 changes: 5 additions & 0 deletions packages/core/src/modules/oob/OutOfBandModule.ts
Original file line number Diff line number Diff line change
@@ -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'
Expand Down Expand Up @@ -54,6 +55,7 @@ export interface CreateOutOfBandInvitationConfig {
multiUseInvitation?: boolean
autoAcceptConnection?: boolean
routing?: Routing
appendedAttachments?: Attachment[]
}

export interface CreateLegacyInvitationConfig {
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -182,6 +186,7 @@ export class OutOfBandModule {
accept: didCommProfiles,
services,
handshakeProtocols,
appendedAttachments,
}
const outOfBandInvitation = new OutOfBandInvitation(options)

Expand Down
Original file line number Diff line number Diff line change
@@ -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'
Expand Down Expand Up @@ -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',
Expand Down
48 changes: 47 additions & 1 deletion packages/core/src/modules/oob/__tests__/helpers.test.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { Attachment } from '../../../decorators/attachment/Attachment'
import { JsonTransformer } from '../../../utils'
import { ConnectionInvitationMessage } from '../../connections'
import { DidCommV1Service } from '../../dids'
Expand All @@ -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',
Expand All @@ -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 } },
},
],
})
})

Expand All @@ -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)
Expand All @@ -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 } },
},
],
})
})

Expand Down
3 changes: 3 additions & 0 deletions packages/core/src/modules/oob/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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],
Expand All @@ -45,6 +46,7 @@ export function convertToOldInvitation(newInvitation: OutOfBandInvitation) {
label: newInvitation.label,
did: service,
imageUrl: newInvitation.imageUrl,
appendedAttachments: newInvitation.appendedAttachments,
}
} else {
options = {
Expand All @@ -54,6 +56,7 @@ export function convertToOldInvitation(newInvitation: OutOfBandInvitation) {
routingKeys: service.routingKeys?.map(didKeyToVerkey),
serviceEndpoint: service.serviceEndpoint,
imageUrl: newInvitation.imageUrl,
appendedAttachments: newInvitation.appendedAttachments,
}
}

Expand Down
2 changes: 2 additions & 0 deletions packages/core/src/modules/oob/messages/OutOfBandInvitation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export interface OutOfBandInvitationOptions {
handshakeProtocols?: HandshakeProtocol[]
services: Array<OutOfBandDidCommService | string>
imageUrl?: string
appendedAttachments?: Attachment[]
}

export class OutOfBandInvitation extends AgentMessage {
Expand All @@ -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
}
}

Expand Down