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

feat: added support for DRPC protocol #1753

Merged
merged 24 commits into from
Feb 23, 2024
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
5761855
implemented the basic DRPC protocol
wadeking98 Feb 8, 2024
3039165
Merge branch 'main' of https://github.com/openwallet-foundation/credo…
wadeking98 Feb 8, 2024
a967cd3
added improved error handling
wadeking98 Feb 9, 2024
968efd9
fixed tests and linting
wadeking98 Feb 9, 2024
9dbc405
revert devcontainer.env
wadeking98 Feb 9, 2024
61c1008
removed unused imports
wadeking98 Feb 10, 2024
9393f95
removed message handler architechture
wadeking98 Feb 12, 2024
c7f088b
moved drpc into module
wadeking98 Feb 13, 2024
de18c69
cleaned class/file names
wadeking98 Feb 13, 2024
db1269c
refactored code to be inline with other modules
wadeking98 Feb 14, 2024
1cc1864
added some curry to get the send/listen flow with encapsulation
wadeking98 Feb 14, 2024
2cc8c79
Merge branch 'main' into feat-drpc-protocol
wadeking98 Feb 15, 2024
3f54bbc
updated readme
wadeking98 Feb 15, 2024
4adbfc6
Merge branch 'feat-drpc-protocol' of https://github.com/wadeking98/ar…
wadeking98 Feb 15, 2024
5faccea
fixed readme formatting
wadeking98 Feb 16, 2024
662aaaa
Merge branch 'main' into feat-drpc-protocol
wadeking98 Feb 16, 2024
cb4880c
Merge branch 'main' into feat-drpc-protocol
wadeking98 Feb 21, 2024
8fd1d96
cleanup drpc module
wadeking98 Feb 22, 2024
b1c485a
Merge branch 'feat-drpc-protocol' of https://github.com/wadeking98/ar…
wadeking98 Feb 22, 2024
01f8b3b
quick bug fix
wadeking98 Feb 23, 2024
fe4f38f
fixed timeout test
wadeking98 Feb 23, 2024
7eebc05
Merge branch 'main' into feat-drpc-protocol
wadeking98 Feb 23, 2024
cb0aa24
update timeout
wadeking98 Feb 23, 2024
52dae58
Merge branch 'feat-drpc-protocol' of https://github.com/wadeking98/ar…
wadeking98 Feb 23, 2024
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
1 change: 0 additions & 1 deletion packages/core/src/modules/connections/ConnectionsApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ export interface SendPingOptions {
responseRequested?: boolean
withReturnRouting?: boolean
}
wadeking98 marked this conversation as resolved.
Show resolved Hide resolved

@injectable()
export class ConnectionsApi {
/**
Expand Down
98 changes: 98 additions & 0 deletions packages/drpc/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
# Change Log
wadeking98 marked this conversation as resolved.
Show resolved Hide resolved

All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.

## [0.4.2](https://github.com/hyperledger/aries-framework-javascript/compare/v0.4.1...v0.4.2) (2023-10-05)

**Note:** Version bump only for package @credo-ts/question-answer

## [0.4.1](https://github.com/hyperledger/aries-framework-javascript/compare/v0.4.0...v0.4.1) (2023-08-28)

### Features

- oob without handhsake improvements and routing ([#1511](https://github.com/hyperledger/aries-framework-javascript/issues/1511)) ([9e69cf4](https://github.com/hyperledger/aries-framework-javascript/commit/9e69cf441a75bf7a3c5556cf59e730ee3fce8c28))

# [0.4.0](https://github.com/hyperledger/aries-framework-javascript/compare/v0.3.3...v0.4.0) (2023-06-03)

### Bug Fixes

- thread id improvements ([#1311](https://github.com/hyperledger/aries-framework-javascript/issues/1311)) ([229ed1b](https://github.com/hyperledger/aries-framework-javascript/commit/229ed1b9540ca0c9380b5cca6c763fefd6628960))

- refactor!: remove Dispatcher.registerMessageHandler (#1354) ([78ecf1e](https://github.com/hyperledger/aries-framework-javascript/commit/78ecf1ed959c9daba1c119d03f4596f1db16c57c)), closes [#1354](https://github.com/hyperledger/aries-framework-javascript/issues/1354)

### Features

- outbound message send via session ([#1335](https://github.com/hyperledger/aries-framework-javascript/issues/1335)) ([582c711](https://github.com/hyperledger/aries-framework-javascript/commit/582c711728db12b7d38a0be2e9fa78dbf31b34c6))

### BREAKING CHANGES

- `Dispatcher.registerMessageHandler` has been removed in favour of `MessageHandlerRegistry.registerMessageHandler`. If you want to register message handlers in an extension module, you can use directly `agentContext.dependencyManager.registerMessageHandlers`.

Signed-off-by: Ariel Gentile <gentilester@gmail.com>

## [0.3.3](https://github.com/hyperledger/aries-framework-javascript/compare/v0.3.2...v0.3.3) (2023-01-18)

### Bug Fixes

- fix typing issues with typescript 4.9 ([#1214](https://github.com/hyperledger/aries-framework-javascript/issues/1214)) ([087980f](https://github.com/hyperledger/aries-framework-javascript/commit/087980f1adf3ee0bc434ca9782243a62c6124444))

### Features

- **indy-sdk:** add indy-sdk package ([#1200](https://github.com/hyperledger/aries-framework-javascript/issues/1200)) ([9933b35](https://github.com/hyperledger/aries-framework-javascript/commit/9933b35a6aa4524caef8a885e71b742cd0d7186b))

## [0.3.2](https://github.com/hyperledger/aries-framework-javascript/compare/v0.3.1...v0.3.2) (2023-01-04)

**Note:** Version bump only for package @credo-ts/question-answer

## [0.3.1](https://github.com/hyperledger/aries-framework-javascript/compare/v0.3.0...v0.3.1) (2022-12-27)

**Note:** Version bump only for package @credo-ts/question-answer

# [0.3.0](https://github.com/hyperledger/aries-framework-javascript/compare/v0.2.5...v0.3.0) (2022-12-22)

- refactor!: rename Handler to MessageHandler (#1161) ([5e48696](https://github.com/hyperledger/aries-framework-javascript/commit/5e48696ec16d88321f225628e6cffab243718b4c)), closes [#1161](https://github.com/hyperledger/aries-framework-javascript/issues/1161)
- feat(action-menu)!: move to separate package (#1049) ([e0df0d8](https://github.com/hyperledger/aries-framework-javascript/commit/e0df0d884b1a7816c7c638406606e45f6e169ff4)), closes [#1049](https://github.com/hyperledger/aries-framework-javascript/issues/1049)
- feat(question-answer)!: separate logic to a new module (#1040) ([97d3073](https://github.com/hyperledger/aries-framework-javascript/commit/97d3073aa9300900740c3e8aee8233d38849293d)), closes [#1040](https://github.com/hyperledger/aries-framework-javascript/issues/1040)

### BREAKING CHANGES

- Handler has been renamed to MessageHandler to be more descriptive, along with related types and methods. This means:

Handler is now MessageHandler
HandlerInboundMessage is now MessageHandlerInboundMessage
Dispatcher.registerHandler is now Dispatcher.registerMessageHandlers

- action-menu module has been removed from the core and moved to a separate package. To integrate it in an Agent instance, it can be injected in constructor like this:

```ts
const agent = new Agent({
config: {
/* config */
},
dependencies: agentDependencies,
modules: {
actionMenu: new ActionMenuModule(),
/* other custom modules */
},
})
```

Then, module API can be accessed in `agent.modules.actionMenu`.

- question-answer module has been removed from the core and moved to a separate package. To integrate it in an Agent instance, it can be injected in constructor like this:

```ts
const agent = new Agent({
config: {
/* config */
},
dependencies: agentDependencies,
modules: {
questionAnswer: new QuestionAnswerModule(),
/* other custom modules */
},
})
```

Then, module API can be accessed in `agent.modules.questionAnswer`.
73 changes: 73 additions & 0 deletions packages/drpc/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
<p align="center">
<br />
<img
alt="Credo Logo"
src="https://github.com/openwallet-foundation/credo-ts/blob/c7886cb8377ceb8ee4efe8d264211e561a75072d/images/credo-logo.png"
height="250px"
/>
</p>
<h1 align="center"><b>Credo DRPC Module</b></h1>
<p align="center">
<a
href="https://raw.githubusercontent.com/openwallet-foundation/credo-ts/main/LICENSE"
><img
alt="License"
src="https://img.shields.io/badge/License-Apache%202.0-blue.svg"
/></a>
<a href="https://www.typescriptlang.org/"
><img
alt="typescript"
src="https://img.shields.io/badge/%3C%2F%3E-TypeScript-%230074c1.svg"
/></a>
<a href="https://www.npmjs.com/package/@credo-ts/question-answer"
><img
alt="@credo-ts/question-answer version"
src="https://img.shields.io/npm/v/@credo-ts/question-answer"
/></a>

</p>
<br />

DRPC module for [Credo](https://github.com/openwallet-foundation/credo-ts.git). Implements [Aries RFC 0804](https://github.com/hyperledger/aries-rfcs/blob/ea87d2e37640ef944568e3fa01df1f36fe7f0ff3/features/0804-didcomm-rpc/README.md).

### Quick start

In order for this module to work, we have to inject it into the agent to access agent functionality. See the example for more information.

### Example of usage

```ts
import { DrpcModule } from '@credo-ts/drpc'

const agent = new Agent({
config: {
/* config */
},
dependencies: agentDependencies,
modules: {
drpc: new DrpcModule(),
/* other custom modules */
},
})

await agent.initialize()

// Send a request to the specified connection
const responseListener = await senderAgent.modules.drpc.sendRequest(connectionId, {
jsonrpc: '2.0',
method: 'hello',
id: 1,
})

// Listen for any incoming requests
const { request, sendResponse } = await receiverAgent.modules.drpc.recvRequest()

// Process the recieved request and create a response
wadeking98 marked this conversation as resolved.
Show resolved Hide resolved
const result =
request.method === 'hello'
? { jsonrpc: '2.0', result: 'Hello world!', id: request.id }
: { jsonrpc: '2.0', error: { code: DrpcErrorCode.METHOD_NOT_FOUND, message: 'Method not found' } }

// Send the response back
await sendResponse(result)
```
13 changes: 13 additions & 0 deletions packages/drpc/jest.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import type { Config } from '@jest/types'

import base from '../../jest.config.base'

import packageJson from './package.json'

const config: Config.InitialOptions = {
...base,
displayName: packageJson.name,
setupFilesAfterEnv: ['./tests/setup.ts'],
}

export default config
37 changes: 37 additions & 0 deletions packages/drpc/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
{
"name": "@credo-ts/drpc",
"main": "build/index",
"types": "build/index",
"version": "0.4.2",
"files": [
"build"
],
"license": "Apache-2.0",
"publishConfig": {
"access": "public"
},
"homepage": "https://github.com/openwallet-foundation/credo-ts/tree/main/packages/drpc",
"repository": {
"type": "git",
"url": "https://github.com/openwallet-foundation/credo-ts",
"directory": "packages/drpc"
},
"scripts": {
"build": "yarn run clean && yarn run compile",
"clean": "rimraf ./build",
"compile": "tsc -p tsconfig.build.json",
"prepublishOnly": "yarn run build",
"test": "jest"
},
"dependencies": {
"@credo-ts/core": "0.4.2",
"class-transformer": "^0.5.1",
"class-validator": "0.14.1"
},
"devDependencies": {
"@credo-ts/node": "0.4.2",
"reflect-metadata": "^0.1.13",
"rimraf": "^4.4.0",
"typescript": "~4.9.5"
}
}
152 changes: 152 additions & 0 deletions packages/drpc/src/DrpcApi.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
import type { DrpcRequest, DrpcResponse, DrpcRequestMessage, DrpcResponseMessage } from './messages'
import type { DrpcMessageRecord } from './repository/DrpcMessageRecord'
import type { ConnectionRecord } from '@credo-ts/core'

import {
AgentContext,
MessageHandlerRegistry,
MessageSender,
OutboundMessageContext,
injectable,
ConnectionService,
} from '@credo-ts/core'

import { DrpcRequestHandler, DrpcResponseHandler } from './handlers'
import { DrpcService } from './services'

@injectable()
export class DrpcApi {
private drpcMessageService: DrpcService
private messageSender: MessageSender
private connectionService: ConnectionService
private agentContext: AgentContext

public constructor(
messageHandlerRegistry: MessageHandlerRegistry,
drpcMessageService: DrpcService,
messageSender: MessageSender,
connectionService: ConnectionService,
agentContext: AgentContext
) {
this.drpcMessageService = drpcMessageService
this.messageSender = messageSender
this.connectionService = connectionService
this.agentContext = agentContext
this.registerMessageHandlers(messageHandlerRegistry)
}

/**
* sends the request object to the connection and returns a function that will resolve to the response
* @param connectionId the connection to send the request to
* @param request the request object
* @returns curried function that waits for the response
*/
public async sendRequest(connectionId: string, request: DrpcRequest): Promise<() => Promise<DrpcResponse>> {
const connection = await this.connectionService.getById(this.agentContext, connectionId)
const { message: drpcMessage, record: drpcMessageRecord } = await this.drpcMessageService.createRequestMessage(
this.agentContext,
request,
connection.id
)
const messageId = drpcMessage.id
await this.sendMessage(connection, drpcMessage, drpcMessageRecord)
return this.recvResponse.bind(this, messageId)
wadeking98 marked this conversation as resolved.
Show resolved Hide resolved
}

/**
* Listen for a response that has a thread id matching the provided messageId
* @param messageId the id to match the response to
* @returns the response object
*/
private async recvResponse(messageId: string): Promise<DrpcResponse> {
return new Promise((resolve) => {
const listener = ({
drpcMessageRecord,
removeListener,
}: {
drpcMessageRecord: DrpcMessageRecord
removeListener: () => void
}) => {
const message = drpcMessageRecord.message
if (drpcMessageRecord.threadId === messageId) {
removeListener()
resolve(message as DrpcResponse)
}
}

this.drpcMessageService.createResponseListener(listener)
})
}

/**
* Listen for a request and returns the request object and a function to send the response
* @returns the request object and a function to send the response
*/
public async recvRequest(): Promise<{
wadeking98 marked this conversation as resolved.
Show resolved Hide resolved
request: DrpcRequest
sendResponse: (response: DrpcResponse) => Promise<void>
}> {
return new Promise((resolve) => {
const listener = ({
drpcMessageRecord,
removeListener,
}: {
drpcMessageRecord: DrpcMessageRecord
removeListener: () => void
}) => {
const message = drpcMessageRecord.message
removeListener()
resolve({
sendResponse: async (response: DrpcResponse) => {
await this.sendResponse(drpcMessageRecord.connectionId, drpcMessageRecord.threadId, response)
},
request: message as DrpcRequest,
})
}

this.drpcMessageService.createRequestListener(listener)
})
}

/**
* Sends a drpc response to a connection
* @param connectionId the connection id to use
* @param threadId the thread id to respond to
* @param response the drpc response object to send
*/
private async sendResponse(connectionId: string, threadId: string, response: DrpcResponse): Promise<void> {
wadeking98 marked this conversation as resolved.
Show resolved Hide resolved
const connection = await this.connectionService.getById(this.agentContext, connectionId)
const drpcMessageRecord = await this.drpcMessageService.findByThreadAndConnectionId(
this.agentContext,
connectionId,
threadId
)
if (!drpcMessageRecord) {
throw new Error(`No request found for threadId ${threadId}`)
}
const { message, record } = await this.drpcMessageService.createResponseMessage(
this.agentContext,
response,
drpcMessageRecord
)
await this.sendMessage(connection, message, record)
}

private async sendMessage(
connection: ConnectionRecord,
message: DrpcRequestMessage | DrpcResponseMessage,
messageRecord: DrpcMessageRecord
): Promise<void> {
const outboundMessageContext = new OutboundMessageContext(message, {
agentContext: this.agentContext,
connection,
associatedRecord: messageRecord,
})
await this.messageSender.sendMessage(outboundMessageContext)
}

private registerMessageHandlers(messageHandlerRegistry: MessageHandlerRegistry) {
messageHandlerRegistry.registerMessageHandler(new DrpcRequestHandler(this.drpcMessageService))
messageHandlerRegistry.registerMessageHandler(new DrpcResponseHandler(this.drpcMessageService))
}
}
Loading
Loading