Skip to content

Commit

Permalink
feat: generic attachment handler (#578)
Browse files Browse the repository at this point in the history
Signed-off-by: morrieinmaas <[email protected]>
Co-authored-by: annelein <[email protected]>
  • Loading branch information
morrieinmaas and Annelein authored Dec 16, 2021
1 parent cf69a54 commit 4d7d3c1
Show file tree
Hide file tree
Showing 11 changed files with 65 additions and 54 deletions.
38 changes: 33 additions & 5 deletions packages/core/src/decorators/attachment/Attachment.test.ts
Original file line number Diff line number Diff line change
@@ -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',
Expand All @@ -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'
Expand All @@ -29,6 +42,7 @@ const data = {
},
sha256: '00d7b2068a0b237f14a7979bbfc01ad62f60792e459467bfc4a7d3b9a6dbbe3e',
}
const dataInstance = new AttachmentData(data)

describe('Decorators | Attachment', () => {
it('should correctly transform Json to Attachment class', () => {
Expand All @@ -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', () => {
Expand All @@ -50,7 +64,7 @@ describe('Decorators | Attachment', () => {
mimeType,
lastmodTime,
byteCount,
data,
data: dataInstance,
})

const json = JsonTransformer.toJSON(decorator)
Expand All @@ -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)
})
})
15 changes: 15 additions & 0 deletions packages/core/src/decorators/attachment/Attachment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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>(): 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.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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}`
)
})

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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'

Expand Down Expand Up @@ -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<Cred>() ?? null

return credentialJson
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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'

Expand Down Expand Up @@ -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<CredOffer>() ?? null

return credentialOfferJson
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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'

Expand Down Expand Up @@ -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<CredReq>() ?? null

return credentialReqJson
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
)
}
Expand Down Expand Up @@ -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 }
)
}
Expand Down Expand Up @@ -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 }
)
}
Expand Down Expand Up @@ -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 }
)
}
Expand All @@ -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 }
)
}
Expand Down Expand Up @@ -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 }
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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'

Expand Down Expand Up @@ -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<IndyProof>() ?? null

return proofJson
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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'

Expand Down Expand Up @@ -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<ProofRequest>() ?? null
const proofRequest = JsonTransformer.fromJSON(proofRequestJson, ProofRequest)

return proofRequest
Expand Down
8 changes: 4 additions & 4 deletions packages/core/src/modules/proofs/services/ProofService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
)
}
Expand Down Expand Up @@ -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 }
)
}
Expand Down Expand Up @@ -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 }
)
}
Expand Down

0 comments on commit 4d7d3c1

Please sign in to comment.