From 4d7d3c1502d5eafa2b884a4a84934e072fe70ea6 Mon Sep 17 00:00:00 2001 From: Mo <10432473+morrieinmaas@users.noreply.github.com> Date: Thu, 16 Dec 2021 12:24:39 +0100 Subject: [PATCH] feat: generic attachment handler (#578) Signed-off-by: morrieinmaas Co-authored-by: annelein --- .../decorators/attachment/Attachment.test.ts | 38 ++++++++++++++++--- .../src/decorators/attachment/Attachment.ts | 15 ++++++++ .../CredentialResponseCoordinator.ts | 2 +- .../__tests__/CredentialService.test.ts | 2 +- .../messages/IssueCredentialMessage.ts | 8 +--- .../messages/OfferCredentialMessage.ts | 8 +--- .../messages/RequestCredentialMessage.ts | 9 +---- .../credentials/services/CredentialService.ts | 12 +++--- .../proofs/messages/PresentationMessage.ts | 8 +--- .../messages/RequestPresentationMessage.ts | 9 +---- .../modules/proofs/services/ProofService.ts | 8 ++-- 11 files changed, 65 insertions(+), 54 deletions(-) diff --git a/packages/core/src/decorators/attachment/Attachment.test.ts b/packages/core/src/decorators/attachment/Attachment.test.ts index 8f21c58ccd..b983de2a53 100644 --- a/packages/core/src/decorators/attachment/Attachment.test.ts +++ b/packages/core/src/decorators/attachment/Attachment.test.ts @@ -1,6 +1,7 @@ -import { JsonTransformer } from '../..' +import { JsonEncoder } from '../../utils/JsonEncoder' +import { JsonTransformer } from '../../utils/JsonTransformer' -import { Attachment } from './Attachment' +import { Attachment, AttachmentData } from './Attachment' const mockJson = { '@id': 'ceffce22-6471-43e4-8945-b604091981c9', @@ -17,6 +18,18 @@ const mockJson = { }, } +const mockJsonBase64 = { + '@id': 'ceffce22-6471-43e4-8945-b604091981c9', + description: 'A small picture of a cat', + filename: 'cat.png', + 'mime-type': 'text/plain', + lastmod_time: new Date(), + byte_count: 9200, + data: { + base64: JsonEncoder.toBase64(mockJson.data.json), + }, +} + const id = 'ceffce22-6471-43e4-8945-b604091981c9' const description = 'A small picture of a cat' const filename = 'cat.png' @@ -29,6 +42,7 @@ const data = { }, sha256: '00d7b2068a0b237f14a7979bbfc01ad62f60792e459467bfc4a7d3b9a6dbbe3e', } +const dataInstance = new AttachmentData(data) describe('Decorators | Attachment', () => { it('should correctly transform Json to Attachment class', () => { @@ -39,7 +53,7 @@ describe('Decorators | Attachment', () => { expect(decorator.filename).toBe(mockJson.filename) expect(decorator.lastmodTime).toEqual(mockJson.lastmod_time) expect(decorator.byteCount).toEqual(mockJson.byte_count) - expect(decorator.data).toEqual(mockJson.data) + expect(decorator.data).toMatchObject(mockJson.data) }) it('should correctly transform Attachment class to Json', () => { @@ -50,7 +64,7 @@ describe('Decorators | Attachment', () => { mimeType, lastmodTime, byteCount, - data, + data: dataInstance, }) const json = JsonTransformer.toJSON(decorator) @@ -64,6 +78,20 @@ describe('Decorators | Attachment', () => { data, } - expect(json).toEqual(transformed) + expect(json).toMatchObject(transformed) + }) + + it('should return the data correctly if only JSON exists', () => { + const decorator = JsonTransformer.fromJSON(mockJson, Attachment) + + const gotData = decorator.data.getDataAsJson() + expect(decorator.data.json).toEqual(gotData) + }) + + it('should return the data correctly if only Base64 exists', () => { + const decorator = JsonTransformer.fromJSON(mockJsonBase64, Attachment) + + const gotData = decorator.data.getDataAsJson() + expect(mockJson.data.json).toEqual(gotData) }) }) diff --git a/packages/core/src/decorators/attachment/Attachment.ts b/packages/core/src/decorators/attachment/Attachment.ts index 5dea2fb778..24044e9d05 100644 --- a/packages/core/src/decorators/attachment/Attachment.ts +++ b/packages/core/src/decorators/attachment/Attachment.ts @@ -11,6 +11,8 @@ import { ValidateNested, } from 'class-validator' +import { AriesFrameworkError } from '../../error' +import { JsonEncoder } from '../../utils/JsonEncoder' import { uuid } from '../../utils/uuid' export interface AttachmentOptions { @@ -45,6 +47,19 @@ export class AttachmentData { } } + /* + * Helper function returning JSON representation of attachment data (if present). Tries to obtain the data from .base64 or .json, throws an error otherwise + */ + public getDataAsJson(): T { + if (typeof this.base64 === 'string') { + return JsonEncoder.fromBase64(this.base64) as T + } else if (this.json) { + return this.json as T + } else { + throw new AriesFrameworkError('No attachment data found in `json` or `base64` data fields.') + } + } + /** * Base64-encoded data, when representing arbitrary content inline instead of via links. Optional. */ diff --git a/packages/core/src/modules/credentials/CredentialResponseCoordinator.ts b/packages/core/src/modules/credentials/CredentialResponseCoordinator.ts index d076596061..a463555665 100644 --- a/packages/core/src/modules/credentials/CredentialResponseCoordinator.ts +++ b/packages/core/src/modules/credentials/CredentialResponseCoordinator.ts @@ -133,7 +133,7 @@ export class CredentialResponseCoordinator { const indyCredential = credentialRecord.credentialMessage.indyCredential if (!indyCredential) { - this.agentConfig.logger.error(`Missing required base64 encoded attachment data for credential`) + this.agentConfig.logger.error(`Missing required base64 or json encoded attachment data for credential`) return false } diff --git a/packages/core/src/modules/credentials/__tests__/CredentialService.test.ts b/packages/core/src/modules/credentials/__tests__/CredentialService.test.ts index c7eba091c2..411056df7e 100644 --- a/packages/core/src/modules/credentials/__tests__/CredentialService.test.ts +++ b/packages/core/src/modules/credentials/__tests__/CredentialService.test.ts @@ -587,7 +587,7 @@ describe('CredentialService', () => { }) ) ).rejects.toThrowError( - `Missing required base64 encoded attachment data for credential request with thread id ${threadId}` + `Missing required base64 or json encoded attachment data for credential request with thread id ${threadId}` ) }) diff --git a/packages/core/src/modules/credentials/messages/IssueCredentialMessage.ts b/packages/core/src/modules/credentials/messages/IssueCredentialMessage.ts index aab5a37e84..c5e4689137 100644 --- a/packages/core/src/modules/credentials/messages/IssueCredentialMessage.ts +++ b/packages/core/src/modules/credentials/messages/IssueCredentialMessage.ts @@ -5,7 +5,6 @@ import { Equals, IsArray, IsInstance, IsOptional, IsString, ValidateNested } fro import { AgentMessage } from '../../../agent/AgentMessage' import { Attachment } from '../../../decorators/attachment/Attachment' -import { JsonEncoder } from '../../../utils/JsonEncoder' export const INDY_CREDENTIAL_ATTACHMENT_ID = 'libindy-cred-0' @@ -48,13 +47,8 @@ export class IssueCredentialMessage extends AgentMessage { public get indyCredential(): Cred | null { const attachment = this.credentialAttachments.find((attachment) => attachment.id === INDY_CREDENTIAL_ATTACHMENT_ID) - // Return null if attachment is not found - if (!attachment?.data?.base64) { - return null - } - // Extract credential from attachment - const credentialJson = JsonEncoder.fromBase64(attachment.data.base64) + const credentialJson = attachment?.data?.getDataAsJson() ?? null return credentialJson } diff --git a/packages/core/src/modules/credentials/messages/OfferCredentialMessage.ts b/packages/core/src/modules/credentials/messages/OfferCredentialMessage.ts index 5736a9a26f..c1b1953e8d 100644 --- a/packages/core/src/modules/credentials/messages/OfferCredentialMessage.ts +++ b/packages/core/src/modules/credentials/messages/OfferCredentialMessage.ts @@ -5,7 +5,6 @@ import { Equals, IsArray, IsInstance, IsOptional, IsString, ValidateNested } fro import { AgentMessage } from '../../../agent/AgentMessage' import { Attachment } from '../../../decorators/attachment/Attachment' -import { JsonEncoder } from '../../../utils/JsonEncoder' import { CredentialPreview } from './CredentialPreview' @@ -63,13 +62,8 @@ export class OfferCredentialMessage extends AgentMessage { public get indyCredentialOffer(): CredOffer | null { const attachment = this.offerAttachments.find((attachment) => attachment.id === INDY_CREDENTIAL_OFFER_ATTACHMENT_ID) - // Return null if attachment is not found - if (!attachment?.data?.base64) { - return null - } - // Extract credential offer from attachment - const credentialOfferJson = JsonEncoder.fromBase64(attachment.data.base64) + const credentialOfferJson = attachment?.data?.getDataAsJson() ?? null return credentialOfferJson } diff --git a/packages/core/src/modules/credentials/messages/RequestCredentialMessage.ts b/packages/core/src/modules/credentials/messages/RequestCredentialMessage.ts index 2600a44cac..e377f6b5f8 100644 --- a/packages/core/src/modules/credentials/messages/RequestCredentialMessage.ts +++ b/packages/core/src/modules/credentials/messages/RequestCredentialMessage.ts @@ -5,7 +5,6 @@ import { Equals, IsArray, IsInstance, IsOptional, IsString, ValidateNested } fro import { AgentMessage } from '../../../agent/AgentMessage' import { Attachment } from '../../../decorators/attachment/Attachment' -import { JsonEncoder } from '../../../utils/JsonEncoder' export const INDY_CREDENTIAL_REQUEST_ATTACHMENT_ID = 'libindy-cred-request-0' @@ -49,14 +48,8 @@ export class RequestCredentialMessage extends AgentMessage { const attachment = this.requestAttachments.find( (attachment) => attachment.id === INDY_CREDENTIAL_REQUEST_ATTACHMENT_ID ) - - // Return null if attachment is not found - if (!attachment?.data?.base64) { - return null - } - // Extract proof request from attachment - const credentialReqJson = JsonEncoder.fromBase64(attachment.data.base64) + const credentialReqJson = attachment?.data?.getDataAsJson() ?? null return credentialReqJson } diff --git a/packages/core/src/modules/credentials/services/CredentialService.ts b/packages/core/src/modules/credentials/services/CredentialService.ts index 6537c1a266..30ca9d6fab 100644 --- a/packages/core/src/modules/credentials/services/CredentialService.ts +++ b/packages/core/src/modules/credentials/services/CredentialService.ts @@ -357,7 +357,7 @@ export class CredentialService { const indyCredentialOffer = credentialOfferMessage.indyCredentialOffer if (!indyCredentialOffer) { throw new CredentialProblemReportError( - `Missing required base64 encoded attachment data for credential offer with thread id ${credentialOfferMessage.threadId}`, + `Missing required base64 or json encoded attachment data for credential offer with thread id ${credentialOfferMessage.threadId}`, { problemCode: CredentialProblemReportReason.IssuanceAbandoned } ) } @@ -433,7 +433,7 @@ export class CredentialService { if (!credentialOffer) { throw new CredentialProblemReportError( - `Missing required base64 encoded attachment data for credential offer with thread id ${credentialRecord.threadId}`, + `Missing required base64 or json encoded attachment data for credential offer with thread id ${credentialRecord.threadId}`, { problemCode: CredentialProblemReportReason.IssuanceAbandoned } ) } @@ -494,7 +494,7 @@ export class CredentialService { if (!indyCredentialRequest) { throw new CredentialProblemReportError( - `Missing required base64 encoded attachment data for credential request with thread id ${credentialRequestMessage.threadId}`, + `Missing required base64 or json encoded attachment data for credential request with thread id ${credentialRequestMessage.threadId}`, { problemCode: CredentialProblemReportReason.IssuanceAbandoned } ) } @@ -554,7 +554,7 @@ export class CredentialService { const indyCredentialOffer = offerMessage?.indyCredentialOffer if (!indyCredentialOffer) { throw new CredentialProblemReportError( - `Missing required base64 encoded attachment data for credential offer with thread id ${credentialRecord.threadId}`, + `Missing required base64 or json encoded attachment data for credential offer with thread id ${credentialRecord.threadId}`, { problemCode: CredentialProblemReportReason.IssuanceAbandoned } ) } @@ -563,7 +563,7 @@ export class CredentialService { const indyCredentialRequest = requestMessage?.indyCredentialRequest if (!indyCredentialRequest) { throw new CredentialProblemReportError( - `Missing required base64 encoded attachment data for credential request with thread id ${credentialRecord.threadId}`, + `Missing required base64 or json encoded attachment data for credential request with thread id ${credentialRecord.threadId}`, { problemCode: CredentialProblemReportReason.IssuanceAbandoned } ) } @@ -641,7 +641,7 @@ export class CredentialService { const indyCredential = issueCredentialMessage.indyCredential if (!indyCredential) { throw new CredentialProblemReportError( - `Missing required base64 encoded attachment data for credential with thread id ${issueCredentialMessage.threadId}`, + `Missing required base64 or json encoded attachment data for credential with thread id ${issueCredentialMessage.threadId}`, { problemCode: CredentialProblemReportReason.IssuanceAbandoned } ) } diff --git a/packages/core/src/modules/proofs/messages/PresentationMessage.ts b/packages/core/src/modules/proofs/messages/PresentationMessage.ts index 65bd1d3aa2..04f56ac045 100644 --- a/packages/core/src/modules/proofs/messages/PresentationMessage.ts +++ b/packages/core/src/modules/proofs/messages/PresentationMessage.ts @@ -5,7 +5,6 @@ import { Equals, IsArray, IsString, ValidateNested, IsOptional, IsInstance } fro import { AgentMessage } from '../../../agent/AgentMessage' import { Attachment } from '../../../decorators/attachment/Attachment' -import { JsonEncoder } from '../../../utils/JsonEncoder' export const INDY_PROOF_ATTACHMENT_ID = 'libindy-presentation-0' @@ -60,12 +59,7 @@ export class PresentationMessage extends AgentMessage { public get indyProof(): IndyProof | null { const attachment = this.presentationAttachments.find((attachment) => attachment.id === INDY_PROOF_ATTACHMENT_ID) - // Return null if attachment is not found - if (!attachment?.data?.base64) { - return null - } - - const proofJson = JsonEncoder.fromBase64(attachment.data.base64) + const proofJson = attachment?.data?.getDataAsJson() ?? null return proofJson } diff --git a/packages/core/src/modules/proofs/messages/RequestPresentationMessage.ts b/packages/core/src/modules/proofs/messages/RequestPresentationMessage.ts index fad50b97cc..585e40b1ac 100644 --- a/packages/core/src/modules/proofs/messages/RequestPresentationMessage.ts +++ b/packages/core/src/modules/proofs/messages/RequestPresentationMessage.ts @@ -3,7 +3,6 @@ import { Equals, IsArray, IsString, ValidateNested, IsOptional, IsInstance } fro import { AgentMessage } from '../../../agent/AgentMessage' import { Attachment } from '../../../decorators/attachment/Attachment' -import { JsonEncoder } from '../../../utils/JsonEncoder' import { JsonTransformer } from '../../../utils/JsonTransformer' import { ProofRequest } from '../models' @@ -58,14 +57,8 @@ export class RequestPresentationMessage extends AgentMessage { const attachment = this.requestPresentationAttachments.find( (attachment) => attachment.id === INDY_PROOF_REQUEST_ATTACHMENT_ID ) - - // Return null if attachment is not found - if (!attachment?.data?.base64) { - return null - } - // Extract proof request from attachment - const proofRequestJson = JsonEncoder.fromBase64(attachment.data.base64) + const proofRequestJson = attachment?.data?.getDataAsJson() ?? null const proofRequest = JsonTransformer.fromJSON(proofRequestJson, ProofRequest) return proofRequest diff --git a/packages/core/src/modules/proofs/services/ProofService.ts b/packages/core/src/modules/proofs/services/ProofService.ts index 6f83404667..df131aab10 100644 --- a/packages/core/src/modules/proofs/services/ProofService.ts +++ b/packages/core/src/modules/proofs/services/ProofService.ts @@ -354,7 +354,7 @@ export class ProofService { // Assert attachment if (!proofRequest) { throw new PresentationProblemReportError( - `Missing required base64 encoded attachment data for presentation request with thread id ${proofRequestMessage.threadId}`, + `Missing required base64 or json encoded attachment data for presentation request with thread id ${proofRequestMessage.threadId}`, { problemCode: PresentationProblemReportReason.abandoned } ) } @@ -423,7 +423,7 @@ export class ProofService { const indyProofRequest = proofRecord.requestMessage?.indyProofRequest if (!indyProofRequest) { throw new PresentationProblemReportError( - `Missing required base64 encoded attachment data for presentation with thread id ${proofRecord.threadId}`, + `Missing required base64 or json encoded attachment data for presentation with thread id ${proofRecord.threadId}`, { problemCode: PresentationProblemReportReason.abandoned } ) } @@ -490,14 +490,14 @@ export class ProofService { if (!indyProofJson) { throw new PresentationProblemReportError( - `Missing required base64 encoded attachment data for presentation with thread id ${presentationMessage.threadId}`, + `Missing required base64 or json encoded attachment data for presentation with thread id ${presentationMessage.threadId}`, { problemCode: PresentationProblemReportReason.abandoned } ) } if (!indyProofRequest) { throw new PresentationProblemReportError( - `Missing required base64 encoded attachment data for presentation request with thread id ${presentationMessage.threadId}`, + `Missing required base64 or json encoded attachment data for presentation request with thread id ${presentationMessage.threadId}`, { problemCode: PresentationProblemReportReason.abandoned } ) }