Skip to content

Commit ab403c9

Browse files
authored
chore: update extension module sample (openwallet-foundation#1083)
Signed-off-by: Ariel Gentile <[email protected]>
1 parent bd01e40 commit ab403c9

16 files changed

+281
-34
lines changed

Diff for: jest.config.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,11 @@ import base from './jest.config.base'
55
const config: Config.InitialOptions = {
66
...base,
77
roots: ['<rootDir>'],
8-
projects: ['<rootDir>/packages/*', '<rootDir>/tests/jest.config.ts'],
8+
projects: [
9+
'<rootDir>/packages/*',
10+
'<rootDir>/tests/jest.config.ts',
11+
'<rootDir>/samples/extension-module/jest.config.ts',
12+
],
913
}
1014

1115
export default config

Diff for: packages/core/src/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ export * from './wallet'
3838
export type { TransportSession } from './agent/TransportService'
3939
export { TransportService } from './agent/TransportService'
4040
export { Attachment } from './decorators/attachment/Attachment'
41+
export { ReturnRouteTypes } from './decorators/transport/TransportDecorator'
4142

4243
export * from './plugins'
4344
export * from './transport'

Diff for: samples/extension-module/README.md

+18-11
Original file line numberDiff line numberDiff line change
@@ -16,35 +16,42 @@ This example consists of a module that implements a very simple request-response
1616
- Define events (inherited from `BaseEvent`)
1717
- Create a singleton service class that manages records and repository, and also trigger events using Agent's `EventEmitter`
1818
- Create a singleton api class that registers handlers in Agent's `Dispatcher` and provides a simple API to do requests and responses, with the aid of service classes and Agent's `MessageSender`
19-
- Create a module class that registers all the above on the dependency manager so it can be be injected from the `Agent` instance.
19+
- Create a module class that registers all the above on the dependency manager so it can be be injected from the `Agent` instance, and also register the features (such as protocols) the module adds to the Agent.
2020

2121
## Usage
2222

23-
In order to use this module, you first need to register `DummyModule` on the `Agent` instance. After that you need to resolve the `DummyApi` to interact with the public api of the module. Make sure to register and resolve the api **before** initializing the agent.
23+
In order to use this module, you first need to register `DummyModule` on the `Agent` instance. This can be done by adding an entry for it in `AgentOptions`'s modules property:
2424

2525
```ts
26-
import { DummyModule, DummyApi } from './dummy'
27-
28-
const agent = new Agent(/** agent config... */)
26+
import { DummyModule } from './dummy'
2927

3028
// Register the module with it's dependencies
31-
agent.dependencyManager.registerModules(new DummyModule())
32-
33-
const dummyApi = agent.dependencyManager.resolve(DummyApi)
29+
const agent = new Agent({
30+
config: {
31+
/* config */
32+
},
33+
dependencies: agentDependencies,
34+
modules: {
35+
dummy: new DummyModule({
36+
/* module config */
37+
}),
38+
/* other custom modules */
39+
},
40+
})
3441

3542
await agent.initialize()
3643
```
3744

38-
Then, Dummy module API methods can be called, and events listeners can be created:
45+
Then, Dummy module API methods can be called from `agent.modules.dummy` namespace, and events listeners can be created:
3946

4047
```ts
4148
agent.events.on(DummyEventTypes.StateChanged, async (event: DummyStateChangedEvent) => {
4249
if (event.payload.dummyRecord.state === DummyState.RequestReceived) {
43-
await dummyApi.respond(event.payload.dummyRecord)
50+
await agent.modules.dummy.respond(event.payload.dummyRecord)
4451
}
4552
})
4653

47-
const record = await dummyApi.request(connection)
54+
const record = await agent.modules.dummy.request(connection)
4855
```
4956

5057
## Run demo

Diff for: samples/extension-module/dummy/DummyModule.ts

+11-1
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,28 @@
1+
import type { DummyModuleConfigOptions } from './DummyModuleConfig'
12
import type { DependencyManager, FeatureRegistry, Module } from '@aries-framework/core'
23

34
import { Protocol } from '@aries-framework/core'
45

56
import { DummyApi } from './DummyApi'
7+
import { DummyModuleConfig } from './DummyModuleConfig'
68
import { DummyRepository } from './repository'
79
import { DummyService } from './services'
810

911
export class DummyModule implements Module {
10-
public api = DummyApi
12+
public readonly config: DummyModuleConfig
13+
public readonly api = DummyApi
14+
15+
public constructor(config?: DummyModuleConfigOptions) {
16+
this.config = new DummyModuleConfig(config)
17+
}
1118

1219
public register(dependencyManager: DependencyManager, featureRegistry: FeatureRegistry) {
1320
// Api
1421
dependencyManager.registerContextScoped(DummyApi)
1522

23+
// Config
24+
dependencyManager.registerInstance(DummyModuleConfig, this.config)
25+
1626
dependencyManager.registerSingleton(DummyRepository)
1727
dependencyManager.registerSingleton(DummyService)
1828

Diff for: samples/extension-module/dummy/DummyModuleConfig.ts

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/**
2+
* DummyModuleConfigOptions defines the interface for the options of the DummyModuleConfig class.
3+
* This can contain optional parameters that have default values in the config class itself.
4+
*/
5+
export interface DummyModuleConfigOptions {
6+
/**
7+
* Whether to automatically accept request messages.
8+
*
9+
* @default false
10+
*/
11+
autoAcceptRequests?: boolean
12+
}
13+
14+
export class DummyModuleConfig {
15+
private options: DummyModuleConfigOptions
16+
17+
public constructor(options?: DummyModuleConfigOptions) {
18+
this.options = options ?? {}
19+
}
20+
21+
/** See {@link DummyModuleConfigOptions.autoAcceptRequests} */
22+
public get autoAcceptRequests() {
23+
return this.options.autoAcceptRequests ?? false
24+
}
25+
}

Diff for: samples/extension-module/dummy/handlers/DummyRequestHandler.ts

+7-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import type { DummyService } from '../services'
22
import type { Handler, HandlerInboundMessage } from '@aries-framework/core'
33

4+
import { createOutboundMessage } from '@aries-framework/core'
5+
46
import { DummyRequestMessage } from '../messages'
57

68
export class DummyRequestHandler implements Handler {
@@ -12,8 +14,11 @@ export class DummyRequestHandler implements Handler {
1214
}
1315

1416
public async handle(inboundMessage: HandlerInboundMessage<DummyRequestHandler>) {
15-
inboundMessage.assertReadyConnection()
17+
const connection = inboundMessage.assertReadyConnection()
18+
const responseMessage = await this.dummyService.processRequest(inboundMessage)
1619

17-
await this.dummyService.processRequest(inboundMessage)
20+
if (responseMessage) {
21+
return createOutboundMessage(connection, responseMessage)
22+
}
1823
}
1924
}

Diff for: samples/extension-module/dummy/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@ export * from './messages'
44
export * from './services'
55
export * from './repository'
66
export * from './DummyModule'
7+
export * from './DummyModuleConfig'

Diff for: samples/extension-module/dummy/messages/DummyRequestMessage.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { AgentMessage, IsValidMessageType, parseMessageType } from '@aries-framework/core'
1+
import { AgentMessage, IsValidMessageType, parseMessageType, ReturnRouteTypes } from '@aries-framework/core'
22

33
export interface DummyRequestMessageOptions {
44
id?: string
@@ -11,6 +11,8 @@ export class DummyRequestMessage extends AgentMessage {
1111
if (options) {
1212
this.id = options.id ?? this.generateId()
1313
}
14+
15+
this.setReturnRouting(ReturnRouteTypes.all)
1416
}
1517

1618
@IsValidMessageType(DummyRequestMessage.type)

Diff for: samples/extension-module/dummy/services/DummyService.ts

+11-2
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import type { Query, AgentContext, ConnectionRecord, InboundMessageContext } fro
33

44
import { injectable, JsonTransformer, EventEmitter } from '@aries-framework/core'
55

6+
import { DummyModuleConfig } from '../DummyModuleConfig'
67
import { DummyRequestMessage, DummyResponseMessage } from '../messages'
78
import { DummyRecord } from '../repository/DummyRecord'
89
import { DummyRepository } from '../repository/DummyRepository'
@@ -14,8 +15,14 @@ import { DummyEventTypes } from './DummyEvents'
1415
export class DummyService {
1516
private dummyRepository: DummyRepository
1617
private eventEmitter: EventEmitter
18+
private dummyModuleConfig: DummyModuleConfig
1719

18-
public constructor(dummyRepository: DummyRepository, eventEmitter: EventEmitter) {
20+
public constructor(
21+
dummyModuleConfig: DummyModuleConfig,
22+
dummyRepository: DummyRepository,
23+
eventEmitter: EventEmitter
24+
) {
25+
this.dummyModuleConfig = dummyModuleConfig
1926
this.dummyRepository = dummyRepository
2027
this.eventEmitter = eventEmitter
2128
}
@@ -80,7 +87,9 @@ export class DummyService {
8087

8188
this.emitStateChangedEvent(messageContext.agentContext, record, null)
8289

83-
return record
90+
if (this.dummyModuleConfig.autoAcceptRequests) {
91+
return await this.createResponse(messageContext.agentContext, record)
92+
}
8493
}
8594

8695
/**

Diff for: samples/extension-module/jest.config.ts

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import type { Config } from '@jest/types'
2+
3+
import base from '../../jest.config.base'
4+
5+
import packageJson from './package.json'
6+
7+
const config: Config.InitialOptions = {
8+
...base,
9+
displayName: packageJson.name,
10+
setupFilesAfterEnv: ['./tests/setup.ts'],
11+
}
12+
13+
export default config

Diff for: samples/extension-module/requester.ts

+12-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
11
import type { DummyRecord, DummyStateChangedEvent } from './dummy'
22

3-
import { Agent, AriesFrameworkError, ConsoleLogger, LogLevel, WsOutboundTransport } from '@aries-framework/core'
3+
import {
4+
HttpOutboundTransport,
5+
Agent,
6+
AriesFrameworkError,
7+
ConsoleLogger,
8+
LogLevel,
9+
WsOutboundTransport,
10+
} from '@aries-framework/core'
411
import { agentDependencies } from '@aries-framework/node'
512
import { filter, first, firstValueFrom, map, ReplaySubject, timeout } from 'rxjs'
613

@@ -10,6 +17,7 @@ const run = async () => {
1017
// Create transports
1118
const port = process.env.RESPONDER_PORT ? Number(process.env.RESPONDER_PORT) : 3002
1219
const wsOutboundTransport = new WsOutboundTransport()
20+
const httpOutboundTransport = new HttpOutboundTransport()
1321

1422
// Setup the agent
1523
const agent = new Agent({
@@ -19,7 +27,7 @@ const run = async () => {
1927
id: 'requester',
2028
key: 'requester',
2129
},
22-
logger: new ConsoleLogger(LogLevel.test),
30+
logger: new ConsoleLogger(LogLevel.info),
2331
autoAcceptConnections: true,
2432
},
2533
modules: {
@@ -30,6 +38,7 @@ const run = async () => {
3038

3139
// Register transports
3240
agent.registerOutboundTransport(wsOutboundTransport)
41+
agent.registerOutboundTransport(httpOutboundTransport)
3342

3443
// Now agent will handle messages and events from Dummy protocol
3544

@@ -59,7 +68,7 @@ const run = async () => {
5968

6069
// Send a dummy request and wait for response
6170
const record = await agent.modules.dummy.request(connectionRecord.id)
62-
agent.config.logger.info(`Request received for Dummy Record: ${record.id}`)
71+
agent.config.logger.info(`Request sent for Dummy Record: ${record.id}`)
6372

6473
const dummyRecord = await firstValueFrom(subject)
6574
agent.config.logger.info(`Response received for Dummy Record: ${dummyRecord.id}`)

Diff for: samples/extension-module/responder.ts

+14-12
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import type { DummyStateChangedEvent } from './dummy'
22
import type { Socket } from 'net'
33

4-
import { Agent, ConsoleLogger, LogLevel, WsOutboundTransport } from '@aries-framework/core'
4+
import { Agent, ConsoleLogger, LogLevel } from '@aries-framework/core'
55
import { agentDependencies, HttpInboundTransport, WsInboundTransport } from '@aries-framework/node'
66
import express from 'express'
77
import { Server } from 'ws'
@@ -11,35 +11,34 @@ import { DummyModule, DummyEventTypes, DummyState } from './dummy'
1111
const run = async () => {
1212
// Create transports
1313
const port = process.env.RESPONDER_PORT ? Number(process.env.RESPONDER_PORT) : 3002
14+
const autoAcceptRequests = true
1415
const app = express()
1516
const socketServer = new Server({ noServer: true })
1617

1718
const httpInboundTransport = new HttpInboundTransport({ app, port })
1819
const wsInboundTransport = new WsInboundTransport({ server: socketServer })
19-
const wsOutboundTransport = new WsOutboundTransport()
2020

2121
// Setup the agent
2222
const agent = new Agent({
2323
config: {
2424
label: 'Dummy-powered agent - responder',
25-
endpoints: [`ws://localhost:${port}`],
25+
endpoints: [`http://localhost:${port}`],
2626
walletConfig: {
2727
id: 'responder',
2828
key: 'responder',
2929
},
30-
logger: new ConsoleLogger(LogLevel.test),
30+
logger: new ConsoleLogger(LogLevel.debug),
3131
autoAcceptConnections: true,
3232
},
3333
modules: {
34-
dummy: new DummyModule(),
34+
dummy: new DummyModule({ autoAcceptRequests }),
3535
},
3636
dependencies: agentDependencies,
3737
})
3838

3939
// Register transports
4040
agent.registerInboundTransport(httpInboundTransport)
4141
agent.registerInboundTransport(wsInboundTransport)
42-
agent.registerInboundTransport(wsOutboundTransport)
4342

4443
// Allow to create invitation, no other way to ask for invitation yet
4544
app.get('/invitation', async (req, res) => {
@@ -58,12 +57,15 @@ const run = async () => {
5857
})
5958
})
6059

61-
// Subscribe to dummy record events
62-
agent.events.on(DummyEventTypes.StateChanged, async (event: DummyStateChangedEvent) => {
63-
if (event.payload.dummyRecord.state === DummyState.RequestReceived) {
64-
await agent.modules.dummy.respond(event.payload.dummyRecord.id)
65-
}
66-
})
60+
// If autoAcceptRequests is enabled, the handler will automatically respond
61+
// (no need to subscribe to event and manually accept)
62+
if (!autoAcceptRequests) {
63+
agent.events.on(DummyEventTypes.StateChanged, async (event: DummyStateChangedEvent) => {
64+
if (event.payload.dummyRecord.state === DummyState.RequestReceived) {
65+
await agent.modules.dummy.respond(event.payload.dummyRecord.id)
66+
}
67+
})
68+
}
6769

6870
agent.config.logger.info(`Responder listening to port ${port}`)
6971
}

0 commit comments

Comments
 (0)