Do not use proxy in createLocalClient, add bulk methods
This commit is contained in:
parent
d2a5a35543
commit
fbd7a2229b
@ -6,3 +6,4 @@ export * from './local'
|
|||||||
export * from './redux'
|
export * from './redux'
|
||||||
export * from './remote'
|
export * from './remote'
|
||||||
export * from './types'
|
export * from './types'
|
||||||
|
export * from './util'
|
||||||
|
|||||||
@ -1,23 +1,30 @@
|
|||||||
import {TAsyncified, Contextual, ReverseContextual} from './types'
|
import {TAsyncified, Contextual, ReverseContextual} from './types'
|
||||||
import {Request} from 'express'
|
import {Request} from 'express'
|
||||||
import {TGetContext} from './express'
|
import {TGetContext} from './express'
|
||||||
|
import {getAllMethods} from './jsonrpc'
|
||||||
|
|
||||||
|
export type LocalClient<T> = TAsyncified<ReverseContextual<T>>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a local client for a specific service instance. The actual service
|
* Creates a local client for a specific service instance. The actual service
|
||||||
* will be invoked as if it would be remotely. This helps keep the API similar
|
* will be invoked as if it would be remotely. This helps keep the API similar
|
||||||
* on the client- and server-side.
|
* on the client- and server-side.
|
||||||
|
*
|
||||||
|
* The service argument is expected to be a class implementing the
|
||||||
|
* Contextual<Service, Context> type. The first (context) argument will be
|
||||||
|
* automatically removed from all methods in the service, and the supplied
|
||||||
|
* context argument will be used instead.
|
||||||
*/
|
*/
|
||||||
export function createLocalClient<T extends {}, Context>(
|
export function createLocalClient<T extends {}, Context>(
|
||||||
service: T,
|
service: T,
|
||||||
context: Context,
|
context: Context,
|
||||||
): TAsyncified<ReverseContextual<T>> {
|
): LocalClient<T> {
|
||||||
const proxy = new Proxy({}, {
|
return getAllMethods(service)
|
||||||
get(obj, prop) {
|
.filter(prop => typeof service[prop] === 'function')
|
||||||
return async function makeRequest(...args: any[]) {
|
.reduce((obj, prop) => {
|
||||||
const result = (service as any)[prop](context, ...args)
|
obj[prop] = function makeRequest(...args: any[]) {
|
||||||
return result
|
return (service as any)[prop](context, ...args)
|
||||||
}
|
}
|
||||||
},
|
return obj
|
||||||
})
|
}, {} as any)
|
||||||
return proxy as any
|
|
||||||
}
|
}
|
||||||
|
|||||||
107
packages/jsonrpc/src/util.test.ts
Normal file
107
packages/jsonrpc/src/util.test.ts
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
import * as util from './util'
|
||||||
|
import express from 'express'
|
||||||
|
import {Contextual} from './types'
|
||||||
|
import {jsonrpc} from './express'
|
||||||
|
import {noopLogger} from './test-utils'
|
||||||
|
import {createClient} from './supertest'
|
||||||
|
import {json} from 'body-parser'
|
||||||
|
|
||||||
|
describe('util', () => {
|
||||||
|
|
||||||
|
interface IS1 {
|
||||||
|
add(a: number, b: number): number
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IS2 {
|
||||||
|
mul(a: number, b: number): number
|
||||||
|
concat(...str: string[]): string
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IContext {
|
||||||
|
userId: number
|
||||||
|
}
|
||||||
|
|
||||||
|
class Service1 implements Contextual<IS1, IContext> {
|
||||||
|
add(cx: IContext, a: number, b: number) {
|
||||||
|
return a + b + cx.userId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Service2 implements Contextual<IS2, IContext> {
|
||||||
|
mul(cx: IContext, a: number, b: number) {
|
||||||
|
return a * b + cx.userId
|
||||||
|
}
|
||||||
|
concat(cx: IContext, ...str: string[]) {
|
||||||
|
return str.join('') + cx.userId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const services = {
|
||||||
|
s1: new Service1(),
|
||||||
|
s2: new Service2(),
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('bulkCreateLocalClient', () => {
|
||||||
|
it('creates a typed local client', async () => {
|
||||||
|
const client = util.bulkCreateLocalClient(services, {userId: 10})
|
||||||
|
|
||||||
|
const r1: number = await client.s1.add(1, 2)
|
||||||
|
expect(r1).toBe(13)
|
||||||
|
|
||||||
|
const r2: number = await client.s2.mul(2, 3)
|
||||||
|
expect(r2).toBe(16)
|
||||||
|
|
||||||
|
const r3: string = await client.s2.concat('a', 'b')
|
||||||
|
expect(r3).toBe('ab10')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('bulkCreateActions', () => {
|
||||||
|
it('creates typed actions', async () => {
|
||||||
|
const client = util.bulkCreateLocalClient(services, {userId: 10})
|
||||||
|
const actions = util.bulkCreateActions(client)
|
||||||
|
|
||||||
|
const r1 = actions.s1.add(1, 2)
|
||||||
|
const method1: 'add' = r1.method
|
||||||
|
const status1: 'pending' = r1.status
|
||||||
|
const type1: 's1' = r1.type
|
||||||
|
const payload1: Promise<number> = r1.payload
|
||||||
|
expect(type1).toBe('s1')
|
||||||
|
expect(status1).toBe('pending')
|
||||||
|
expect(method1).toBe('add')
|
||||||
|
expect(await payload1).toBe(13)
|
||||||
|
|
||||||
|
const r2 = actions.s2.mul(2, 3)
|
||||||
|
const method2: 'mul' = r2.method
|
||||||
|
const status2: 'pending' = r2.status
|
||||||
|
const type2: 's2' = r2.type
|
||||||
|
const payload2: Promise<number> = r2.payload
|
||||||
|
expect(type2).toBe('s2')
|
||||||
|
expect(status2).toBe('pending')
|
||||||
|
expect(method2).toBe('mul')
|
||||||
|
expect(await payload2).toBe(16)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('bulkJSONRPC', () => {
|
||||||
|
const getContext = () => ({userId: 10})
|
||||||
|
function createApp(router: express.Router) {
|
||||||
|
const app = express()
|
||||||
|
app.use(json())
|
||||||
|
app.use('/rpc', router)
|
||||||
|
return app
|
||||||
|
}
|
||||||
|
|
||||||
|
it('creates JSON RPC services', async () => {
|
||||||
|
const router = util.bulkjsonrpc(
|
||||||
|
jsonrpc(getContext, noopLogger),
|
||||||
|
services,
|
||||||
|
)
|
||||||
|
const app = createApp(router)
|
||||||
|
const client = createClient<IS1>(app, '/rpc/s1')
|
||||||
|
const result: number = await client.add(1, 3)
|
||||||
|
expect(result).toBe(14)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
48
packages/jsonrpc/src/util.ts
Normal file
48
packages/jsonrpc/src/util.ts
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
import {IJSONRPCReturnType} from './express'
|
||||||
|
import {TAsyncified, TReduxed} from './types'
|
||||||
|
import {createActions} from './redux'
|
||||||
|
import {createLocalClient, LocalClient} from './local'
|
||||||
|
|
||||||
|
function keys<T>(obj: T): Array<keyof T & string> {
|
||||||
|
return Object.keys(obj) as Array<keyof T & string>
|
||||||
|
}
|
||||||
|
|
||||||
|
type BulkLocalClient<T> = {[K in keyof T & string]: LocalClient<T[K]>}
|
||||||
|
type BulkActions<T> = {[K in keyof T & string]: TReduxed<T[K], K>}
|
||||||
|
type BulkRemoteClient<T> = {[K in keyof T & string]: TAsyncified<T[K]>}
|
||||||
|
|
||||||
|
function bulkCreate<T, R>(
|
||||||
|
src: T,
|
||||||
|
mapValue: <K extends keyof T & string>(key: K, value: T[K]) => any,
|
||||||
|
) {
|
||||||
|
return keys(src).reduce((obj, key) => {
|
||||||
|
const value = mapValue(key, src[key])
|
||||||
|
obj[key] = value
|
||||||
|
return obj
|
||||||
|
}, {} as any)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function bulkCreateLocalClient<T, Cx>(
|
||||||
|
src: T,
|
||||||
|
context: Cx,
|
||||||
|
): BulkLocalClient<T> {
|
||||||
|
return bulkCreate(src, (key, value) => createLocalClient(value, context))
|
||||||
|
}
|
||||||
|
|
||||||
|
export function bulkCreateActions<T extends Record<string, TAsyncified<any>>>(
|
||||||
|
src: T,
|
||||||
|
): BulkActions<T> {
|
||||||
|
return bulkCreate(src, (key, value) => createActions(value, key))
|
||||||
|
}
|
||||||
|
|
||||||
|
export function bulkjsonrpc<T>(
|
||||||
|
jsonrpc: IJSONRPCReturnType,
|
||||||
|
services: T,
|
||||||
|
) {
|
||||||
|
keys(services).forEach(key => {
|
||||||
|
const service = services[key]
|
||||||
|
jsonrpc.addService('/' + key, service)
|
||||||
|
})
|
||||||
|
|
||||||
|
return jsonrpc.router()
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user