Skip to content

Commit

Permalink
feat(credentials): find didcomm message methods (#887)
Browse files Browse the repository at this point in the history
Signed-off-by: Timo Glastra <[email protected]>
  • Loading branch information
TimoGlastra authored Jun 20, 2022
1 parent c7766d0 commit dc12427
Show file tree
Hide file tree
Showing 6 changed files with 94 additions and 19 deletions.
40 changes: 40 additions & 0 deletions packages/core/src/modules/credentials/CredentialsModule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ import type {
ProposeCredentialOptions,
ServiceMap,
CreateOfferOptions,
FindOfferMessageReturn,
FindRequestMessageReturn,
FindCredentialMessageReturn,
FindProposalMessageReturn,
GetFormatDataReturn,
} from './CredentialsModuleOptions'
import type { CredentialFormat } from './formats'
Expand Down Expand Up @@ -70,6 +74,12 @@ export interface CredentialsModule<CFs extends CredentialFormat[], CSs extends C
findById(credentialRecordId: string): Promise<CredentialExchangeRecord | null>
deleteById(credentialRecordId: string, options?: DeleteCredentialOptions): Promise<void>
getFormatData(credentialRecordId: string): Promise<GetFormatDataReturn<CFs>>

// DidComm Message Records
findProposalMessage(credentialExchangeId: string): Promise<FindProposalMessageReturn<CSs>>
findOfferMessage(credentialExchangeId: string): Promise<FindOfferMessageReturn<CSs>>
findRequestMessage(credentialExchangeId: string): Promise<FindRequestMessageReturn<CSs>>
findCredentialMessage(credentialExchangeId: string): Promise<FindCredentialMessageReturn<CSs>>
}

@scoped(Lifecycle.ContainerScoped)
Expand Down Expand Up @@ -556,4 +566,34 @@ export class CredentialsModule<
const service = this.getService(credentialRecord.protocolVersion)
return service.delete(credentialRecord, options)
}

public async findProposalMessage(credentialExchangeId: string): Promise<FindProposalMessageReturn<CSs>> {
const service = await this.getServiceForCredentialExchangeId(credentialExchangeId)

return service.findProposalMessage(credentialExchangeId)
}

public async findOfferMessage(credentialExchangeId: string): Promise<FindOfferMessageReturn<CSs>> {
const service = await this.getServiceForCredentialExchangeId(credentialExchangeId)

return service.findOfferMessage(credentialExchangeId)
}

public async findRequestMessage(credentialExchangeId: string): Promise<FindRequestMessageReturn<CSs>> {
const service = await this.getServiceForCredentialExchangeId(credentialExchangeId)

return service.findRequestMessage(credentialExchangeId)
}

public async findCredentialMessage(credentialExchangeId: string): Promise<FindCredentialMessageReturn<CSs>> {
const service = await this.getServiceForCredentialExchangeId(credentialExchangeId)

return service.findCredentialMessage(credentialExchangeId)
}

private async getServiceForCredentialExchangeId(credentialExchangeId: string) {
const credentialExchangeRecord = await this.getById(credentialExchangeId)

return this.getService(credentialExchangeRecord.protocolVersion)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,13 @@ import type { CredentialService } from './services'
// re-export GetFormatDataReturn type from service, as it is also used in the module
export type { GetFormatDataReturn }

export type FindProposalMessageReturn<CSs extends CredentialService[]> = ReturnType<CSs[number]['findProposalMessage']>
export type FindOfferMessageReturn<CSs extends CredentialService[]> = ReturnType<CSs[number]['findOfferMessage']>
export type FindRequestMessageReturn<CSs extends CredentialService[]> = ReturnType<CSs[number]['findRequestMessage']>
export type FindCredentialMessageReturn<CSs extends CredentialService[]> = ReturnType<
CSs[number]['findCredentialMessage']
>

/**
* Get the supported protocol versions based on the provided credential services.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat
const message = new V1ProposeCredentialMessage({
...indyCredentialProposal,
id: credentialRecord.threadId,
credentialProposal,
credentialPreview: credentialProposal,
comment,
})

Expand Down Expand Up @@ -279,7 +279,7 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat
// NOTE: We set the credential attributes from the proposal on the record as we've 'accepted' them
// and can now use them to create the offer in the format services. It may be overwritten later on
// if the user provided other attributes in the credentialFormats array.
credentialRecord.credentialAttributes = proposalMessage.credentialProposal?.attributes
credentialRecord.credentialAttributes = proposalMessage.credentialPreview?.attributes

const { attachment, previewAttributes } = await this.formatService.acceptProposal({
attachId: INDY_CREDENTIAL_OFFER_ATTACHMENT_ID,
Expand Down Expand Up @@ -639,7 +639,7 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat
// Create message
const message = new V1ProposeCredentialMessage({
...indyCredentialProposal,
credentialProposal,
credentialPreview: credentialProposal,
comment,
})

Expand Down Expand Up @@ -926,7 +926,7 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat

// Do not auto accept if missing properties
if (!offerMessage || !offerMessage.credentialPreview) return false
if (!proposalMessage.credentialProposal || !proposalMessage.credentialDefinitionId) return false
if (!proposalMessage.credentialPreview || !proposalMessage.credentialDefinitionId) return false

const credentialOfferJson = offerMessage.indyCredentialOffer

Expand All @@ -936,7 +936,7 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat

// Check if preview values match
return arePreviewAttributesEqual(
proposalMessage.credentialProposal.attributes,
proposalMessage.credentialPreview.attributes,
offerMessage.credentialPreview.attributes
)
}
Expand All @@ -956,7 +956,7 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat

// Do not auto accept if missing properties
if (!offerMessage.credentialPreview) return false
if (!proposalMessage || !proposalMessage.credentialProposal || !proposalMessage.credentialDefinitionId) return false
if (!proposalMessage || !proposalMessage.credentialPreview || !proposalMessage.credentialDefinitionId) return false

const credentialOfferJson = offerMessage.indyCredentialOffer

Expand All @@ -966,7 +966,7 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat

// Check if preview values match
return arePreviewAttributesEqual(
proposalMessage.credentialProposal.attributes,
proposalMessage.credentialPreview.attributes,
offerMessage.credentialPreview.attributes
)
}
Expand Down Expand Up @@ -1073,7 +1073,7 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat
const indyCredential = credentialMessage?.indyCredential ?? undefined

return {
proposalAttributes: proposalMessage?.credentialProposal?.attributes,
proposalAttributes: proposalMessage?.credentialPreview?.attributes,
proposal: proposalMessage
? {
indy: indyProposal,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,13 @@ import { DidCommMessageRepository } from '../../../../../storage'
import { JsonTransformer } from '../../../../../utils'
import { CredentialState } from '../../../models/CredentialState'
import { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord'
import { V1CredentialPreview } from '../messages/V1CredentialPreview'
import { V1OfferCredentialMessage } from '../messages/V1OfferCredentialMessage'
import {
V1ProposeCredentialMessage,
V1RequestCredentialMessage,
V1IssueCredentialMessage,
V1OfferCredentialMessage,
V1CredentialPreview,
} from '../messages'

describe('v1 credentials', () => {
let faberAgent: Agent
Expand Down Expand Up @@ -91,12 +96,12 @@ describe('v1 credentials', () => {
})

const didCommMessageRepository = faberAgent.injectionContainer.resolve(DidCommMessageRepository)
const offerMessage = await didCommMessageRepository.findAgentMessage({
const offerMessageRecord = await didCommMessageRepository.findAgentMessage({
associatedRecordId: faberCredentialRecord.id,
messageClass: V1OfferCredentialMessage,
})

expect(JsonTransformer.toJSON(offerMessage)).toMatchObject({
expect(JsonTransformer.toJSON(offerMessageRecord)).toMatchObject({
'@id': expect.any(String),
'@type': 'https://didcomm.org/issue-credential/1.0/offer-credential',
comment: 'V1 Indy Proposal',
Expand Down Expand Up @@ -181,8 +186,17 @@ describe('v1 credentials', () => {
state: CredentialState.Done,
})

const formatData = await aliceAgent.credentials.getFormatData(aliceCredentialRecord.id)
const proposalMessage = await aliceAgent.credentials.findProposalMessage(aliceCredentialRecord.id)
const offerMessage = await aliceAgent.credentials.findOfferMessage(aliceCredentialRecord.id)
const requestMessage = await aliceAgent.credentials.findRequestMessage(aliceCredentialRecord.id)
const credentialMessage = await aliceAgent.credentials.findCredentialMessage(aliceCredentialRecord.id)

expect(proposalMessage).toBeInstanceOf(V1ProposeCredentialMessage)
expect(offerMessage).toBeInstanceOf(V1OfferCredentialMessage)
expect(requestMessage).toBeInstanceOf(V1RequestCredentialMessage)
expect(credentialMessage).toBeInstanceOf(V1IssueCredentialMessage)

const formatData = await aliceAgent.credentials.getFormatData(aliceCredentialRecord.id)
expect(formatData).toMatchObject({
proposalAttributes: [
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { V1CredentialPreview } from './V1CredentialPreview'
export interface V1ProposeCredentialMessageOptions {
id?: string
comment?: string
credentialProposal?: V1CredentialPreview
credentialPreview?: V1CredentialPreview
schemaIssuerDid?: string
schemaId?: string
schemaName?: string
Expand All @@ -34,7 +34,7 @@ export class V1ProposeCredentialMessage extends AgentMessage {
if (options) {
this.id = options.id ?? this.generateId()
this.comment = options.comment
this.credentialProposal = options.credentialProposal
this.credentialPreview = options.credentialPreview
this.schemaIssuerDid = options.schemaIssuerDid
this.schemaId = options.schemaId
this.schemaName = options.schemaName
Expand Down Expand Up @@ -65,7 +65,7 @@ export class V1ProposeCredentialMessage extends AgentMessage {
@ValidateNested()
@IsOptional()
@IsInstance(V1CredentialPreview)
public credentialProposal?: V1CredentialPreview
public credentialPreview?: V1CredentialPreview

/**
* Filter to request credential based on a particular Schema issuer DID.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,13 @@ import { JsonTransformer } from '../../../../../utils'
import { IndyHolderService } from '../../../../indy/services/IndyHolderService'
import { CredentialState } from '../../../models/CredentialState'
import { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord'
import { V2CredentialPreview } from '../messages/V2CredentialPreview'
import { V2OfferCredentialMessage } from '../messages/V2OfferCredentialMessage'
import {
V2IssueCredentialMessage,
V2ProposeCredentialMessage,
V2RequestCredentialMessage,
V2CredentialPreview,
V2OfferCredentialMessage,
} from '../messages'

const credentialPreview = V2CredentialPreview.fromRecord({
name: 'John',
Expand Down Expand Up @@ -476,8 +481,17 @@ describe('v2 credentials', () => {
state: CredentialState.CredentialReceived,
})

const formatData = await aliceAgent.credentials.getFormatData(aliceCredentialRecord.id)
const proposalMessage = await aliceAgent.credentials.findProposalMessage(aliceCredentialRecord.id)
const offerMessage = await aliceAgent.credentials.findOfferMessage(aliceCredentialRecord.id)
const requestMessage = await aliceAgent.credentials.findRequestMessage(aliceCredentialRecord.id)
const credentialMessage = await aliceAgent.credentials.findCredentialMessage(aliceCredentialRecord.id)

expect(proposalMessage).toBeInstanceOf(V2ProposeCredentialMessage)
expect(offerMessage).toBeInstanceOf(V2OfferCredentialMessage)
expect(requestMessage).toBeInstanceOf(V2RequestCredentialMessage)
expect(credentialMessage).toBeInstanceOf(V2IssueCredentialMessage)

const formatData = await aliceAgent.credentials.getFormatData(aliceCredentialRecord.id)
expect(formatData).toMatchObject({
proposalAttributes: [
{
Expand Down

0 comments on commit dc12427

Please sign in to comment.