Skip to content

Commit

Permalink
feat: add Req, Res, and a few more decorators
Browse files Browse the repository at this point in the history
  • Loading branch information
pismenskiy authored Mar 1, 2022
1 parent 7d62b7e commit b7da378
Show file tree
Hide file tree
Showing 2 changed files with 105 additions and 7 deletions.
54 changes: 48 additions & 6 deletions packages/expressjs/src/main/ts/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import {
JsonRpcError,
OK,
} from '@qiwi/json-rpc-common'
import {IMetaTypedValue} from '@qiwi/substrate'
import {IMetaTypedValue, INext, IRequest, IResponse} from '@qiwi/substrate'
import {constructDecorator, METHOD} from '@qiwi/decorator-utils'

export * from '@qiwi/json-rpc-common'
Expand All @@ -28,6 +28,18 @@ export enum ParamMetadataKeys {
PARAMS = 'params',
CLIENT = 'client',
SECURITY = 'security',
REQ = 'req',
RES = 'res',
NEXT = 'next',
BODY = 'body',
QUERY = 'query',
PARAM = 'param',
HEADERS = 'headers',
SESSION = 'session',
FILE = 'file',
FILES = 'files',
HOST = 'host',
IP = 'ip',
}

const asyncMiddleware = (fn: Function) => function(this: any, req: Request, res: Response, next: NextFunction) {
Expand All @@ -40,7 +52,7 @@ const AsyncMiddleware = constructDecorator(({targetType, target}) => {
}
})

type IJsonRpcMetaTypedValue = IMetaTypedValue<IParsedObject, 'jsonRpc', {}>
type IJsonRpcMetaTypedValue = IMetaTypedValue<IParsedObject, 'jsonRpc', {}> & {reqresnext: {req: IRequest, res: IResponse, next: INext}}

export function JsonRpcMiddleware(): ClassDecorator {
return <TFunction extends Function>(target: TFunction) => {
Expand All @@ -49,8 +61,8 @@ export function JsonRpcMiddleware(): ClassDecorator {
class Extended extends base {

@AsyncMiddleware
protected async middleware(req: Request, res: Response): Promise<any> {
const boxedJsonRpc = (this.constructor as any).parseRequest(req)
protected async middleware(req: Request, res: Response, next: NextFunction): Promise<any> {
const boxedJsonRpc = (this.constructor as any).parseRequest(req, res, next)
if (!boxedJsonRpc) {
// TODO
return
Expand All @@ -76,7 +88,7 @@ export function JsonRpcMiddleware(): ClassDecorator {

}

static parseRequest(req: Request): IJsonRpcMetaTypedValue | undefined {
static parseRequest(req: Request, res: Response, next: NextFunction): IJsonRpcMetaTypedValue | undefined {
const jsonRpc = parseJsonRpcObject(req.body)

if (Array.isArray(jsonRpc)) {
Expand All @@ -85,6 +97,7 @@ export function JsonRpcMiddleware(): ClassDecorator {
}

return {
reqresnext: {req, res, next},
meta: {},
value: jsonRpc,
type: 'jsonRpc',
Expand Down Expand Up @@ -136,7 +149,11 @@ export function JsonRpcMiddleware(): ClassDecorator {
}

static resolveParam(boxedJsonRpc: IJsonRpcMetaTypedValue, Param: any, {type, value}: TRpcMethodParam) {
let data
let data = exchangeCommonKeyMetadataForValue(boxedJsonRpc, {type, value})

if (data) {
return data
}

if (boxedJsonRpc.value.type !== 'request') {
return
Expand Down Expand Up @@ -164,6 +181,22 @@ export function JsonRpcMiddleware(): ClassDecorator {
}
}

export const exchangeCommonKeyMetadataForValue = (boxedJsonRpc: IJsonRpcMetaTypedValue, {type, value}: {type: any, value: any}) => {
const req = boxedJsonRpc.reqresnext.req
const metadataMap = {
[ParamMetadataKeys.RES]: boxedJsonRpc.reqresnext.res,
[ParamMetadataKeys.NEXT]: boxedJsonRpc.reqresnext.next,
[ParamMetadataKeys.REQ]: req,
[ParamMetadataKeys.BODY]: value ? req.body[value] : req.body,
[ParamMetadataKeys.PARAM]: value ? req.params[value] : req.params,
[ParamMetadataKeys.QUERY]: value ? req.query[value] : req.value,
[ParamMetadataKeys.HEADERS]: value ? req.headers[value.toLowerCase()] : req.headers,
[ParamMetadataKeys.IP]: req.ip,
}
// @ts-ignore
return metadataMap[type]
}

export const JsonRpcData = (type: ParamMetadataKeys = ParamMetadataKeys.REQUEST, value?: any) =>
(target: Object, propertyKey: string, index: number) => {
const meta: TRpcMeta = Reflect.getOwnMetadata(JSON_RPC_METADATA, target.constructor) || {}
Expand All @@ -189,6 +222,15 @@ export const JsonRpcId = RpcId

export const JsonRpcParams = (value?: string) => JsonRpcData(ParamMetadataKeys.PARAMS, value)

export const Res = () => JsonRpcData(ParamMetadataKeys.RES)
export const Req = () => JsonRpcData(ParamMetadataKeys.REQ)
export const Next = () => JsonRpcData(ParamMetadataKeys.NEXT)
export const Body = (value?: string) => JsonRpcData(ParamMetadataKeys.BODY, value)
export const Param = (value?: string) => JsonRpcData(ParamMetadataKeys.PARAM, value)
export const Query = (value?: string) => JsonRpcData(ParamMetadataKeys.QUERY, value)
export const Headers = (value?: string) => JsonRpcData(ParamMetadataKeys.HEADERS, value)
export const Ip = () => JsonRpcData(ParamMetadataKeys.IP)

export const JsonRpcMethod = (method: string) => {
return (target: any, propertyKey: string) => {
const meta: TRpcMeta = Reflect.getOwnMetadata(JSON_RPC_METADATA, target.constructor) || {}
Expand Down
58 changes: 57 additions & 1 deletion packages/nestjs/src/test/ts/decorators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@ import request from 'supertest'
import {
JsonRpcController,
JsonRpcMethod,
RpcId,
RpcId, Req, Res, Headers,
JsonRpcParams,
} from '../../main/ts/'
import {IRequest} from '@qiwi/substrate'

describe('decorators', () => {
describe('JsonRpcController', () => {
Expand Down Expand Up @@ -39,6 +40,15 @@ describe('decorators', () => {
return {foo: 'quxr', id, a, b}
}

@JsonRpcMethod('test3')
baz(@Req() req: IRequest, @Res() res: IRequest, @Headers() headers: any) {
return {
req: Object.keys(req),
res: Object.keys(res),
headers: Object.keys(headers),
}
}

}

let module: TestingModule
Expand Down Expand Up @@ -98,5 +108,51 @@ describe('decorators', () => {
result: {foo: 'bar', id: '123'},
})
})

it('works correctly with @req', () => {
return request(app.getHttpServer())
.post('/rpc')
.send({
jsonrpc: '2.0',
method: 'test3',
id: '123',
params: {},
})
.expect(HttpStatus.OK)
.expect(({body}) => {
const {result: {req}} = body
expect(req).toEqual(expect.arrayContaining(['url', 'route', 'baseUrl', 'body', 'length', 'method']))
})
})
it('works correctly with @res', () => {
return request(app.getHttpServer())
.post('/rpc')
.send({
jsonrpc: '2.0',
method: 'test3',
id: '123',
params: {},
})
.expect(HttpStatus.OK)
.expect(({body}) => {
const {result: {res}} = body
expect(res).toEqual(expect.arrayContaining(['locals', 'statusCode', 'req']))
})
})
it('works correctly with @headers', () => {
return request(app.getHttpServer())
.post('/rpc')
.send({
jsonrpc: '2.0',
method: 'test3',
id: '123',
params: {},
})
.expect(HttpStatus.OK)
.expect(({body}) => {
const {result: {headers}} = body
expect(headers).toEqual(expect.arrayContaining(['host', 'accept-encoding', 'content-type', 'content-length', 'connection']))
})
})
})
})

0 comments on commit b7da378

Please sign in to comment.