Skip to content

Commit

Permalink
feat: OpenAPI server in CLI
Browse files Browse the repository at this point in the history
  • Loading branch information
simonas-notcat committed Sep 14, 2020
1 parent 9a35ee9 commit ccdd6a7
Show file tree
Hide file tree
Showing 9 changed files with 135 additions and 2 deletions.
1 change: 1 addition & 0 deletions docs/api/daf-express.agentrouteroptions.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,5 @@ export interface AgentRouterOptions
| [exposedMethods](./daf-express.agentrouteroptions.exposedmethods.md) | Array<string> | List of exposed methods |
| [extraMethods](./daf-express.agentrouteroptions.extramethods.md) | Array<string> | List of extra methods |
| [getAgentForRequest](./daf-express.agentrouteroptions.getagentforrequest.md) | (req: Request) =&gt; Promise&lt;[IAgent](./daf-core.iagent.md)<!-- -->&gt; | Function that returns configured agent for specific request |
| [serveSchema](./daf-express.agentrouteroptions.serveschema.md) | boolean | If set to <code>true</code>, router will serve OpenAPI schema JSON on <code>/</code> route |

13 changes: 13 additions & 0 deletions docs/api/daf-express.agentrouteroptions.serveschema.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->

[Home](./index.md) &gt; [daf-express](./daf-express.md) &gt; [AgentRouterOptions](./daf-express.agentrouteroptions.md) &gt; [serveSchema](./daf-express.agentrouteroptions.serveschema.md)

## AgentRouterOptions.serveSchema property

If set to `true`<!-- -->, router will serve OpenAPI schema JSON on `/` route

<b>Signature:</b>

```typescript
serveSchema?: boolean;
```
1 change: 1 addition & 0 deletions packages/daf-cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
"date-fns": "^2.8.1",
"debug": "^4.1.1",
"dotenv": "^8.2.0",
"express": "^4.17.1",
"graphql": "^15.0.0",
"graphql-tools": "^6.0.0",
"inquirer": "^7.0.0",
Expand Down
1 change: 1 addition & 0 deletions packages/daf-cli/src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import './msg'
import './version'
import './crypto'
import './execute'
import './server'
import './setup'

if (!process.argv.slice(2).length) {
Expand Down
1 change: 0 additions & 1 deletion packages/daf-cli/src/graphql.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ program
.command('graphql')
.description('GraphQL server')
.option('-p, --port <port>', 'Port')
.option('-i, --interval <seconds>', 'Poll for new messages with interval of <seconds>')
.action(async (cmd) => {
const agent = getAgent(program.config)
const { typeDefs, resolvers } = createSchema({
Expand Down
100 changes: 100 additions & 0 deletions packages/daf-cli/src/server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import express from 'express'
import program from 'commander'
import { AgentRouter } from 'daf-express'
import { getAgent } from './setup'

program
.command('server')
.description('Launch OpenAPI server')
.option('--port <port>', 'Port', '3332')
.option('--hostname <string>', 'Server hostname', 'localhost')
.option('--https <boolean>', 'Use https instead of http', true)
.option('--createDefaultIdentity <boolean>', 'Should the agent create default web did', true)
.option('--messagingServiceEndpoint <path>', 'Path of the messaging service endpoint', '/messaging')
.option(
'--exposedMethods <string>',
'Comma separated list of exposed agent methods (example: "resolveDid,handleMessage")',
)
.action(async (options) => {
const app = express()
const agent = getAgent(program.config)

const exposedMethods = options.exposedMethods
? options.exposedMethods.split(',')
: agent.availableMethods()

const agentRouter = AgentRouter({
getAgentForRequest: async (req) => agent,
exposedMethods,
serveSchema: true,
})

app.use('/', agentRouter)

if (options.createDefaultIdentity) {
let serverIdentity = await agent.identityManagerGetOrCreateIdentity({
provider: 'did:web',
alias: options.hostname,
})
console.log('Created:', serverIdentity.did)

if (options.messagingServiceEndpoint) {
const serviceEndpoint =
(options.https ? 'https://' : 'http://') + options.hostname + options.messagingServiceEndpoint

await agent.identityManagerAddService({
did: serverIdentity.did,
service: {
id: 'msg',
type: 'Messaging',
serviceEndpoint,
},
})
console.log('Added endpoint', serviceEndpoint)

app.post(serviceEndpoint, express.text({ type: '*/*' }), async (req, res) => {
try {
const message = await agent.handleMessage({ raw: req.body, save: true })
res.json({ id: message.id })
} catch (e) {
console.log(e)
res.send(e.message)
}
})
}

app.get('/.well-known/did.json', async (req, res) => {
serverIdentity = await agent.identityManagerGetOrCreateIdentity({
provider: 'did:web',
alias: options.hostname,
})

const didDoc = {
'@context': 'https://w3id.org/did/v1',
id: serverIdentity.did,
publicKey: serverIdentity.keys.map((key) => ({
id: serverIdentity.did + '#' + key.kid,
type: key.type === 'Secp256k1' ? 'Secp256k1VerificationKey2018' : 'Ed25519VerificationKey2018',
owner: serverIdentity.did,
publicKeyHex: key.publicKeyHex,
})),
authentication: serverIdentity.keys.map((key) => ({
type:
key.type === 'Secp256k1'
? 'Secp256k1SignatureAuthentication2018'
: 'Ed25519SignatureAuthentication2018',
publicKey: serverIdentity.did + '#' + key.kid,
})),
service: serverIdentity.services,
}

res.json(didDoc)
})
}

app.listen(options.port, () => {
console.log(`🚀 Server ready at http://${options.hostname}:${options.port}`)
console.log('Enabled agent methods', JSON.stringify(agent.availableMethods()))
console.log('Exposed methods', JSON.stringify(exposedMethods))
})
})
1 change: 1 addition & 0 deletions packages/daf-cli/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
{ "path": "../daf-resolver-universal" },
{ "path": "../daf-resolver" },
{ "path": "../daf-rest" },
{ "path": "../daf-express" },
{ "path": "../daf-selective-disclosure" },
{ "path": "../daf-typeorm" },
{ "path": "../daf-url" },
Expand Down
18 changes: 17 additions & 1 deletion packages/daf-express/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@

import { IAgent } from 'daf-core'
import { Request, Response, NextFunction, Router, json } from 'express'
import { supportedMethods } from 'daf-rest'
import { supportedMethods, openApiSchema } from 'daf-rest'
import Debug from 'debug'

interface RequestWithAgent extends Request {
Expand All @@ -47,6 +47,11 @@ export interface AgentRouterOptions {
* List of extra methods
*/
extraMethods?: Array<string>

/**
* If set to `true`, router will serve OpenAPI schema JSON on `/` route
*/
serveSchema?: boolean
}

/**
Expand All @@ -65,10 +70,15 @@ export const AgentRouter = (options: AgentRouterOptions): Router => {

const allMethods: Array<string> = supportedMethods.concat(options.extraMethods ? options.extraMethods : [])

const publicApi = { ...openApiSchema }
publicApi.paths = {}

for (const exposedMethod of options.exposedMethods) {
if (!allMethods.includes(exposedMethod)) throw Error('Method not supported: ' + exposedMethod)
Debug('daf:express:initializing')(exposedMethod)

publicApi.paths['/' + exposedMethod] = openApiSchema.paths['/' + exposedMethod]

router.post('/' + exposedMethod, async (req: RequestWithAgent, res: Response, next: NextFunction) => {
if (!req.agent) throw Error('Agent not available')
try {
Expand All @@ -80,5 +90,11 @@ export const AgentRouter = (options: AgentRouterOptions): Router => {
})
}

if (options.serveSchema) {
router.get('/', (req, res) => {
res.json(publicApi)
})
}

return router
}
1 change: 1 addition & 0 deletions report/daf-express.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export interface AgentRouterOptions {
exposedMethods: Array<string>;
extraMethods?: Array<string>;
getAgentForRequest: (req: Request_2) => Promise<IAgent>;
serveSchema?: boolean;
}


Expand Down

0 comments on commit ccdd6a7

Please sign in to comment.