Skip to content

Commit

Permalink
feat(core)!: metadata on records (#505)
Browse files Browse the repository at this point in the history
BREAKING CHANGE: credentialRecord.credentialMetadata has been replaced by credentialRecord.metadata.

Signed-off-by: Berend Sliedrecht <[email protected]>
  • Loading branch information
berendsliedrecht authored Nov 12, 2021
1 parent febfb05 commit c92393a
Show file tree
Hide file tree
Showing 14 changed files with 368 additions and 89 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,26 @@ describe('CredentialRecord', () => {
value: '25',
}),
],
metadata: {
credentialDefinitionId: 'Th7MpTaRZVRYnPiabds81Y:3:CL:17:TAG',
schemaId: 'TL1EaPFCZ8Si5aUrqScBDt:2:test-schema-1599055118161:1.0',
},
})

const credentialInfo = credentialRecord.getCredentialInfo()
expect(credentialInfo?.claims).toEqual({
age: '25',
})
expect(credentialInfo?.metadata).toEqual({
credentialRecord.metadata.set('indyCredential', {
credentialDefinitionId: 'Th7MpTaRZVRYnPiabds81Y:3:CL:17:TAG',
schemaId: 'TL1EaPFCZ8Si5aUrqScBDt:2:test-schema-1599055118161:1.0',
})

const credentialInfo = credentialRecord.getCredentialInfo()

expect(credentialInfo).toEqual({
claims: {
age: '25',
},
metadata: {
indyCredential: {
credentialDefinitionId: 'Th7MpTaRZVRYnPiabds81Y:3:CL:17:TAG',
schemaId: 'TL1EaPFCZ8Si5aUrqScBDt:2:test-schema-1599055118161:1.0',
},
},
})
})
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ import type { ConnectionService } from '../../connections/services/ConnectionSer
import type { StoreCredentialOptions } from '../../indy/services/IndyHolderService'
import type { CredentialStateChangedEvent } from '../CredentialEvents'
import type { CredentialPreviewAttribute } from '../messages'
import type { CredentialRecordMetadata, CustomCredentialTags } from '../repository/CredentialRecord'
import type { IndyCredentialMetadata } from '../models/CredentialInfo'
import type { CustomCredentialTags } from '../repository/CredentialRecord'
import type { CredentialOfferTemplate } from '../services'

import { getAgentConfig, getMockConnection, mockFunction } from '../../../../tests/helpers'
Expand Down Expand Up @@ -98,7 +99,7 @@ const mockCredentialRecord = ({
}: {
state?: CredentialState
requestMessage?: RequestCredentialMessage
metadata?: CredentialRecordMetadata
metadata?: IndyCredentialMetadata & { indyRequest: Record<string, unknown> }
tags?: CustomCredentialTags
threadId?: string
connectionId?: string
Expand All @@ -111,17 +112,34 @@ const mockCredentialRecord = ({
offerAttachments: [offerAttachment],
})

return new CredentialRecord({
const credentialRecord = new CredentialRecord({
offerMessage,
id,
credentialAttributes: credentialAttributes || credentialPreview.attributes,
requestMessage,
metadata,
state: state || CredentialState.OfferSent,
threadId: threadId ?? offerMessage.id,
connectionId: connectionId ?? '123',
tags,
})

if (metadata?.indyRequest) {
credentialRecord.metadata.set('indyRequest', { ...metadata.indyRequest })
}

if (metadata?.schemaId) {
credentialRecord.metadata.add('indyCredential', {
schemaId: metadata.schemaId,
})
}

if (metadata?.credentialDefinitionId) {
credentialRecord.metadata.add('indyCredential', {
credentialDefinitionId: metadata.credentialDefinitionId,
})
}

return credentialRecord
}

describe('CredentialService', () => {
Expand Down Expand Up @@ -320,8 +338,8 @@ describe('CredentialService', () => {
// then
expect(repositoryUpdateSpy).toHaveBeenCalledTimes(1)
const [[updatedCredentialRecord]] = repositoryUpdateSpy.mock.calls
expect(updatedCredentialRecord).toMatchObject({
metadata: { requestMetadata: { cred_req: 'meta-data' } },
expect(updatedCredentialRecord.toJSON()).toMatchObject({
metadata: { indyRequest: { cred_req: 'meta-data' } },
state: CredentialState.RequestSent,
})
})
Expand Down Expand Up @@ -603,7 +621,7 @@ describe('CredentialService', () => {
requestMessage: new RequestCredentialMessage({
requestAttachments: [requestAttachment],
}),
metadata: { requestMetadata: { cred_req: 'meta-data' } },
metadata: { indyRequest: { cred_req: 'meta-data' } },
})

const credentialResponse = new IssueCredentialMessage({
Expand Down Expand Up @@ -725,7 +743,7 @@ describe('CredentialService', () => {
Promise.resolve(
mockCredentialRecord({
state,
metadata: { requestMetadata: { cred_req: 'meta-data' } },
metadata: { indyRequest: { cred_req: 'meta-data' } },
})
)
)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { Attachment } from '../../../decorators/attachment/Attachment'

export interface CredentialInfoOptions {
metadata?: IndyCredentialMetadata
metadata?: IndyCredentialMetadata | null
claims: Record<string, string>
attachments?: Attachment[]
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,14 @@ import { AriesFrameworkError } from '../../../error'
import { BaseRecord } from '../../../storage/BaseRecord'
import { uuid } from '../../../utils/uuid'
import {
ProposeCredentialMessage,
CredentialPreviewAttribute,
IssueCredentialMessage,
RequestCredentialMessage,
OfferCredentialMessage,
CredentialPreviewAttribute,
ProposeCredentialMessage,
RequestCredentialMessage,
} from '../messages'
import { CredentialInfo } from '../models/CredentialInfo'

export interface CredentialRecordMetadata {
requestMetadata?: Record<string, unknown>
credentialDefinitionId?: string
schemaId?: string
}

export interface CredentialRecordProps {
id?: string
createdAt?: Date
Expand All @@ -31,7 +25,6 @@ export interface CredentialRecordProps {
threadId: string

credentialId?: string
metadata?: CredentialRecordMetadata
tags?: CustomCredentialTags
proposalMessage?: ProposeCredentialMessage
offerMessage?: OfferCredentialMessage
Expand All @@ -55,7 +48,6 @@ export class CredentialRecord extends BaseRecord<DefaultCredentialTags, CustomCr
public threadId!: string
public credentialId?: string
public state!: CredentialState
public metadata!: CredentialRecordMetadata
public autoAcceptCredential?: AutoAcceptCredential

// message data
Expand Down Expand Up @@ -85,7 +77,6 @@ export class CredentialRecord extends BaseRecord<DefaultCredentialTags, CustomCr
this.createdAt = props.createdAt ?? new Date()
this.state = props.state
this.connectionId = props.connectionId
this.metadata = props.metadata ?? {}
this.credentialId = props.credentialId
this.threadId = props.threadId
this._tags = props.tags ?? {}
Expand Down Expand Up @@ -124,10 +115,7 @@ export class CredentialRecord extends BaseRecord<DefaultCredentialTags, CustomCr
return new CredentialInfo({
claims,
attachments: this.linkedAttachments,
metadata: {
credentialDefinitionId: this.metadata.credentialDefinitionId,
schemaId: this.metadata.schemaId,
},
metadata: this.metadata.getAll(),
})
}

Expand Down
57 changes: 39 additions & 18 deletions packages/core/src/modules/credentials/services/CredentialService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import type { ConnectionRecord } from '../../connections'
import type { AutoAcceptCredential } from '../CredentialAutoAcceptType'
import type { CredentialStateChangedEvent } from '../CredentialEvents'
import type { ProposeCredentialMessageOptions } from '../messages'
import type { CredReqMetadata } from 'indy-sdk'

import { scoped, Lifecycle } from 'tsyringe'

Expand Down Expand Up @@ -105,6 +106,13 @@ export class CredentialService {
credentialAttributes: proposalMessage.credentialProposal?.attributes,
autoAcceptCredential: config?.autoAcceptCredential,
})

// Set the metadata
credentialRecord.metadata.set('indyCredential', {
schemaId: options.schemaId,
credentialDefinintionId: options.credentialDefinitionId,
})

await this.credentialRepository.save(credentialRecord)
this.eventEmitter.emit<CredentialStateChangedEvent>({
type: CredentialEventTypes.CredentialStateChanged,
Expand Down Expand Up @@ -187,6 +195,11 @@ export class CredentialService {
state: CredentialState.ProposalReceived,
})

credentialRecord.metadata.set('indyCredential', {
schemaId: proposalMessage.schemaId,
credentialDefinitionId: proposalMessage.credentialDefinitionId,
})

// Assert
this.connectionService.assertConnectionOrServiceDecorator(messageContext)

Expand Down Expand Up @@ -244,8 +257,10 @@ export class CredentialService {

credentialRecord.offerMessage = credentialOfferMessage
credentialRecord.credentialAttributes = preview.attributes
credentialRecord.metadata.credentialDefinitionId = credOffer.cred_def_id
credentialRecord.metadata.schemaId = credOffer.schema_id
credentialRecord.metadata.set('indyCredential', {
schemaId: credOffer.schema_id,
credentialDefinitionId: credOffer.cred_def_id,
})
credentialRecord.linkedAttachments = attachments?.filter((attachment) => isLinkedAttachment(attachment))
credentialRecord.autoAcceptCredential =
credentialTemplate.autoAcceptCredential ?? credentialRecord.autoAcceptCredential
Expand Down Expand Up @@ -302,14 +317,15 @@ export class CredentialService {
offerMessage: credentialOfferMessage,
credentialAttributes: credentialPreview.attributes,
linkedAttachments: linkedAttachments?.map((linkedAttachments) => linkedAttachments.attachment),
metadata: {
credentialDefinitionId: credOffer.cred_def_id,
schemaId: credOffer.schema_id,
},
state: CredentialState.OfferSent,
autoAcceptCredential: credentialTemplate.autoAcceptCredential,
})

credentialRecord.metadata.set('indyCredential', {
credentialDefinitionId: credOffer.cred_def_id,
schemaId: credOffer.schema_id,
})

await this.credentialRepository.save(credentialRecord)
this.eventEmitter.emit<CredentialStateChangedEvent>({
type: CredentialEventTypes.CredentialStateChanged,
Expand Down Expand Up @@ -357,11 +373,13 @@ export class CredentialService {
})

credentialRecord.offerMessage = credentialOfferMessage
credentialRecord.linkedAttachments = credentialOfferMessage.attachments?.filter((attachment) =>
isLinkedAttachment(attachment)
)
credentialRecord.metadata.credentialDefinitionId = indyCredentialOffer.cred_def_id
credentialRecord.metadata.schemaId = indyCredentialOffer.schema_id
credentialRecord.linkedAttachments = credentialOfferMessage.attachments?.filter(isLinkedAttachment)

credentialRecord.metadata.set('indyCredential', {
schemaId: indyCredentialOffer.schema_id,
credentialDefinitionId: indyCredentialOffer.cred_def_id,
})

await this.updateState(credentialRecord, CredentialState.OfferReceived)
} catch {
// No credential record exists with thread id
Expand All @@ -370,13 +388,14 @@ export class CredentialService {
threadId: credentialOfferMessage.id,
offerMessage: credentialOfferMessage,
credentialAttributes: credentialOfferMessage.credentialPreview.attributes,
metadata: {
credentialDefinitionId: indyCredentialOffer.cred_def_id,
schemaId: indyCredentialOffer.schema_id,
},
state: CredentialState.OfferReceived,
})

credentialRecord.metadata.set('indyCredential', {
credentialDefinitionId: indyCredentialOffer.cred_def_id,
schemaId: indyCredentialOffer.schema_id,
})

// Assert
this.connectionService.assertConnectionOrServiceDecorator(messageContext)

Expand Down Expand Up @@ -440,7 +459,7 @@ export class CredentialService {
})
credentialRequest.setThread({ threadId: credentialRecord.threadId })

credentialRecord.metadata.requestMetadata = credReqMetadata
credentialRecord.metadata.set('indyRequest', credReqMetadata)
credentialRecord.requestMessage = credentialRequest
credentialRecord.autoAcceptCredential = options?.autoAcceptCredential ?? credentialRecord.autoAcceptCredential

Expand Down Expand Up @@ -604,7 +623,9 @@ export class CredentialService {
previousSentMessage: credentialRecord.requestMessage,
})

if (!credentialRecord.metadata.requestMetadata) {
const credentialRequestMetadata = credentialRecord.metadata.get<CredReqMetadata>('indyRequest')

if (!credentialRequestMetadata) {
throw new AriesFrameworkError(`Missing required request metadata for credential with id ${credentialRecord.id}`)
}

Expand All @@ -619,7 +640,7 @@ export class CredentialService {

const credentialId = await this.indyHolderService.storeCredential({
credentialId: uuid(),
credentialRequestMetadata: credentialRecord.metadata.requestMetadata,
credentialRequestMetadata,
credential: indyCredential,
credentialDefinition,
})
Expand Down
8 changes: 8 additions & 0 deletions packages/core/src/storage/BaseRecord.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import { Exclude, Type } from 'class-transformer'

import { JsonTransformer } from '../utils/JsonTransformer'
import { MetadataTransformer } from '../utils/transformers'

import { Metadata } from './Metadata'

export type TagValue = string | boolean | undefined | Array<string>
export type TagsBase = {
Expand All @@ -9,6 +12,7 @@ export type TagsBase = {
}

export type Tags<DefaultTags extends TagsBase, CustomTags extends TagsBase> = CustomTags & DefaultTags

export type RecordTags<Record extends BaseRecord> = ReturnType<Record['getTags']>

export abstract class BaseRecord<DefaultTags extends TagsBase = TagsBase, CustomTags extends TagsBase = TagsBase> {
Expand All @@ -26,6 +30,10 @@ export abstract class BaseRecord<DefaultTags extends TagsBase = TagsBase, Custom
public readonly type = BaseRecord.type
public static readonly type: string = 'BaseRecord'

/** @inheritdoc {Metadata#Metadata} */
@MetadataTransformer()
public metadata: Metadata = new Metadata({})

/**
* Get all tags. This is includes custom and default tags
* @returns tags object
Expand Down
Loading

0 comments on commit c92393a

Please sign in to comment.