diff --git a/packages/jsonrpc/src/express.ts b/packages/jsonrpc/src/express.ts index 48d24ee..a89b2e3 100644 --- a/packages/jsonrpc/src/express.ts +++ b/packages/jsonrpc/src/express.ts @@ -1,7 +1,7 @@ import express, {ErrorRequestHandler} from 'express' import {FunctionPropertyNames} from './types' import {IDEMPOTENT_METHOD_REGEX} from './idempotent' -import {IErrorResponse} from './jsonrpc' +import {IErrorResponse} from './error' import {ILogger} from '@rondo/common' import {ISuccessResponse} from './jsonrpc' import {NextFunction, Request, Response, Router} from 'express' diff --git a/packages/jsonrpc/src/jsonrpc.ts b/packages/jsonrpc/src/jsonrpc.ts index 0db5a4a..f2ee8c8 100644 --- a/packages/jsonrpc/src/jsonrpc.ts +++ b/packages/jsonrpc/src/jsonrpc.ts @@ -1,7 +1,7 @@ export type TId = number | string import {ArgumentTypes, FunctionPropertyNames, RetType} from './types' import {isPromise} from './isPromise' -import {createError, IErrorWithData} from './error' +import {createError, IErrorResponse, IErrorWithData} from './error' export const ERROR_PARSE = { code: -32700, @@ -43,7 +43,7 @@ export function pick>(t: T, keys: K[]) }, {} as Pick) } -export interface IRequest { +export interface IRequest { jsonrpc: '2.0' id: TId | null method: M @@ -57,13 +57,6 @@ export interface ISuccessResponse { error: null } -export interface IErrorResponse { - jsonrpc: '2.0' - id: TId | null - result: null - error: IErrorWithData -} - export type IResponse = ISuccessResponse | IErrorResponse export function createSuccessResponse(id: number | string, result: T) @@ -76,19 +69,6 @@ export function createSuccessResponse(id: number | string, result: T) } } -export function createErrorResponse( - id: number | string | null, error: IErrorWithData) - : IErrorResponse { - return { - jsonrpc: '2.0', - id, - result: null, - error, - } -} - -export type TGetContext = (req: Request) => Context - export const createRpcService = >( service: T, diff --git a/packages/jsonrpc/src/remote.test.ts b/packages/jsonrpc/src/remote.test.ts index 6d5958d..dfd3d3f 100644 --- a/packages/jsonrpc/src/remote.test.ts +++ b/packages/jsonrpc/src/remote.test.ts @@ -15,6 +15,7 @@ describe('remote', () => { interface IService { add(a: number, b: number): number + fetchItem(id: number): Promise } const IServiceKeys = keys() @@ -22,6 +23,9 @@ describe('remote', () => { add(a: number, b: number) { return a + b } + async fetchItem(id: number): Promise { + return Promise.resolve('id:' + id) + } } const service = new Service() @@ -52,11 +56,20 @@ describe('remote', () => { }) }) - describe('method invocation', () => { + describe('idempotent method invocation (GET)', () => { it('creates a proxy for remote service', async () => { - const s = createRemoteClient( + const rpc = createRemoteClient( baseUrl, '/myService', IServiceKeys) - const result = await s.add(3, 7) + const result = await rpc.fetchItem(5) + expect(result).toBe('id:5') + }) + }) + + describe('method invocation (POST)', () => { + it('creates a proxy for remote service', async () => { + const rpc = createRemoteClient( + baseUrl, '/myService', IServiceKeys) + const result = await rpc.add(3, 7) expect(result).toBe(3 + 7) }) }) diff --git a/packages/jsonrpc/src/remote.ts b/packages/jsonrpc/src/remote.ts index a08fb36..5dec6a3 100644 --- a/packages/jsonrpc/src/remote.ts +++ b/packages/jsonrpc/src/remote.ts @@ -1,37 +1,51 @@ import Axios from 'axios' import {FunctionPropertyNames, TAsyncified} from './types' +import {IDEMPOTENT_METHOD_REGEX} from './idempotent' + +export type TRequestIdGenerator = () => T + +export const createNumberGenerator = (val: number) => () => ++val +export const constantId = (val: string) => () => val export function createRemoteClient( baseUrl: string, url: string, methods: Array>, + getNextRequestId: TRequestIdGenerator = constantId('c'), + idempotentMethodRegex = IDEMPOTENT_METHOD_REGEX, ) { const axios = Axios.create({ baseURL: baseUrl, }) - let id = 0 + async function createRequest( + id: string | number | null, + method: string, + params: any[], + ) { + const reqMethod = IDEMPOTENT_METHOD_REGEX.test(method) ? 'get' : 'post' + const payloadKey = reqMethod === 'post' ? 'data' : 'params' + + const response = await axios({ + method: reqMethod, + url, + [payloadKey]: { + id, + jsonrpc: '2.0', + method, + params, + }, + }) + if (response.data.error) { + // TODO create an actual error + throw response.data.error + } + return response.data.result + } + const service = methods.reduce((obj, method) => { obj[method] = async function makeRequest(...args: any[]) { - id++ - const response = await axios({ - method: 'post', - url, - headers: { - 'content-type': 'application/json', - }, - data: { - id, - jsonrpc: '2.0', - method, - params: args, - }, - }) - if (response.data.error) { - // TODO create an actual error - throw response.data.error - } - return response.data.result + return createRequest(getNextRequestId(), method, args) } return obj }, {} as any) diff --git a/packages/jsonrpc/src/types.ts b/packages/jsonrpc/src/types.ts index 539949e..68c1a1b 100644 --- a/packages/jsonrpc/src/types.ts +++ b/packages/jsonrpc/src/types.ts @@ -10,7 +10,11 @@ type PromisifyReturnType = (...a: ArgumentTypes) => RetProm>> export type FunctionPropertyNames = { - [K in keyof T]: T[K] extends (...args: any[]) => any ? K : never + [K in keyof T]: K extends string + ? T[K] extends (...args: any[]) => any + ? K + : never + : never }[keyof T] export type TAsyncified = {