Skip to content

Commit

Permalink
Merge pull request #14 from TimoGlastra/0211-2
Browse files Browse the repository at this point in the history
refactor: mediator recipient cleanup
  • Loading branch information
burdettadam authored Jul 9, 2021
2 parents 8b09cc8 + a46bd21 commit fb19af9
Show file tree
Hide file tree
Showing 19 changed files with 261 additions and 268 deletions.
26 changes: 20 additions & 6 deletions src/agent/Agent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ export class Agent {
public async init() {
await this.wallet.init()

const { publicDidSeed } = this.agentConfig
const { publicDidSeed, mediatorConnectionsInvite } = this.agentConfig
if (publicDidSeed) {
// If an agent has publicDid it will be used as routing key.
await this.wallet.initPublicDid({ seed: publicDidSeed })
Expand All @@ -140,20 +140,34 @@ export class Agent {
await this.inboundTransporter.start(this)
}

await this.mediationRecipient.init(this.connections)
// Connect to mediator through provided invitation if provided in config
// Also requests mediation ans sets as default mediator
// Because this requires the connections module, we do this in the agent constructor
if (mediatorConnectionsInvite) {
// Assumption: processInvitation is a URL-encoded invitation
let connectionRecord = await this.connections.receiveInvitationFromUrl(mediatorConnectionsInvite, {
autoAcceptConnection: true,
})

// TODO: add timeout to returnWhenIsConnected
connectionRecord = await this.connections.returnWhenIsConnected(connectionRecord.id)
const mediationRecord = await this.mediationRecipient.requestAndAwaitGrant(connectionRecord, 60000) // TODO: put timeout as a config parameter
await this.mediationRecipient.setDefaultMediator(mediationRecord)
}

await this.mediationRecipient.initialize()

this._isInitialized = true
}

public get publicDid() {
return this.wallet.publicDid
}
public async getMediatorUrl() {
const defaultMediator = await this.mediationRecipient.getDefaultMediator()
return defaultMediator?.endpoint ?? this.agentConfig.getEndpoint()
}

public get port() {
return this.agentConfig.port
}

public async receiveMessage(inboundPackedMessage: unknown, session?: TransportSession) {
return await this.messageReceiver.receiveMessage(inboundPackedMessage, session)
}
Expand Down
2 changes: 1 addition & 1 deletion src/modules/connections/services/ConnectionService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -202,9 +202,9 @@ export class ConnectionService {
throw new AriesFrameworkError('Invalid message')
}

connectionRecord.theirDid = message.connection.did
connectionRecord.theirDidDoc = message.connection.didDoc
connectionRecord.threadId = message.id
connectionRecord.theirDid = message.connection.did

if (!connectionRecord.theirKey) {
throw new AriesFrameworkError(`Connection with id ${connectionRecord.id} has no recipient keys.`)
Expand Down
43 changes: 21 additions & 22 deletions src/modules/routing/MediatorModule.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import type { WireMessage } from '../../types'
import type { ConnectionRecord } from '../connections/repository/ConnectionRecord'
import type { MediationRecord } from './index'
import type { MediationRecord } from './repository'

import { Lifecycle, scoped } from 'tsyringe'

import { AgentConfig } from '../../agent/AgentConfig'
import { Dispatcher } from '../../agent/Dispatcher'
import { EventEmitter } from '../../agent/EventEmitter'
import { MessageSender } from '../../agent/MessageSender'
import { createOutboundMessage } from '../../agent/helpers'
import { ConnectionService } from '../connections/services'

import { KeylistUpdateHandler, ForwardHandler, BatchPickupHandler, BatchHandler } from './handlers'
import { MediationRequestHandler } from './handlers/MediationRequestHandler'
Expand All @@ -20,39 +21,37 @@ export class MediatorModule {
private messagePickupService: MessagePickupService
private messageSender: MessageSender
public eventEmitter: EventEmitter
public agentConfig: AgentConfig
public connectionService: ConnectionService

public constructor(
dispatcher: Dispatcher,
mediationService: MediatorService,
messagePickupService: MessagePickupService,
messageSender: MessageSender,
eventEmitter: EventEmitter
eventEmitter: EventEmitter,
agentConfig: AgentConfig,
connectionService: ConnectionService
) {
this.mediatorService = mediationService
this.messagePickupService = messagePickupService
this.messageSender = messageSender
this.eventEmitter = eventEmitter
this.agentConfig = agentConfig
this.connectionService = connectionService
this.registerHandlers(dispatcher)
}
/* eslint-disable @typescript-eslint/no-unused-vars */
public async init(config: { autoAcceptMediationRequests: boolean }) {
// autoAcceptMediationRequests
// "automatically granting to everyone asking, rather than enabling the feature altogether"
// "After establishing a connection, "
// "if enabled, an agent may request message mediation, which will "
// "allow the mediator to forward messages on behalf of the recipient. "
// "See aries-rfc:0211."
//if (config.autoAcceptMediationRequests) {
// this.autoAcceptMediationRequests = config.autoAcceptMediationRequests
//}
}
/* eslint-enable @typescript-eslint/no-unused-vars */

public async grantRequestedMediation(connectionRecord: ConnectionRecord, mediationRecord: MediationRecord) {
const grantMessage = await this.mediatorService.createGrantMediationMessage(mediationRecord)
const outboundMessage = createOutboundMessage(connectionRecord, grantMessage)
const response = await this.messageSender.sendMessage(outboundMessage)
return response
public async grantRequestedMediation(mediatorId: string): Promise<MediationRecord> {
const record = await this.mediatorService.getById(mediatorId)
const connectionRecord = await this.connectionService.getById(record.connectionId)

const { message, mediationRecord } = await this.mediatorService.createGrantMediationMessage(record)
const outboundMessage = createOutboundMessage(connectionRecord, message)

await this.messageSender.sendMessage(outboundMessage)

return mediationRecord
}

public queueMessage(theirKey: string, message: WireMessage) {
Expand All @@ -64,6 +63,6 @@ export class MediatorModule {
dispatcher.registerHandler(new ForwardHandler(this.mediatorService))
dispatcher.registerHandler(new BatchPickupHandler(this.messagePickupService))
dispatcher.registerHandler(new BatchHandler(this.eventEmitter))
dispatcher.registerHandler(new MediationRequestHandler(this.mediatorService))
dispatcher.registerHandler(new MediationRequestHandler(this.mediatorService, this.agentConfig))
}
}
76 changes: 23 additions & 53 deletions src/modules/routing/RecipientModule.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import type { ConnectionRecord } from '../connections'
import type { ConnectionsModule } from '../connections/ConnectionsModule'
import type { MediationStateChangedEvent } from './RoutingEvents'
import type { MediationRecord } from './index'
import type { Verkey } from 'indy-sdk'
Expand Down Expand Up @@ -47,48 +46,30 @@ export class RecipientModule {
this.registerHandlers(dispatcher)
}

public async init(connections: ConnectionsModule) {
this.recipientService.init()
if (this.agentConfig.mediatorConnectionsInvite) {
/* --------------------------------
| Connect to mediator through provided invitation
| and send mediation request and set as default mediator.
*/
// Check if inviation was provided in config
// Assumption: processInvitation is a URL-encoded invitation
let connectionRecord = await connections.receiveInvitationFromUrl(this.agentConfig.mediatorConnectionsInvite, {
autoAcceptConnection: true,
alias: 'InitedMediator', // TODO come up with a better name for this
})
connectionRecord = await connections.returnWhenIsConnected(connectionRecord.id)
const mediationRecord = await this.requestAndAwaitGrant(connectionRecord, 60000) // TODO: put timeout as a config parameter
await this.recipientService.setDefaultMediator(mediationRecord)
}
public async initialize() {
const { defaultMediatorId, clearDefaultMediator } = this.agentConfig
// Set default mediator by id
if (this.agentConfig.defaultMediatorId) {
/*
| Set the default mediator by ID
*/
const mediatorRecord = await this.recipientService.findById(this.agentConfig.defaultMediatorId)
if (mediatorRecord) {
this.recipientService.setDefaultMediator(mediatorRecord)
await this.recipientService.setDefaultMediator(mediatorRecord)
} else {
this.agentConfig.logger.error('Mediator record not found from config')
this.agentConfig.logger.error(`Mediator record with id ${defaultMediatorId} not found from config`)
// TODO: Handle error properly - not found condition
}
}
if (this.agentConfig.clearDefaultMediator) {
/*
| Clear the stored default mediator
*/
this.recipientService.clearDefaultMediator()
// Clear the stored default mediator
else if (clearDefaultMediator) {
await this.recipientService.clearDefaultMediator()
}
}

public async discoverMediation() {
return this.recipientService.discoverMediation()
}

public async downloadMessages(mediatorConnection: ConnectionRecord) {
let connection = mediatorConnection ?? (await this.getDefaultMediatorConnection())
let connection = mediatorConnection ?? (await this.findDefaultMediatorConnection())
connection = assertConnection(connection, 'connection not found for default mediator')
const batchPickupMessage = new BatchPickupMessage({ batchSize: 10 })
const outboundMessage = createOutboundMessage(connection, batchPickupMessage)
Expand All @@ -97,18 +78,18 @@ export class RecipientModule {
}

public async setDefaultMediator(mediatorRecord: MediationRecord) {
return await this.recipientService.setDefaultMediator(mediatorRecord)
return this.recipientService.setDefaultMediator(mediatorRecord)
}

public async requestMediation(connection: ConnectionRecord): Promise<MediationRecord> {
const [record, message] = await this.recipientService.createRequest(connection)
const { mediationRecord, message } = await this.recipientService.createRequest(connection)
const outboundMessage = createOutboundMessage(connection, message)
await this.messageSender.sendMessage(outboundMessage)
return record
return mediationRecord
}

public async notifyKeylistUpdate(connection: ConnectionRecord, verkey: Verkey) {
const message = await this.recipientService.createKeylistUpdateMessage(verkey)
const message = this.recipientService.createKeylistUpdateMessage(verkey)
const outboundMessage = createOutboundMessage(connection, message)
const response = await this.messageSender.sendMessage(outboundMessage)
return response
Expand All @@ -129,29 +110,18 @@ export class RecipientModule {
return await this.recipientService.getMediators()
}

public async getMediatorConnections() {
const allMediators = await this.getMediators()
const mediatorConnectionIds = allMediators ? allMediators.map((mediator) => mediator.connectionId) : []
const allConnections = await this.connectionService.getAll()
return allConnections && mediatorConnectionIds
? allConnections.filter((connection) => mediatorConnectionIds.includes(connection.id))
: []
}

public async getDefaultMediatorId() {
return await this.recipientService.getDefaultMediatorId()
public async findDefaultMediator(): Promise<MediationRecord | null> {
return this.recipientService.findDefaultMediator()
}

public async getDefaultMediator(): Promise<MediationRecord | undefined> {
return await this.recipientService.getDefaultMediator()
}
public async findDefaultMediatorConnection(): Promise<ConnectionRecord | null> {
const mediatorRecord = await this.findDefaultMediator()

public async getDefaultMediatorConnection(): Promise<ConnectionRecord | undefined> {
const mediatorRecord = await this.getDefaultMediator()
if (mediatorRecord) {
return await this.connectionService.getById(mediatorRecord.connectionId)
return this.connectionService.getById(mediatorRecord.connectionId)
}
return undefined

return null
}

/**
Expand All @@ -162,7 +132,7 @@ export class RecipientModule {
* return promise with listener
**/
public async requestAndAwaitGrant(connection: ConnectionRecord, timeout = 10000): Promise<MediationRecord> {
const [record, message] = await this.recipientService.createRequest(connection)
const { mediationRecord, message } = await this.recipientService.createRequest(connection)

const sendMediationRequest = async () => {
message.setReturnRouting(ReturnRouteTypes.all) // return message on request response
Expand All @@ -171,7 +141,7 @@ export class RecipientModule {
}
const condition = async (event: MediationStateChangedEvent) => {
const previousStateMatches = MediationState.Requested === event.payload.previousState
const mediationIdMatches = record.id === event.payload.mediationRecord.id
const mediationIdMatches = mediationRecord.id === event.payload.mediationRecord.id
const stateMatches = MediationState.Granted === event.payload.mediationRecord.state
return previousStateMatches && mediationIdMatches && stateMatches
}
Expand Down
2 changes: 1 addition & 1 deletion src/modules/routing/RoutingEvents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export interface MediationStateChangedEvent extends BaseEvent {
type: typeof RoutingEventTypes.MediationStateChanged
payload: {
mediationRecord: MediationRecord
previousState: MediationState
previousState: MediationState | null
}
}

Expand Down
9 changes: 3 additions & 6 deletions src/modules/routing/__tests__/RecipientService.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,13 +46,11 @@ describe('Recipient', () => {
const record = new MediationRecord({
state: MediationState.Init,
role: MediationRole.Recipient,
threadId: 'fakeThreadId',
connectionId: 'fakeConnectionId',
recipientKeys: ['fakeRecipientKey'],
tags: {
state: MediationState.Init,
role: MediationRole.Recipient,
connectionId: 'fakeConnectionId',
default: 'false',
default: false,
},
})
assert(record.state, 'Expected MediationRecord to have an `state` property')
Expand All @@ -76,8 +74,7 @@ describe('Recipient', () => {
describe('Recipient service tests', () => {
it('validate service class signiture', () => {
assert(recipientService.setDefaultMediator, 'Expected RecipientService to have a `setDefaultMediator` method')
assert(recipientService.getDefaultMediator, 'Expected RecipientService to have a `getDefaultMediator` method')
assert(recipientService.getDefaultMediatorId, 'Expected RecipientService to have a `getDefaultMediatorId` method')
assert(recipientService.findDefaultMediator, 'Expected RecipientService to have a `getDefaultMediator` method')
assert(recipientService.getMediators, 'Expected RecipientService to have a `getMediators` method')
assert(recipientService.clearDefaultMediator, 'Expected RecipientService to have a `clearDefaultMediator` method')
assert(recipientService.findByConnectionId, 'Expected RecipientService to have a `findByConnectionId` method')
Expand Down
5 changes: 4 additions & 1 deletion src/modules/routing/handlers/KeylistUpdateHandler.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { Handler, HandlerInboundMessage } from '../../../agent/Handler'
import type { MediatorService } from '../services/MediatorService'

import { createOutboundMessage } from '../../../agent/helpers'
import { AriesFrameworkError } from '../../../error'
import { KeylistUpdateMessage } from '../messages'

Expand All @@ -16,6 +17,8 @@ export class KeylistUpdateHandler implements Handler {
if (!messageContext.connection) {
throw new AriesFrameworkError(`Connection for verkey ${messageContext.recipientVerkey} not found!`)
}
return await this.mediatorService.processKeylistUpdateRequest(messageContext)

const response = await this.mediatorService.processKeylistUpdateRequest(messageContext)
return createOutboundMessage(messageContext.connection, response)
}
}
16 changes: 13 additions & 3 deletions src/modules/routing/handlers/MediationRequestHandler.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,31 @@
import type { AgentConfig } from '../../../agent/AgentConfig'
import type { Handler, HandlerInboundMessage } from '../../../agent/Handler'
import type { MediatorService } from '../services/MediatorService'

import { createOutboundMessage } from '../../../agent/helpers'
import { AriesFrameworkError } from '../../../error'
import { MediationRequestMessage } from '../messages/MediationRequestMessage'

export class MediationRequestHandler implements Handler {
private mediatorService: MediatorService
private agentConfig: AgentConfig
public supportedMessages = [MediationRequestMessage]

public constructor(mediatorService: MediatorService) {
public constructor(mediatorService: MediatorService, agentConfig: AgentConfig) {
this.mediatorService = mediatorService
this.agentConfig = agentConfig
}

public async handle(messageContext: HandlerInboundMessage<MediationRequestHandler>) {
if (!messageContext.connection) {
throw new Error(`Connection for verkey ${messageContext.recipientVerkey} not found!`)
throw new AriesFrameworkError(`Connection for verkey ${messageContext.recipientVerkey} not found!`)
}

return await this.mediatorService.processMediationRequest(messageContext)
const mediationRecord = await this.mediatorService.processMediationRequest(messageContext)

if (this.agentConfig.autoAcceptMediationRequests) {
const { message } = await this.mediatorService.createGrantMediationMessage(mediationRecord)
return createOutboundMessage(messageContext.connection, message)
}
}
}
4 changes: 4 additions & 0 deletions src/modules/routing/messages/KeylistUpdateResponseMessage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { RoutingMessageType as MessageType } from './RoutingMessageType'
export interface KeylistUpdateResponseMessageOptions {
id?: string
keylist: KeylistUpdated[]
threadId: string
}

/**
Expand All @@ -24,6 +25,9 @@ export class KeylistUpdateResponseMessage extends AgentMessage {
if (options) {
this.id = options.id || this.generateId()
this.updated = options.keylist
this.setThread({
threadId: options.threadId,
})
}
}

Expand Down
Loading

0 comments on commit fb19af9

Please sign in to comment.