Skip to content

Commit

Permalink
refactor: resolve feedback for problem report (#584)
Browse files Browse the repository at this point in the history
Signed-off-by: Amit Padmani <amit@northernblock.io>
  • Loading branch information
nbAmit committed Dec 21, 2021
1 parent 4d7d3c1 commit ef6b8bc
Show file tree
Hide file tree
Showing 20 changed files with 63 additions and 92 deletions.
8 changes: 2 additions & 6 deletions packages/core/src/agent/MessageReceiver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,11 @@ import { Lifecycle, scoped } from 'tsyringe'

import { AriesFrameworkError } from '../error'
import { ConnectionService } from '../modules/connections/services/ConnectionService'
import { ProblemReportError, ProblemReportMessage } from '../modules/problem-reports'
import { ProblemReportError, ProblemReportMessage, ProblemReportReason } from '../modules/problem-reports'
import { JsonTransformer } from '../utils/JsonTransformer'
import { MessageValidator } from '../utils/MessageValidator'
import { replaceLegacyDidSovPrefixOnMessage } from '../utils/messageType'

import { CommonMessageType } from './../modules/common/messages/CommonMessageType'
import { AgentConfig } from './AgentConfig'
import { Dispatcher } from './Dispatcher'
import { EnvelopeService } from './EnvelopeService'
Expand All @@ -23,9 +22,6 @@ import { TransportService } from './TransportService'
import { createOutboundMessage } from './helpers'
import { InboundMessageContext } from './models/InboundMessageContext'

export enum ProblemReportReason {
MessageParseFailure = 'message-parse-failure',
}
@scoped(Lifecycle.ContainerScoped)
export class MessageReceiver {
private config: AgentConfig
Expand Down Expand Up @@ -226,7 +222,7 @@ export class MessageReceiver {
connection: ConnectionRecord,
plaintextMessage: PlaintextMessage
) {
if (plaintextMessage['@type'] === CommonMessageType.ProblemReport) {
if (plaintextMessage['@type'] === ProblemReportMessage.type) {
throw new AriesFrameworkError(message)
}
const problemReportMessage = new ProblemReportMessage({
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { Wallet } from '../../wallet/Wallet'

import { ConnectionProblemReportError, ConnectionProblemReportReason } from '../../modules/connections/errors'
import { AriesFrameworkError } from '../../error'
import { BufferEncoder } from '../../utils/BufferEncoder'
import { JsonEncoder } from '../../utils/JsonEncoder'
import { Buffer } from '../../utils/buffer'
Expand Down Expand Up @@ -29,9 +29,7 @@ export async function unpackAndVerifySignatureDecorator(
const isValid = await wallet.verify(signerVerkey, signedData, signature)

if (!isValid) {
throw new ConnectionProblemReportError('Signature is not valid', {
problemCode: ConnectionProblemReportReason.RequestProcessingError,
})
throw new AriesFrameworkError('Signature is not valid')
}

// TODO: return Connection instance instead of raw json
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
export enum CommonMessageType {
Ack = 'https://didcomm.org/notification/1.0/ack',
ProblemReport = 'https://didcomm.org/notification/1.0/problem-report',
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,4 @@ export enum ConnectionState {
Requested = 'requested',
Responded = 'responded',
Complete = 'complete',
None = 'none',
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export interface ConnectionRecordProps {
imageUrl?: string
multiUseInvitation: boolean
mediatorId?: string
errorMsg?: string
errorMessage?: string
}

export type CustomConnectionTags = TagsBase
Expand Down Expand Up @@ -69,7 +69,7 @@ export class ConnectionRecord

public threadId?: string
public mediatorId?: string
public errorMsg?: string
public errorMessage?: string

public static readonly type = 'ConnectionRecord'
public readonly type = ConnectionRecord.type
Expand All @@ -96,7 +96,7 @@ export class ConnectionRecord
this.imageUrl = props.imageUrl
this.multiUseInvitation = props.multiUseInvitation
this.mediatorId = props.mediatorId
this.errorMsg = props.errorMsg
this.errorMessage = props.errorMessage
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -323,7 +323,16 @@ export class ConnectionService {
connectionRecord.assertState(ConnectionState.Requested)
connectionRecord.assertRole(ConnectionRole.Invitee)

const connectionJson = await unpackAndVerifySignatureDecorator(message.connectionSig, this.wallet)
let connectionJson = null
try {
connectionJson = await unpackAndVerifySignatureDecorator(message.connectionSig, this.wallet)
} catch (error) {
if (error instanceof AriesFrameworkError) {
throw new ConnectionProblemReportError(error.message, {
problemCode: ConnectionProblemReportReason.RequestProcessingError,
})
}
}

const connection = JsonTransformer.fromJSON(connectionJson, Connection)
await MessageValidator.validate(connection)
Expand Down Expand Up @@ -417,7 +426,7 @@ export class ConnectionService {
public async processProblemReport(
messageContext: InboundMessageContext<ConnectionProblemReportMessage>
): Promise<ConnectionRecord> {
const { message: connectionProblemReportMessage, recipientVerkey } = messageContext
const { message: connectionProblemReportMessage, recipientVerkey, senderVerkey } = messageContext

this.logger.debug(`Processing connection problem report for verkey ${recipientVerkey}`)

Expand All @@ -433,8 +442,12 @@ export class ConnectionService {
)
}

connectionRecord.errorMsg = `${connectionProblemReportMessage.description.code} : ${connectionProblemReportMessage.description.en}`
await this.updateState(connectionRecord, ConnectionState.None)
if (connectionRecord.theirKey && connectionRecord.theirKey !== senderVerkey) {
throw new AriesFrameworkError("Sender verkey doesn't match verkey of connection record")
}

connectionRecord.errorMessage = `${connectionProblemReportMessage.description.code} : ${connectionProblemReportMessage.description.en}`
await this.update(connectionRecord)
return connectionRecord
}

Expand Down Expand Up @@ -529,6 +542,10 @@ export class ConnectionService {
})
}

public update(connectionRecord: ConnectionRecord) {
return this.connectionRepository.update(connectionRecord)
}

/**
* Retrieve all connections records
*
Expand Down
1 change: 0 additions & 1 deletion packages/core/src/modules/credentials/CredentialState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,4 @@ export enum CredentialState {
CredentialIssued = 'credential-issued',
CredentialReceived = 'credential-received',
Done = 'done',
None = 'none',
}
Original file line number Diff line number Diff line change
Expand Up @@ -981,7 +981,7 @@ describe('CredentialService', () => {
})
})

test(`updates state to ${CredentialState.None} and returns credential record`, async () => {
test(`updates problem report error message and returns credential record`, async () => {
const repositoryUpdateSpy = jest.spyOn(credentialRepository, 'update')

// given
Expand All @@ -992,7 +992,7 @@ describe('CredentialService', () => {

// then
const expectedCredentialRecord = {
state: CredentialState.None,
errorMessage: 'issuance-abandoned: Indy error',
}
expect(credentialRepository.getSingleByQuery).toHaveBeenNthCalledWith(1, {
threadId: 'somethreadid',
Expand All @@ -1003,28 +1003,6 @@ describe('CredentialService', () => {
expect(updatedCredentialRecord).toMatchObject(expectedCredentialRecord)
expect(returnedCredentialRecord).toMatchObject(expectedCredentialRecord)
})

test(`emits stateChange event from ${CredentialState.OfferReceived} to ${CredentialState.None}`, async () => {
const eventListenerMock = jest.fn()
eventEmitter.on<CredentialStateChangedEvent>(CredentialEventTypes.CredentialStateChanged, eventListenerMock)

// given
mockFunction(credentialRepository.getSingleByQuery).mockReturnValue(Promise.resolve(credential))

// when
await credentialService.processProblemReport(messageContext)

// then
expect(eventListenerMock).toHaveBeenCalledWith({
type: 'CredentialStateChanged',
payload: {
previousState: CredentialState.OfferReceived,
credentialRecord: expect.objectContaining({
state: CredentialState.None,
}),
},
})
})
})

describe('repository methods', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export interface CredentialRecordProps {
credentialAttributes?: CredentialPreviewAttribute[]
autoAcceptCredential?: AutoAcceptCredential
linkedAttachments?: Attachment[]
errorMsg?: string
errorMessage?: string
}

export type CustomCredentialTags = TagsBase
Expand All @@ -50,7 +50,7 @@ export class CredentialRecord extends BaseRecord<DefaultCredentialTags, CustomCr
public credentialId?: string
public state!: CredentialState
public autoAcceptCredential?: AutoAcceptCredential
public errorMsg?: string
public errorMessage?: string

// message data
@Type(() => ProposeCredentialMessage)
Expand Down Expand Up @@ -90,7 +90,7 @@ export class CredentialRecord extends BaseRecord<DefaultCredentialTags, CustomCr
this.credentialAttributes = props.credentialAttributes
this.autoAcceptCredential = props.autoAcceptCredential
this.linkedAttachments = props.linkedAttachments
this.errorMsg = props.errorMsg
this.errorMessage = props.errorMessage
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -733,7 +733,9 @@ export class CredentialService {
public async processProblemReport(
messageContext: InboundMessageContext<CredentialProblemReportMessage>
): Promise<CredentialRecord> {
const { message: credentialProblemReportMessage, connection } = messageContext
const { message: credentialProblemReportMessage } = messageContext

const connection = messageContext.assertReadyConnection()

this.logger.debug(`Processing problem report with id ${credentialProblemReportMessage.id}`)

Expand All @@ -743,8 +745,8 @@ export class CredentialService {
)

// Update record
credentialRecord.errorMsg = `${credentialProblemReportMessage.description.code}: ${credentialProblemReportMessage.description.en}`
await this.updateState(credentialRecord, CredentialState.None)
credentialRecord.errorMessage = `${credentialProblemReportMessage.description.code}: ${credentialProblemReportMessage.description.en}`
await this.update(credentialRecord)
return credentialRecord
}

Expand Down
1 change: 1 addition & 0 deletions packages/core/src/modules/problem-reports/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from './errors'
export * from './messages'
export * from './models'
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { Expose } from 'class-transformer'
import { Equals, IsEnum, IsOptional, IsString } from 'class-validator'

import { AgentMessage } from '../../../agent/AgentMessage'
import { CommonMessageType } from '../../common/messages/CommonMessageType'

export enum WhoRetriesStatus {
You = 'YOU',
Expand Down Expand Up @@ -80,7 +79,7 @@ export class ProblemReportMessage extends AgentMessage {

@Equals(ProblemReportMessage.type)
public readonly type: string = ProblemReportMessage.type
public static readonly type: string = CommonMessageType.ProblemReport
public static readonly type: string = 'https://didcomm.org/notification/1.0/problem-report'

public description!: DescriptionOptions

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export enum ProblemReportReason {
MessageParseFailure = 'message-parse-failure',
}
1 change: 1 addition & 0 deletions packages/core/src/modules/problem-reports/models/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './ProblemReportReason'
1 change: 0 additions & 1 deletion packages/core/src/modules/proofs/ProofState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,4 @@ export enum ProofState {
PresentationReceived = 'presentation-received',
Declined = 'declined',
Done = 'done',
None = 'none',
}
2 changes: 1 addition & 1 deletion packages/core/src/modules/proofs/ProofsModule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -386,7 +386,7 @@ export class ProofsModule {
const presentationProblemReportMessage = new PresentationProblemReportMessage({
description: {
en: message,
code: PresentationProblemReportReason.abandoned,
code: PresentationProblemReportReason.Abandoned,
},
})
presentationProblemReportMessage.setThread({
Expand Down
30 changes: 4 additions & 26 deletions packages/core/src/modules/proofs/__tests__/ProofService.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ describe('ProofService', () => {
const presentationProblemReportMessage = await new PresentationProblemReportMessage({
description: {
en: 'Indy error',
code: PresentationProblemReportReason.abandoned,
code: PresentationProblemReportReason.Abandoned,
},
})

Expand Down Expand Up @@ -224,7 +224,7 @@ describe('ProofService', () => {
const presentationProblemReportMessage = new PresentationProblemReportMessage({
description: {
en: 'Indy error',
code: PresentationProblemReportReason.abandoned,
code: PresentationProblemReportReason.Abandoned,
},
})
presentationProblemReportMessage.setThread({ threadId: 'somethreadid' })
Expand All @@ -233,7 +233,7 @@ describe('ProofService', () => {
})
})

test(`updates state to ${ProofState.None} and returns proof record`, async () => {
test(`updates problem report error message and returns proof record`, async () => {
const repositoryUpdateSpy = jest.spyOn(proofRepository, 'update')

// given
Expand All @@ -244,7 +244,7 @@ describe('ProofService', () => {

// then
const expectedCredentialRecord = {
state: ProofState.None,
errorMessage: 'abandoned: Indy error',
}
expect(proofRepository.getSingleByQuery).toHaveBeenNthCalledWith(1, {
threadId: 'somethreadid',
Expand All @@ -255,27 +255,5 @@ describe('ProofService', () => {
expect(updatedCredentialRecord).toMatchObject(expectedCredentialRecord)
expect(returnedCredentialRecord).toMatchObject(expectedCredentialRecord)
})

test(`emits stateChange event from ${ProofState.RequestReceived} to ${ProofState.None}`, async () => {
const eventListenerMock = jest.fn()
eventEmitter.on<ProofStateChangedEvent>(ProofEventTypes.ProofStateChanged, eventListenerMock)

// given
mockFunction(proofRepository.getSingleByQuery).mockReturnValue(Promise.resolve(proof))

// when
await proofService.processProblemReport(messageContext)

// then
expect(eventListenerMock).toHaveBeenCalledWith({
type: 'ProofStateChanged',
payload: {
previousState: ProofState.RequestReceived,
proofRecord: expect.objectContaining({
state: ProofState.None,
}),
},
})
})
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@
* @see https://github.com/hyperledger/aries-rfcs/blob/main/features/0037-present-proof/README.md
*/
export enum PresentationProblemReportReason {
abandoned = 'abandoned',
Abandoned = 'abandoned',
}
6 changes: 3 additions & 3 deletions packages/core/src/modules/proofs/repository/ProofRecord.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export interface ProofRecordProps {
presentationId?: string
tags?: CustomProofTags
autoAcceptProof?: AutoAcceptProof
errorMsg?: string
errorMessage?: string

// message data
proposalMessage?: ProposePresentationMessage
Expand All @@ -42,7 +42,7 @@ export class ProofRecord extends BaseRecord<DefaultProofTags, CustomProofTags> {
public presentationId?: string
public state!: ProofState
public autoAcceptProof?: AutoAcceptProof
public errorMsg?: string
public errorMessage?: string

// message data
@Type(() => ProposePresentationMessage)
Expand Down Expand Up @@ -71,7 +71,7 @@ export class ProofRecord extends BaseRecord<DefaultProofTags, CustomProofTags> {
this.presentationId = props.presentationId
this.autoAcceptProof = props.autoAcceptProof
this._tags = props.tags ?? {}
this.errorMsg = props.errorMsg
this.errorMessage = props.errorMessage
}
}

Expand Down

0 comments on commit ef6b8bc

Please sign in to comment.