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: add internal polling inbound transporter #323

Merged
Show file tree
Hide file tree
Changes from all commits
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
57 changes: 7 additions & 50 deletions docs/getting-started/1-transports.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,65 +27,22 @@ agent.setOutboundTransporter(wsOutboundTransporter)

## Inbound Transport

Inbound transports allow you to receive messages from other agents. No inbound transports are exported from the framework at the moment. See the example transports below to get started.
Inbound transports allow you to receive messages from other agents. Only `PollingInboundTransporter` is exported from the framework at the moment. Make sure you provide a `mediatorUrl` if using the polling inbound transporters. See the example transports below for other inbound transports.

```ts
import { Agent, PollingInboundTransporter } from 'aries-framework'

const agentConfig = {
// ... agent config ... //
mediatorUrl: 'https://your-afj-mediator-url.com',
}

const someInboundTransport = new SomeInboundTransport()

const agent = new Agent(agentConfig)
agent.setInboundTransporter(someInboundTransport)
```

### Example `PollingInboundTransporter`

This is an example of a polling inbound transport. This is mostly useful for edge agents, that use a mediator to receive inbound messages. Make sure to provide a `mediatorUrl` in the agent config when using this transport. See [3. Routing](./3-routing.md) for more information.

```ts
import { Agent, InboundTransporter } from 'aries-framework'

// In React Native you don't have to import node-fetch
// Fetch is globally available in React Native
import fetch from 'node-fetch'

class PollingInboundTransporter implements InboundTransporter {
public stop: boolean
// Construct polling inbound transporter with optional polling interval in ms
const pollingInboundTransporter = new PollingInboundTransporter(5000)

public constructor() {
this.stop = false
}
public async start(agent: Agent) {
await this.registerMediator(agent)
}

public async registerMediator(agent: Agent) {
const mediatorUrl = agent.getMediatorUrl()

if (!mediatorUrl) {
throw new AriesFrameworkError(
'Agent has no mediator URL. Make sure to provide the `mediatorUrl` in the agent config.'
)
}

const mediatorInvitationUrl = await get(`${mediatorUrl}/invitation`)
const { verkey: mediatorVerkey } = JSON.parse(await get(`${mediatorUrl}/`))
await agent.routing.provision({
verkey: mediatorVerkey,
invitationUrl: mediatorInvitationUrl,
})
this.pollDownloadMessages(agent)
}

private async pollDownloadMessages(agent: Agent) {
while (!this.stop) {
await agent.routing.downloadMessages()
await sleep(5000)
}
}
}
agent.setInboundTransporter(pollingInboundTransporter)
```

### Example `HttpInboundTransporter`
Expand Down
2 changes: 0 additions & 2 deletions src/__tests__/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,6 @@ export const genesisPath = process.env.GENESIS_TXN_PATH

export const publicDidSeed = process.env.TEST_AGENT_PUBLIC_DID_SEED ?? '000000000000000000000000Trustee9'

export const sleep = (ms: number) => new Promise((res) => setTimeout(res, ms))

export function getBaseConfig(name: string, extraConfig: Partial<InitConfig> = {}) {
const config: InitConfig = {
label: `Agent: ${name}`,
Expand Down
3 changes: 2 additions & 1 deletion src/__tests__/ledger.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ import indy from 'indy-sdk'

import { Agent } from '../agent/Agent'
import { DID_IDENTIFIER_REGEX, VERKEY_REGEX, isFullVerkey, isAbbreviatedVerkey } from '../utils/did'
import { sleep } from '../utils/sleep'

import { genesisPath, getBaseConfig, sleep } from './helpers'
import { genesisPath, getBaseConfig } from './helpers'
import testLogger from './logger'

const faberConfig = getBaseConfig('Faber Ledger', { genesisPath })
Expand Down
49 changes: 49 additions & 0 deletions src/transport/PollingInboundTransporter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import type { Agent } from '../agent/Agent'
import type { InboundTransporter } from './InboundTransporter'

import { AriesFrameworkError } from '../error/AriesFrameworkError'
import { fetch } from '../utils/fetch'
import { sleep } from '../utils/sleep'

export class PollingInboundTransporter implements InboundTransporter {
public stop: boolean
private pollingInterval: number

public constructor(pollingInterval = 5000) {
this.stop = false
this.pollingInterval = pollingInterval
}

public async start(agent: Agent) {
await this.registerMediator(agent)
}

public async registerMediator(agent: Agent) {
const mediatorUrl = agent.getMediatorUrl()

if (!mediatorUrl) {
throw new AriesFrameworkError(
'Agent has no mediator URL. Make sure to provide the `mediatorUrl` in the agent config.'
)
}

const invitationResponse = await fetch(`${mediatorUrl}/invitation`)
const invitationUrl = await invitationResponse.text()

const mediatorDidResponse = await fetch(`${mediatorUrl}`)
const { verkey } = await mediatorDidResponse.json()

await agent.routing.provision({
verkey,
invitationUrl,
})
this.pollDownloadMessages(agent)
}

private async pollDownloadMessages(agent: Agent) {
while (!this.stop) {
await agent.routing.downloadMessages()
await sleep(this.pollingInterval)
}
}
}
1 change: 1 addition & 0 deletions src/transport/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ export * from './InboundTransporter'
export * from './OutboundTransporter'
export * from './HttpOutboundTransporter'
export * from './WsOutboundTransporter'
export * from './PollingInboundTransporter'
3 changes: 3 additions & 0 deletions src/utils/sleep.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export function sleep(ms: number) {
return new Promise((res) => setTimeout(res, ms))
}
42 changes: 2 additions & 40 deletions tests/__tests__/e2e.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import type { InboundTransporter } from '../../src'

import { Agent, AriesFrameworkError, HttpOutboundTransporter } from '../../src'
import { getBaseConfig, sleep, waitForBasicMessage } from '../../src/__tests__/helpers'
import { Agent, HttpOutboundTransporter, PollingInboundTransporter } from '../../src'
import { getBaseConfig, waitForBasicMessage } from '../../src/__tests__/helpers'
import logger from '../../src/__tests__/logger'
import { get } from '../http'

Expand Down Expand Up @@ -93,39 +91,3 @@ describe('with mediator', () => {
expect(basicMessage.content).toBe(message)
})
})

class PollingInboundTransporter implements InboundTransporter {
public stop: boolean

public constructor() {
this.stop = false
}
public async start(agent: Agent) {
await this.registerMediator(agent)
}

public async registerMediator(agent: Agent) {
const mediatorUrl = agent.getMediatorUrl()

if (!mediatorUrl) {
throw new AriesFrameworkError(
'Agent has no mediator URL. Make sure to provide the `mediatorUrl` in the agent config.'
)
}

const mediatorInvitationUrl = await get(`${mediatorUrl}/invitation`)
const { verkey: mediatorVerkey } = JSON.parse(await get(`${mediatorUrl}/`))
await agent.routing.provision({
verkey: mediatorVerkey,
invitationUrl: mediatorInvitationUrl,
})
this.pollDownloadMessages(agent)
}

private async pollDownloadMessages(agent: Agent) {
while (!this.stop) {
await agent.routing.downloadMessages()
await sleep(5000)
}
}
}