Add ability to wrap method calls in jsonrpc
This commit is contained in:
parent
4ea534293b
commit
9cc5a7affb
@ -1,10 +1,11 @@
|
|||||||
import bodyParser from 'body-parser'
|
import bodyParser from 'body-parser'
|
||||||
import express from 'express'
|
import express from 'express'
|
||||||
import request from 'supertest'
|
import request from 'supertest'
|
||||||
|
import {IRequest} from './jsonrpc'
|
||||||
import {createClient} from './supertest'
|
import {createClient} from './supertest'
|
||||||
|
import {ensure} from './ensure'
|
||||||
import {jsonrpc} from './express'
|
import {jsonrpc} from './express'
|
||||||
import {noopLogger} from './test-utils'
|
import {noopLogger} from './test-utils'
|
||||||
import {ensure} from './ensure'
|
|
||||||
|
|
||||||
describe('jsonrpc', () => {
|
describe('jsonrpc', () => {
|
||||||
|
|
||||||
@ -240,6 +241,54 @@ describe('jsonrpc', () => {
|
|||||||
.get(`/myService?jsonrpc=2.0&id=1&method=add¶ms=${params}`)
|
.get(`/myService?jsonrpc=2.0&id=1&method=add¶ms=${params}`)
|
||||||
.expect(405)
|
.expect(405)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('wrapCall', () => {
|
||||||
|
|
||||||
|
let requests: IRequest[] = []
|
||||||
|
let results: any[] = []
|
||||||
|
function create() {
|
||||||
|
requests = []
|
||||||
|
results = []
|
||||||
|
|
||||||
|
userId = 1000
|
||||||
|
const app = express()
|
||||||
|
const myService = new Service(5)
|
||||||
|
// console.log('service', myService, Object.
|
||||||
|
app.use(bodyParser.json())
|
||||||
|
app.use('/',
|
||||||
|
jsonrpc(
|
||||||
|
req => ({userId}),
|
||||||
|
noopLogger,
|
||||||
|
async (path, req, makeRequest) => {
|
||||||
|
requests.push(req)
|
||||||
|
const result = await makeRequest()
|
||||||
|
results.push(result)
|
||||||
|
return result
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.addService('/myService', myService)
|
||||||
|
.router(),
|
||||||
|
)
|
||||||
|
return app
|
||||||
|
}
|
||||||
|
|
||||||
|
it('should wrap POST rpc method call', async () => {
|
||||||
|
const req = {
|
||||||
|
jsonrpc: '2.0',
|
||||||
|
id: 1,
|
||||||
|
method: 'add',
|
||||||
|
params: [1, 2],
|
||||||
|
}
|
||||||
|
const response = await request(create())
|
||||||
|
.post('/myService')
|
||||||
|
.send(req)
|
||||||
|
|
||||||
|
expect(response.body.result).toEqual(3)
|
||||||
|
expect(requests).toEqual([ req ])
|
||||||
|
expect(results).toEqual([ response.body ])
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import {NextFunction, Request, Response, Router} from 'express'
|
|||||||
import {createError, isJSONRPCError, IJSONRPCError, IError} from './error'
|
import {createError, isJSONRPCError, IJSONRPCError, IError} from './error'
|
||||||
import {
|
import {
|
||||||
createRpcService, ERROR_SERVER, ERROR_INVALID_PARAMS, ERROR_METHOD_NOT_FOUND,
|
createRpcService, ERROR_SERVER, ERROR_INVALID_PARAMS, ERROR_METHOD_NOT_FOUND,
|
||||||
|
IRequest,
|
||||||
} from './jsonrpc'
|
} from './jsonrpc'
|
||||||
|
|
||||||
export type TGetContext<Context> = (req: Request) => Context
|
export type TGetContext<Context> = (req: Request) => Context
|
||||||
@ -21,9 +22,19 @@ export interface IJSONRPCReturnType {
|
|||||||
router(): Router
|
router(): Router
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function wrap<A, R>(
|
||||||
|
path: string, request: A, fn: () => Promise<R>): Promise<R> {
|
||||||
|
const result = await fn()
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
export function jsonrpc<Context>(
|
export function jsonrpc<Context>(
|
||||||
getContext: TGetContext<Context>,
|
getContext: TGetContext<Context>,
|
||||||
logger: ILogger,
|
logger: ILogger,
|
||||||
|
wrapCall: <A extends IRequest, R>(
|
||||||
|
path: string,
|
||||||
|
request: A,
|
||||||
|
fn: (request?: A) => Promise<R>) => Promise<R> = wrap,
|
||||||
idempotentMethodRegex = IDEMPOTENT_METHOD_REGEX,
|
idempotentMethodRegex = IDEMPOTENT_METHOD_REGEX,
|
||||||
): IJSONRPCReturnType {
|
): IJSONRPCReturnType {
|
||||||
|
|
||||||
@ -94,13 +105,15 @@ export function jsonrpc<Context>(
|
|||||||
method: req.query.method,
|
method: req.query.method,
|
||||||
params: JSON.parse(req.query.params),
|
params: JSON.parse(req.query.params),
|
||||||
}
|
}
|
||||||
rpcService.invoke(request, getContext(req))
|
wrapCall(path, request,
|
||||||
|
(body = request) => rpcService.invoke(body, getContext(req)))
|
||||||
.then(response => handleResponse(response, res))
|
.then(response => handleResponse(response, res))
|
||||||
.catch(next)
|
.catch(next)
|
||||||
})
|
})
|
||||||
|
|
||||||
router.post(path, (req, res, next) => {
|
router.post(path, (req, res, next) => {
|
||||||
rpcService.invoke(req.body, getContext(req))
|
wrapCall(path, req.body,
|
||||||
|
(body = req.body) => rpcService.invoke(body, getContext(req)))
|
||||||
.then(response => handleResponse(response, res))
|
.then(response => handleResponse(response, res))
|
||||||
.catch(next)
|
.catch(next)
|
||||||
})
|
})
|
||||||
|
|||||||
@ -45,6 +45,21 @@ export function pick<T, K extends FunctionPropertyNames<T>>(t: T, keys: K[])
|
|||||||
}, {} as Pick<T, K>)
|
}, {} as Pick<T, K>)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getAllMethods<T>(obj: T): Array<FunctionPropertyNames<T>> {
|
||||||
|
const props = new Set<string>()
|
||||||
|
do {
|
||||||
|
const l = Object.getOwnPropertyNames(obj)
|
||||||
|
.filter((p, i, arr) => {
|
||||||
|
return typeof (obj as any)[p] === 'function' &&
|
||||||
|
p !== 'constructor'
|
||||||
|
})
|
||||||
|
.forEach(p => props.add(p))
|
||||||
|
obj = Object.getPrototypeOf(obj)
|
||||||
|
} while (Object.getPrototypeOf(obj))
|
||||||
|
|
||||||
|
return Array.from(props) as unknown as Array<FunctionPropertyNames<T>>
|
||||||
|
}
|
||||||
|
|
||||||
export interface IRequest<M extends string = any, A = any[]> {
|
export interface IRequest<M extends string = any, A = any[]> {
|
||||||
jsonrpc: '2.0'
|
jsonrpc: '2.0'
|
||||||
id: TId | null
|
id: TId | null
|
||||||
@ -104,7 +119,9 @@ export const createRpcService = <T, M extends FunctionPropertyNames<T>>(
|
|||||||
service: T,
|
service: T,
|
||||||
methods?: M[],
|
methods?: M[],
|
||||||
) => {
|
) => {
|
||||||
const rpcService = methods ? pick(service, methods) : service
|
|
||||||
|
const rpcService = methods ? pick(service, methods) :
|
||||||
|
pick(service, getAllMethods(service))
|
||||||
return {
|
return {
|
||||||
async invoke<Context>(
|
async invoke<Context>(
|
||||||
req: IRequest<M, ArgumentTypes<T[M]>>,
|
req: IRequest<M, ArgumentTypes<T[M]>>,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user