Implement GET method for idempotent methods
This commit is contained in:
parent
7edbe354a9
commit
81225b77a8
@ -1,7 +1,7 @@
|
|||||||
import express, {ErrorRequestHandler} from 'express'
|
import express, {ErrorRequestHandler} from 'express'
|
||||||
import {FunctionPropertyNames} from './types'
|
import {FunctionPropertyNames} from './types'
|
||||||
import {IDEMPOTENT_METHOD_REGEX} from './idempotent'
|
import {IDEMPOTENT_METHOD_REGEX} from './idempotent'
|
||||||
import {IErrorResponse} from './jsonrpc'
|
import {IErrorResponse} from './error'
|
||||||
import {ILogger} from '@rondo/common'
|
import {ILogger} from '@rondo/common'
|
||||||
import {ISuccessResponse} from './jsonrpc'
|
import {ISuccessResponse} from './jsonrpc'
|
||||||
import {NextFunction, Request, Response, Router} from 'express'
|
import {NextFunction, Request, Response, Router} from 'express'
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
export type TId = number | string
|
export type TId = number | string
|
||||||
import {ArgumentTypes, FunctionPropertyNames, RetType} from './types'
|
import {ArgumentTypes, FunctionPropertyNames, RetType} from './types'
|
||||||
import {isPromise} from './isPromise'
|
import {isPromise} from './isPromise'
|
||||||
import {createError, IErrorWithData} from './error'
|
import {createError, IErrorResponse, IErrorWithData} from './error'
|
||||||
|
|
||||||
export const ERROR_PARSE = {
|
export const ERROR_PARSE = {
|
||||||
code: -32700,
|
code: -32700,
|
||||||
@ -43,7 +43,7 @@ export function pick<T, K extends FunctionPropertyNames<T>>(t: T, keys: K[])
|
|||||||
}, {} as Pick<T, K>)
|
}, {} as Pick<T, K>)
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IRequest<M extends string | symbol | number = any, A = any[]> {
|
export interface IRequest<M extends string = any, A = any[]> {
|
||||||
jsonrpc: '2.0'
|
jsonrpc: '2.0'
|
||||||
id: TId | null
|
id: TId | null
|
||||||
method: M
|
method: M
|
||||||
@ -57,13 +57,6 @@ export interface ISuccessResponse<T> {
|
|||||||
error: null
|
error: null
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IErrorResponse<T> {
|
|
||||||
jsonrpc: '2.0'
|
|
||||||
id: TId | null
|
|
||||||
result: null
|
|
||||||
error: IErrorWithData<T>
|
|
||||||
}
|
|
||||||
|
|
||||||
export type IResponse<T = any> = ISuccessResponse<T> | IErrorResponse<T>
|
export type IResponse<T = any> = ISuccessResponse<T> | IErrorResponse<T>
|
||||||
|
|
||||||
export function createSuccessResponse<T>(id: number | string, result: T)
|
export function createSuccessResponse<T>(id: number | string, result: T)
|
||||||
@ -76,19 +69,6 @@ export function createSuccessResponse<T>(id: number | string, result: T)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createErrorResponse<T>(
|
|
||||||
id: number | string | null, error: IErrorWithData<T>)
|
|
||||||
: IErrorResponse<T> {
|
|
||||||
return {
|
|
||||||
jsonrpc: '2.0',
|
|
||||||
id,
|
|
||||||
result: null,
|
|
||||||
error,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export type TGetContext<Context> = (req: Request) => Context
|
|
||||||
|
|
||||||
export const createRpcService =
|
export const createRpcService =
|
||||||
<T, M extends FunctionPropertyNames<T>>(
|
<T, M extends FunctionPropertyNames<T>>(
|
||||||
service: T,
|
service: T,
|
||||||
|
|||||||
@ -15,6 +15,7 @@ describe('remote', () => {
|
|||||||
|
|
||||||
interface IService {
|
interface IService {
|
||||||
add(a: number, b: number): number
|
add(a: number, b: number): number
|
||||||
|
fetchItem(id: number): Promise<string>
|
||||||
}
|
}
|
||||||
const IServiceKeys = keys<IService>()
|
const IServiceKeys = keys<IService>()
|
||||||
|
|
||||||
@ -22,6 +23,9 @@ describe('remote', () => {
|
|||||||
add(a: number, b: number) {
|
add(a: number, b: number) {
|
||||||
return a + b
|
return a + b
|
||||||
}
|
}
|
||||||
|
async fetchItem(id: number): Promise<string> {
|
||||||
|
return Promise.resolve('id:' + id)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const service = new Service()
|
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 () => {
|
it('creates a proxy for remote service', async () => {
|
||||||
const s = createRemoteClient<IService>(
|
const rpc = createRemoteClient<IService>(
|
||||||
baseUrl, '/myService', IServiceKeys)
|
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<IService>(
|
||||||
|
baseUrl, '/myService', IServiceKeys)
|
||||||
|
const result = await rpc.add(3, 7)
|
||||||
expect(result).toBe(3 + 7)
|
expect(result).toBe(3 + 7)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@ -1,37 +1,51 @@
|
|||||||
import Axios from 'axios'
|
import Axios from 'axios'
|
||||||
import {FunctionPropertyNames, TAsyncified} from './types'
|
import {FunctionPropertyNames, TAsyncified} from './types'
|
||||||
|
import {IDEMPOTENT_METHOD_REGEX} from './idempotent'
|
||||||
|
|
||||||
|
export type TRequestIdGenerator<T extends string | number> = () => T
|
||||||
|
|
||||||
|
export const createNumberGenerator = (val: number) => () => ++val
|
||||||
|
export const constantId = (val: string) => () => val
|
||||||
|
|
||||||
export function createRemoteClient<T>(
|
export function createRemoteClient<T>(
|
||||||
baseUrl: string,
|
baseUrl: string,
|
||||||
url: string,
|
url: string,
|
||||||
methods: Array<FunctionPropertyNames<T>>,
|
methods: Array<FunctionPropertyNames<T>>,
|
||||||
|
getNextRequestId: TRequestIdGenerator<string | number> = constantId('c'),
|
||||||
|
idempotentMethodRegex = IDEMPOTENT_METHOD_REGEX,
|
||||||
) {
|
) {
|
||||||
const axios = Axios.create({
|
const axios = Axios.create({
|
||||||
baseURL: baseUrl,
|
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) => {
|
const service = methods.reduce((obj, method) => {
|
||||||
obj[method] = async function makeRequest(...args: any[]) {
|
obj[method] = async function makeRequest(...args: any[]) {
|
||||||
id++
|
return createRequest(getNextRequestId(), method, args)
|
||||||
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 obj
|
return obj
|
||||||
}, {} as any)
|
}, {} as any)
|
||||||
|
|||||||
@ -10,7 +10,11 @@ type PromisifyReturnType<T> = (...a: ArgumentTypes<T>) =>
|
|||||||
RetProm<UnwrapHOC<RetType<T>>>
|
RetProm<UnwrapHOC<RetType<T>>>
|
||||||
|
|
||||||
export type FunctionPropertyNames<T> = {
|
export type FunctionPropertyNames<T> = {
|
||||||
[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]
|
}[keyof T]
|
||||||
|
|
||||||
export type TAsyncified<T> = {
|
export type TAsyncified<T> = {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user