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: generic attachment handler #578

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
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