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

refactor: unify connection record state and role #732

Merged
Show file tree
Hide file tree
Changes from 1 commit
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
4 changes: 2 additions & 2 deletions packages/core/src/agent/__tests__/TransportService.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { getMockConnection } from '../../../tests/helpers'
import { ConnectionRole } from '../../modules/connections'
import { DidExchangeRole } from '../../modules/connections'
import { TransportService } from '../TransportService'

import { DummyTransportSession } from './stubs'
Expand All @@ -13,7 +13,7 @@ describe('TransportService', () => {
})

test(`remove session saved for a given connection`, () => {
const connection = getMockConnection({ id: 'test-123', role: ConnectionRole.Inviter })
const connection = getMockConnection({ id: 'test-123', role: DidExchangeRole.Responder })
const session = new DummyTransportSession('dummy-session-123')
session.connection = connection

Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/modules/connections/ConnectionEvents.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { BaseEvent } from '../../agent/Events'
import type { ConnectionState, DidExchangeState } from './models'
import type { DidExchangeState } from './models'
import type { ConnectionRecord } from './repository/ConnectionRecord'

export enum ConnectionEventTypes {
Expand All @@ -10,6 +10,6 @@ export interface ConnectionStateChangedEvent extends BaseEvent {
type: typeof ConnectionEventTypes.ConnectionStateChanged
payload: {
connectionRecord: ConnectionRecord
previousState: ConnectionState | DidExchangeState | null
previousState: DidExchangeState | null
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,14 @@ import { DidRepository } from '../../dids/repository'
import { OutOfBandRole } from '../../oob/domain/OutOfBandRole'
import { OutOfBandState } from '../../oob/domain/OutOfBandState'
import { ConnectionRequestMessage, ConnectionResponseMessage, TrustPingMessage } from '../messages'
import { Connection, ConnectionState, ConnectionRole, DidDoc, EmbeddedAuthentication, Ed25119Sig2018 } from '../models'
import {
Connection,
DidDoc,
EmbeddedAuthentication,
Ed25119Sig2018,
DidExchangeRole,
DidExchangeState,
} from '../models'
import { ConnectionRepository } from '../repository/ConnectionRepository'
import { ConnectionService } from '../services/ConnectionService'
import { convertToNewDidDocument } from '../services/helpers'
Expand Down Expand Up @@ -76,7 +83,7 @@ describe('ConnectionService', () => {

const { connectionRecord, message } = await connectionService.createRequest(outOfBand, config)

expect(connectionRecord.state).toBe(ConnectionState.Requested)
expect(connectionRecord.state).toBe(DidExchangeState.RequestSent)
expect(message.label).toBe(agentConfig.label)
expect(message.connection.did).toBe('fakeDid')
expect(message.connection.didDoc).toEqual(
Expand Down Expand Up @@ -199,7 +206,7 @@ describe('ConnectionService', () => {
})
const processedConnection = await connectionService.processRequest(messageContext, outOfBand)

expect(processedConnection.state).toBe(ConnectionState.Requested)
expect(processedConnection.state).toBe(DidExchangeState.RequestReceived)
expect(processedConnection.theirDid).toBe('did:peer:1zQmXUaPPhPCbUVZ3hGYmQmGxWTwyDfhqESXCpMFhKaF9Y2A')
expect(processedConnection.theirLabel).toBe('test-label')
expect(processedConnection.threadId).toBe(connectionRequest.id)
Expand All @@ -211,8 +218,8 @@ describe('ConnectionService', () => {

const connectionRecord = getMockConnection({
id: 'test',
state: ConnectionState.Invited,
role: ConnectionRole.Inviter,
state: DidExchangeState.InvitationSent,
role: DidExchangeRole.Responder,
multiUseInvitation: true,
})

Expand Down Expand Up @@ -251,15 +258,15 @@ describe('ConnectionService', () => {
})
const processedConnection = await connectionService.processRequest(messageContext, outOfBand)

expect(processedConnection.state).toBe(ConnectionState.Requested)
expect(processedConnection.state).toBe(DidExchangeState.RequestReceived)
expect(processedConnection.theirDid).toBe('did:peer:1zQmXUaPPhPCbUVZ3hGYmQmGxWTwyDfhqESXCpMFhKaF9Y2A')
expect(processedConnection.theirLabel).toBe('test-label')
expect(processedConnection.threadId).toBe(connectionRequest.id)

expect(connectionRepository.save).toHaveBeenCalledTimes(1)
expect(processedConnection.id).not.toBe(connectionRecord.id)
expect(connectionRecord.id).toBe('test')
expect(connectionRecord.state).toBe(ConnectionState.Invited)
expect(connectionRecord.state).toBe(DidExchangeState.InvitationSent)
})

it('throws an error when the message does not contain a did doc', async () => {
Expand Down Expand Up @@ -320,8 +327,8 @@ describe('ConnectionService', () => {
// Needed for signing connection~sig
const { did, verkey } = await wallet.createDid()
const mockConnection = getMockConnection({
state: ConnectionState.Requested,
role: ConnectionRole.Inviter,
state: DidExchangeState.RequestReceived,
role: DidExchangeRole.Responder,
tags: {
threadId: 'test',
},
Expand Down Expand Up @@ -368,33 +375,42 @@ describe('ConnectionService', () => {
})
const plainConnection = JsonTransformer.toJSON(connection)

expect(connectionRecord.state).toBe(ConnectionState.Responded)
expect(connectionRecord.state).toBe(DidExchangeState.ResponseSent)
expect(await unpackAndVerifySignatureDecorator(message.connectionSig, wallet)).toEqual(plainConnection)
})

it(`throws an error when connection role is ${ConnectionRole.Invitee} and not ${ConnectionRole.Inviter}`, async () => {
it(`throws an error when connection role is ${DidExchangeRole.Requester} and not ${DidExchangeRole.Responder}`, async () => {
expect.assertions(1)

const connection = getMockConnection({
role: ConnectionRole.Invitee,
state: ConnectionState.Requested,
role: DidExchangeRole.Requester,
state: DidExchangeState.RequestReceived,
})
const outOfBand = getMockOutOfBand()
return expect(connectionService.createResponse(connection, outOfBand)).rejects.toThrowError(
`Connection record has invalid role ${ConnectionRole.Invitee}. Expected role ${ConnectionRole.Inviter}.`
`Connection record has invalid role ${DidExchangeRole.Requester}. Expected role ${DidExchangeRole.Responder}.`
)
})

const invalidOutOfBandStates = [ConnectionState.Invited, ConnectionState.Responded, ConnectionState.Complete]
const invalidOutOfBandStates = [
DidExchangeState.InvitationSent,
DidExchangeState.InvitationReceived,
DidExchangeState.RequestSent,
DidExchangeState.ResponseSent,
DidExchangeState.ResponseReceived,
DidExchangeState.Completed,
DidExchangeState.Abandoned,
DidExchangeState.Start,
]
test.each(invalidOutOfBandStates)(
`throws an error when connection state is %s and not ${ConnectionState.Requested}`,
`throws an error when connection state is %s and not ${DidExchangeState.RequestReceived}`,
async (state) => {
expect.assertions(1)

const connection = getMockConnection({ state })
const outOfBand = getMockOutOfBand()
return expect(connectionService.createResponse(connection, outOfBand)).rejects.toThrowError(
`Connection record is in invalid state ${state}. Valid states are: ${ConnectionState.Requested}.`
`Connection record is in invalid state ${state}. Valid states are: ${DidExchangeState.RequestReceived}.`
)
}
)
Expand All @@ -409,8 +425,8 @@ describe('ConnectionService', () => {

const connectionRecord = getMockConnection({
did,
state: ConnectionState.Requested,
role: ConnectionRole.Invitee,
state: DidExchangeState.RequestSent,
role: DidExchangeRole.Requester,
})

const otherPartyConnection = new Connection({
Expand Down Expand Up @@ -451,17 +467,17 @@ describe('ConnectionService', () => {
convertToNewDidDocument(otherPartyConnection.didDoc!),
PeerDidNumAlgo.GenesisDoc
)
expect(processedConnection.state).toBe(ConnectionState.Responded)
expect(processedConnection.state).toBe(DidExchangeState.ResponseReceived)
expect(processedConnection.theirDid).toBe(peerDid.did)
})

it(`throws an error when connection role is ${ConnectionRole.Inviter} and not ${ConnectionRole.Invitee}`, async () => {
it(`throws an error when connection role is ${DidExchangeRole.Responder} and not ${DidExchangeRole.Requester}`, async () => {
expect.assertions(1)

const outOfBandRecord = getMockOutOfBand()
const connectionRecord = getMockConnection({
role: ConnectionRole.Inviter,
state: ConnectionState.Requested,
role: DidExchangeRole.Responder,
state: DidExchangeState.RequestSent,
})
const messageContext = new InboundMessageContext(jest.fn()(), {
connection: connectionRecord,
Expand All @@ -470,7 +486,7 @@ describe('ConnectionService', () => {
})

return expect(connectionService.processResponse(messageContext, outOfBandRecord)).rejects.toThrowError(
`Connection record has invalid role ${ConnectionRole.Inviter}. Expected role ${ConnectionRole.Invitee}.`
`Connection record has invalid role ${DidExchangeRole.Responder}. Expected role ${DidExchangeRole.Requester}.`
)
})

Expand All @@ -481,8 +497,8 @@ describe('ConnectionService', () => {
const { did: theirDid, verkey: theirVerkey } = await wallet.createDid()
const connectionRecord = getMockConnection({
did,
role: ConnectionRole.Invitee,
state: ConnectionState.Requested,
role: DidExchangeRole.Requester,
state: DidExchangeState.RequestSent,
})

const otherPartyConnection = new Connection({
Expand Down Expand Up @@ -531,7 +547,7 @@ describe('ConnectionService', () => {
const { did: theirDid, verkey: theirVerkey } = await wallet.createDid()
const connectionRecord = getMockConnection({
did,
state: ConnectionState.Requested,
state: DidExchangeState.RequestSent,
theirDid: undefined,
})

Expand All @@ -558,23 +574,31 @@ describe('ConnectionService', () => {
it('returns a trust ping message', async () => {
expect.assertions(2)

const mockConnection = getMockConnection({ state: ConnectionState.Responded })
const mockConnection = getMockConnection({ state: DidExchangeState.ResponseReceived })

const { message, connectionRecord: connectionRecord } = await connectionService.createTrustPing(mockConnection)

expect(connectionRecord.state).toBe(ConnectionState.Complete)
expect(connectionRecord.state).toBe(DidExchangeState.Completed)
expect(message).toEqual(expect.any(TrustPingMessage))
})

const invalidConnectionStates = [ConnectionState.Invited, ConnectionState.Requested]
const invalidConnectionStates = [
DidExchangeState.InvitationSent,
DidExchangeState.InvitationReceived,
DidExchangeState.RequestSent,
DidExchangeState.RequestReceived,
DidExchangeState.ResponseSent,
DidExchangeState.Abandoned,
DidExchangeState.Start,
]
test.each(invalidConnectionStates)(
`throws an error when connection state is %s and not ${ConnectionState.Responded} or ${ConnectionState.Complete}`,
`throws an error when connection state is %s and not ${DidExchangeState.ResponseReceived} or ${DidExchangeState.Completed}`,
(state) => {
expect.assertions(1)
const connection = getMockConnection({ state })

return expect(connectionService.createTrustPing(connection)).rejects.toThrowError(
`Connection record is in invalid state ${state}. Valid states are: ${ConnectionState.Responded}, ${ConnectionState.Complete}.`
`Connection record is in invalid state ${state}. Valid states are: ${DidExchangeState.ResponseReceived}, ${DidExchangeState.Completed}.`
)
}
)
Expand All @@ -596,12 +620,12 @@ describe('ConnectionService', () => {
)
})

it('updates the state to Completed when the state is Responded and role is Inviter', async () => {
it('updates the state to Completed when the state is ResponseSent and role is Responder', async () => {
expect.assertions(1)

const connection = getMockConnection({
state: ConnectionState.Responded,
role: ConnectionRole.Inviter,
state: DidExchangeState.ResponseSent,
role: DidExchangeRole.Responder,
})

const ack = new AckMessage({
Expand All @@ -613,15 +637,15 @@ describe('ConnectionService', () => {

const updatedConnection = await connectionService.processAck(messageContext)

expect(updatedConnection.state).toBe(ConnectionState.Complete)
expect(updatedConnection.state).toBe(DidExchangeState.Completed)
})

it('does not update the state when the state is not Responded or the role is not Inviter', async () => {
it('does not update the state when the state is not ResponseSent or the role is not Responder', async () => {
expect.assertions(1)

const connection = getMockConnection({
state: ConnectionState.Responded,
role: ConnectionRole.Invitee,
state: DidExchangeState.ResponseReceived,
role: DidExchangeRole.Requester,
})

const ack = new AckMessage({
Expand All @@ -633,7 +657,7 @@ describe('ConnectionService', () => {

const updatedConnection = await connectionService.processAck(messageContext)

expect(updatedConnection.state).toBe(ConnectionState.Responded)
expect(updatedConnection.state).toBe(DidExchangeState.ResponseReceived)
})
})

Expand All @@ -642,7 +666,7 @@ describe('ConnectionService', () => {
expect.assertions(1)

const messageContext = new InboundMessageContext(new AgentMessage(), {
connection: getMockConnection({ state: ConnectionState.Complete }),
connection: getMockConnection({ state: DidExchangeState.Completed }),
})

expect(() => connectionService.assertConnectionOrServiceDecorator(messageContext)).not.toThrow()
Expand All @@ -652,7 +676,7 @@ describe('ConnectionService', () => {
expect.assertions(1)

const messageContext = new InboundMessageContext(new AgentMessage(), {
connection: getMockConnection({ state: ConnectionState.Invited }),
connection: getMockConnection({ state: DidExchangeState.InvitationReceived }),
})

expect(() => connectionService.assertConnectionOrServiceDecorator(messageContext)).toThrowError(
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import type { TrustPingService } from '../services/TrustPingService'

import { AriesFrameworkError } from '../../../error'
import { TrustPingMessage } from '../messages'
import { ConnectionState } from '../models'
import { DidExchangeState } from '../models'

export class TrustPingMessageHandler implements Handler {
private trustPingService: TrustPingService
Expand All @@ -24,8 +24,8 @@ export class TrustPingMessageHandler implements Handler {

// TODO: This is better addressed in a middleware of some kind because
// any message can transition the state to complete, not just an ack or trust ping
if (connection.state === ConnectionState.Responded) {
await this.connectionService.updateState(connection, ConnectionState.Complete)
if (connection.state === DidExchangeState.ResponseSent) {
await this.connectionService.updateState(connection, DidExchangeState.Completed)
}

return this.trustPingService.processPing(messageContext, connection)
Expand Down
21 changes: 19 additions & 2 deletions packages/core/src/modules/connections/models/ConnectionState.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,30 @@
import { DidExchangeState } from './DidExchangeState'

/**
* Connection states as defined in RFC 0160.
*
* State 'null' from RFC is changed to 'init'
*
* @see https://github.com/hyperledger/aries-rfcs/blob/master/features/0160-connection-protocol/README.md#states
*/
export enum ConnectionState {
Null = 'null',
Invited = 'invited',
Requested = 'requested',
Responded = 'responded',
Complete = 'complete',
}

export function rfc0160StateFromDidExchangeState(didExchangeState: DidExchangeState) {
const stateMapping = {
[DidExchangeState.Start]: ConnectionState.Null,
[DidExchangeState.Abandoned]: ConnectionState.Null,
[DidExchangeState.InvitationReceived]: ConnectionState.Invited,
[DidExchangeState.InvitationSent]: ConnectionState.Invited,
[DidExchangeState.RequestReceived]: ConnectionState.Requested,
[DidExchangeState.RequestSent]: ConnectionState.Requested,
[DidExchangeState.ResponseReceived]: ConnectionState.Responded,
[DidExchangeState.ResponseSent]: ConnectionState.Responded,
[DidExchangeState.Completed]: DidExchangeState.Completed,
}

return stateMapping[didExchangeState]
}
Loading