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]>
berendsliedrecht authored Nov 12, 2021

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
1 parent febfb05 commit c92393a
Showing 14 changed files with 368 additions and 89 deletions.
Original file line number Diff line number Diff line change
@@ -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
@@ -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'
@@ -98,7 +99,7 @@ const mockCredentialRecord = ({
}: {
state?: CredentialState
requestMessage?: RequestCredentialMessage
metadata?: CredentialRecordMetadata
metadata?: IndyCredentialMetadata & { indyRequest: Record<string, unknown> }
tags?: CustomCredentialTags
threadId?: string
connectionId?: string
@@ -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', () => {
@@ -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,
})
})
@@ -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({
@@ -725,7 +743,7 @@ describe('CredentialService', () => {
Promise.resolve(
mockCredentialRecord({
state,
metadata: { requestMetadata: { cred_req: 'meta-data' } },
metadata: { indyRequest: { cred_req: 'meta-data' } },
})
)
)
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[]
}
Original file line number Diff line number Diff line change
@@ -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
@@ -31,7 +25,6 @@ export interface CredentialRecordProps {
threadId: string

credentialId?: string
metadata?: CredentialRecordMetadata
tags?: CustomCredentialTags
proposalMessage?: ProposeCredentialMessage
offerMessage?: OfferCredentialMessage
@@ -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
@@ -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 ?? {}
@@ -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(),
})
}

57 changes: 39 additions & 18 deletions packages/core/src/modules/credentials/services/CredentialService.ts
Original file line number Diff line number Diff line change
@@ -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'

@@ -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,
@@ -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)

@@ -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
@@ -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,
@@ -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
@@ -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)

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

@@ -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}`)
}

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

const credentialId = await this.indyHolderService.storeCredential({
credentialId: uuid(),
credentialRequestMetadata: credentialRecord.metadata.requestMetadata,
credentialRequestMetadata,
credential: indyCredential,
credentialDefinition,
})
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 = {
@@ -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> {
@@ -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
Loading

0 comments on commit c92393a

Please sign in to comment.