From 9adbc584b3007a75e813bbbe6ecb18fbebe3fdce Mon Sep 17 00:00:00 2001 From: Jerko Steiner Date: Thu, 1 Aug 2019 09:18:29 +0700 Subject: [PATCH] Add generic action creators for jsonrpc --- packages/jsonrpc-client/src/redux.test.ts | 123 ++++++++++++++++++++++ packages/jsonrpc-client/src/redux.ts | 22 ++++ packages/jsonrpc-common/src/index.ts | 9 ++ 3 files changed, 154 insertions(+) create mode 100644 packages/jsonrpc-client/src/redux.test.ts create mode 100644 packages/jsonrpc-client/src/redux.ts diff --git a/packages/jsonrpc-client/src/redux.test.ts b/packages/jsonrpc-client/src/redux.test.ts new file mode 100644 index 0000000..63dc033 --- /dev/null +++ b/packages/jsonrpc-client/src/redux.test.ts @@ -0,0 +1,123 @@ +/** + * @jest-environment node + */ +import bodyParser from 'body-parser' +import express from 'express' +import {AddressInfo} from 'net' +import {Server} from 'http' +import {createReduxClient} from './redux' +import {createRemoteClient} from './remote' +import {jsonrpc} from '@rondo/jsonrpc-server' +import {keys} from 'ts-transformer-keys' + +describe('createReduxClient', () => { + + interface IService { + add(a: number, b: number): number + addAsync(a: number, b: number): Promise + addWithContext(a: number, b: number): (ctx: IContext) => number + addAsyncWithContext(a: number, b: number): (ctx: IContext) => + Promise + } + + interface IContext { + userId: number + } + + class Service implements IService { + add(a: number, b: number) { + return a + b + } + addAsync(a: number, b: number) { + return new Promise(resolve => resolve(a + b)) + } + addWithContext = (a: number, b: number) => (ctx: IContext) => + a + b + ctx.userId + addAsyncWithContext = (a: number, b: number) => (ctx: IContext) => + new Promise(resolve => resolve(a + b + ctx.userId)) + } + + const app = express() + app.use(bodyParser.json()) + app.use('/service', jsonrpc(new Service(), keys(), () => ({ + userId: 1000, + }))) + + let baseUrl: string + let server: Server + beforeEach(async () => { + await new Promise(resolve => { + server = app.listen(0, '127.0.0.1', resolve) + }) + const addr = server.address() as AddressInfo + baseUrl = `http://${addr.address}:${addr.port}` + }) + + afterEach(() => { + return new Promise(resolve => { + server.close(resolve) + }) + }) + + function getClient() { + const remoteClient = createRemoteClient( + baseUrl, '/service', keys()) + return createReduxClient(remoteClient, 'myService') + } + + describe('action creators', () => { + describe('add', () => { + it('creates a redux action with type, method and status', async () => { + const client = getClient() + const action = client.add(3, 7) + expect(action.method).toEqual('add') + expect(action.type).toEqual('myService') + expect(action.status).toEqual('pending') + const result = await action.payload + expect(result).toEqual(3 + 7) + // compilation test + expect(result + 2).toEqual(12) + }) + }) + describe('addAsync', () => { + it('creates a redux action with type, method and status', async () => { + const client = getClient() + const action = client.addAsync(3, 7) + expect(action.method).toEqual('addAsync') + expect(action.type).toEqual('myService') + expect(action.status).toEqual('pending') + const result = await action.payload + expect(result).toEqual(3 + 7) + // compilation test + expect(result + 2).toEqual(12) + }) + }) + describe('addWithContext', () => { + it('creates a redux action with type, method and status', async () => { + const client = getClient() + const action = client.addWithContext(3, 7) + expect(action.method).toEqual('addWithContext') + expect(action.type).toEqual('myService') + expect(action.status).toEqual('pending') + const result = await action.payload + expect(result).toEqual(3 + 7 + 1000) + // compilation test + expect(result + 2).toEqual(1012) + }) + }) + describe('addAsyncWithContext', () => { + it('creates a redux action with type, method and status', async () => { + const client = getClient() + const action = client.addAsyncWithContext(3, 7) + expect(action.method).toEqual('addAsyncWithContext') + expect(action.type).toEqual('myService') + expect(action.status).toEqual('pending') + const result = await action.payload + expect(result).toEqual(3 + 7 + 1000) + // compilation test + expect(result + 2).toEqual(1012) + }) + }) + }) + +}) diff --git a/packages/jsonrpc-client/src/redux.ts b/packages/jsonrpc-client/src/redux.ts new file mode 100644 index 0000000..cc31921 --- /dev/null +++ b/packages/jsonrpc-client/src/redux.ts @@ -0,0 +1,22 @@ +import {Asyncified, Reduxed} from '@rondo/jsonrpc-common' +import {createRemoteClient} from './remote' + +export function createReduxClient( + client: Asyncified, + type: ActionType, +) { + const service = Object.keys(client).reduce((obj, method: any) => { + obj[method] = function makeAction(...args: any[]) { + const payload = ((client as any)[method])(...args) + return { + payload, + type, + method, + status: 'pending', + } + } + return obj + }, {} as any) + + return service as Reduxed +} diff --git a/packages/jsonrpc-common/src/index.ts b/packages/jsonrpc-common/src/index.ts index 794cbe5..b4bbc59 100644 --- a/packages/jsonrpc-common/src/index.ts +++ b/packages/jsonrpc-common/src/index.ts @@ -8,3 +8,12 @@ export type PromisifyReturnType = (...a: ArgumentTypes) => export type Asyncified = { [K in keyof T]: PromisifyReturnType } + +export type Reduxed = { + [K in keyof T]: (...a: ArgumentTypes) => { + type: ActionType + payload: RetProm>> + method: K + status: 'pending' + } +}