From 8968da901ae53d07d179ade9e7b30970616af814 Mon Sep 17 00:00:00 2001 From: AVVS Date: Sun, 16 Feb 2025 15:53:33 -0800 Subject: [PATCH 1/3] feat: improved types for actions --- packages/plugin-router-hapi/src/attach.ts | 7 ++- .../plugin-router/src/extensions/audit/log.ts | 15 ++++--- .../validate/query-string-parser.ts | 14 +++--- packages/plugin-router/src/index.ts | 3 +- packages/plugin-router/src/routes.ts | 14 ++++-- packages/plugin-router/src/types/router.ts | 44 +++++++++++++------ 6 files changed, 65 insertions(+), 32 deletions(-) diff --git a/packages/plugin-router-hapi/src/attach.ts b/packages/plugin-router-hapi/src/attach.ts index 5924868ab..fe5b6b437 100644 --- a/packages/plugin-router-hapi/src/attach.ts +++ b/packages/plugin-router-hapi/src/attach.ts @@ -33,13 +33,18 @@ function attachRequestCountEvents(server: Server, router: Microfleet['router']) server.events.on('stop', onStop) } +declare module '@microfleet/plugin-router' { + +} + export default function attachRouter(service: Microfleet, config: RouterHapiPluginConfig): ServerRegisterPluginObject { return { plugin: { name: 'microfleetRouter', version: '1.0.0', async register(server: Server) { - for (const [actionName, handler] of service.router.routes.get(ActionTransport.http).entries()) { + const routes = service.router.routes.get<{ TransportOptions: { hapi: ServerRoute } }>(ActionTransport.http) + for (const [actionName, handler] of routes.entries()) { const path = fromNameToPath(actionName, config.prefix) const serverRoute: ServerRoute = { path, diff --git a/packages/plugin-router/src/extensions/audit/log.ts b/packages/plugin-router/src/extensions/audit/log.ts index 875c70855..66a3f451a 100644 --- a/packages/plugin-router/src/extensions/audit/log.ts +++ b/packages/plugin-router/src/extensions/audit/log.ts @@ -1,5 +1,5 @@ import { Microfleet } from '@microfleet/core-types' -import type { ServiceRequest } from '../../types/router' +import type { ReplyGenericInterface, RequestGenericInterface, ServiceRequest } from '../../types/router' import { Lifecycle, LifecycleExtensions } from '../../lifecycle/index' import { initTimingExtension } from './timing' @@ -13,15 +13,18 @@ export type AuditLogExtensionParams = { getErrorLevel?: (this: Microfleet, error: any) => ErrorLevel | undefined } -export type MetaLog = { - headers: Record +export type MetaLog< + DefaultRequest extends RequestGenericInterface = RequestGenericInterface, + DefaultReply extends ReplyGenericInterface = ReplyGenericInterface +> = { + headers: DefaultRequest["Headers"] latency: number | null method: string - params: any - query: any + params: DefaultRequest["Params"] + query: DefaultRequest["Querystring"] route: string transport: string - response?: any + response?: DefaultReply["Reply"] err?: Error } diff --git a/packages/plugin-router/src/extensions/validate/query-string-parser.ts b/packages/plugin-router/src/extensions/validate/query-string-parser.ts index 29f640b64..04d04a101 100644 --- a/packages/plugin-router/src/extensions/validate/query-string-parser.ts +++ b/packages/plugin-router/src/extensions/validate/query-string-parser.ts @@ -19,12 +19,14 @@ async function preValidate(request: QSParserAugmentedAction): Promise { const { action } = request const { transformQuery = identity, transformOpts } = action - request.query = transformQuery(parse(query, { - depth: 1, - parameterLimit: 10, - parseArrays: false, - ...transformOpts, - })) + if (typeof query === 'string') { + request.query = transformQuery(parse(query, { + depth: 1, + parameterLimit: 10, + parseArrays: false, + ...transformOpts, + })) + } } } diff --git a/packages/plugin-router/src/index.ts b/packages/plugin-router/src/index.ts index 3e6b63caa..a2f4988bb 100644 --- a/packages/plugin-router/src/index.ts +++ b/packages/plugin-router/src/index.ts @@ -18,7 +18,8 @@ export type { ServiceMiddleware, ServiceActionHandler, ServiceActionAuthGetName, - TransportOptions, + RequestGenericInterface, + ReplyGenericInterface } from './types/router' export type { AuthInfo } from './lifecycle/handlers/auth' diff --git a/packages/plugin-router/src/routes.ts b/packages/plugin-router/src/routes.ts index 96689a7d6..542ee6767 100644 --- a/packages/plugin-router/src/routes.ts +++ b/packages/plugin-router/src/routes.ts @@ -1,15 +1,21 @@ -import { ServiceAction } from './types/router' +import type { ReplyGenericInterface, RequestGenericInterface, ServiceAction } from './types/router' export type RouteName = string export type TransportName = string -export type RoutesCollection = Map +export type RoutesCollection< + DefaultRequest extends RequestGenericInterface = RequestGenericInterface, + DefaultResponse extends ReplyGenericInterface = ReplyGenericInterface +> = Map> export type RoutesGroupedByTransport = Map export default class Routes { protected groupedByTransport: RoutesGroupedByTransport = new Map() - get(transport: TransportName): RoutesCollection { + get< + DefaultRequest extends RequestGenericInterface = RequestGenericInterface, + DefaultResponse extends ReplyGenericInterface = ReplyGenericInterface + >(transport: TransportName): RoutesCollection { const { groupedByTransport } = this - const routes: RoutesCollection = groupedByTransport.get(transport) || new Map() + const routes: RoutesCollection = groupedByTransport.get(transport) || new Map() groupedByTransport.set(transport, routes) diff --git a/packages/plugin-router/src/types/router.ts b/packages/plugin-router/src/types/router.ts index 60162b49e..fffa83b08 100644 --- a/packages/plugin-router/src/types/router.ts +++ b/packages/plugin-router/src/types/router.ts @@ -7,34 +7,50 @@ export type ServiceMiddleware = (this: Microfleet, request: ServiceRequest) => P export type ServiceActionAuthGetName = (request: ServiceRequest) => string export type ServiceActionHandler = ServiceAction['handler'] -export interface ServiceAction { - handler(this: Microfleet, request: ServiceRequest, ...params: any[]): Promise + +export interface RequestGenericInterface { + Params?: unknown; + Headers?: unknown; + Querystring?: unknown; + Locals?: unknown; + TransportOptions?: unknown; +} + +export interface ReplyGenericInterface { + Reply?: unknown; +} +export interface ServiceAction< + DefaultRequest extends RequestGenericInterface = RequestGenericInterface, + DefaultReply extends ReplyGenericInterface = ReplyGenericInterface, +> { + handler(this: Microfleet, request: ServiceRequest, ...params: any[]): Promise actionName: string transports: ServiceRequest['transport'][] - transportOptions?: TransportOptions + transportOptions?: DefaultRequest["TransportOptions"] validateResponse: boolean schema?: string | null | boolean responseSchema?: string; readonly?: boolean } -// eslint-disable-next-line @typescript-eslint/no-empty-interface -export interface TransportOptions {} - -export interface ServiceRequest { +export interface ServiceRequest< + DefaultRequest extends RequestGenericInterface = RequestGenericInterface, + DefaultReply extends ReplyGenericInterface = ReplyGenericInterface, + ErrorArg extends Error & { name: string, code?: string } = any +> { route: string - action: ServiceAction - params: any - headers: any - query: any + action: ServiceAction + params: DefaultRequest["Params"] + headers: DefaultRequest["Headers"] + query: DefaultRequest["Querystring"] method: keyof typeof RequestDataKey transport: typeof ActionTransport[keyof typeof ActionTransport] transportRequest: any | ClientRequest - locals: any + locals: DefaultRequest["Locals"] parentSpan: null span: null log: Logger - response?: unknown - error?: any + response?: DefaultReply["Reply"] + error?: ErrorArg reformatError: boolean } From 677e0d8572ada365ddf1229bad604cb9be96e09a Mon Sep 17 00:00:00 2001 From: AVVS Date: Mon, 17 Feb 2025 14:48:24 -0800 Subject: [PATCH 2/3] fix: revert back-compatible issues --- .../__tests__/artifacts/utils.ts | 6 +++++- .../__tests__/suites/01.router.spec.ts | 16 +++++++------- .../validate/query-string-parser.ts | 21 +++++++++---------- 3 files changed, 22 insertions(+), 21 deletions(-) diff --git a/packages/plugin-router/__tests__/artifacts/utils.ts b/packages/plugin-router/__tests__/artifacts/utils.ts index 355febb57..638c7a499 100644 --- a/packages/plugin-router/__tests__/artifacts/utils.ts +++ b/packages/plugin-router/__tests__/artifacts/utils.ts @@ -59,7 +59,7 @@ const reqPromise = async (reqUrl: URL, requestOptions: any) => { return res } -export function getHTTPRequest(_options: RequestInit & {url: string }): (action: string, params?: any, opts?: any) => Bluebird { +export function getHTTPRequest(_options: RequestInit & { url: string }): (action: string, params?: any, opts?: any) => Bluebird { return (action: string, params?: any, opts: any = {}): Bluebird => { const { url, ...options } = _options const requestOptions = { @@ -70,6 +70,10 @@ export function getHTTPRequest(_options: RequestInit & {url: string }): ...options, } + if (opts.method) { + requestOptions.method = opts.method + } + const reqUrl = new URL(`${url}${action}`) requestOptions.body = (params || opts.json) ? JSON.stringify(opts.json || params) : null diff --git a/packages/plugin-router/__tests__/suites/01.router.spec.ts b/packages/plugin-router/__tests__/suites/01.router.spec.ts index 72628b3d8..4322e3089 100644 --- a/packages/plugin-router/__tests__/suites/01.router.spec.ts +++ b/packages/plugin-router/__tests__/suites/01.router.spec.ts @@ -249,15 +249,13 @@ describe('@microfleet/plugin-router', async () => { } await service.connect() - await Promise.all([ - rget({ sample: 1, bool: true }), - rget({ sample: 'crap', bool: true }, false), - rget({ sample: 13, bool: 'invalid' }, false), - rget({ sample: 13, bool: '0' }), - rget({ sample: 13, bool: '0', oops: 'q' }, false), - rget({ sample: 13.4, bool: '0' }, false), - rget(null, false, { json: { sample: 13.4, bool: '0' }, method: 'post' }), - ]) + await rget({ sample: 1, bool: true }) + await rget({ sample: 'crap', bool: true }, false) + await rget({ sample: 13, bool: 'invalid' }, false) + await rget({ sample: 13, bool: '0' }) + await rget({ sample: 13, bool: '0', oops: 'q' }, false) + await rget({ sample: 13.4, bool: '0' }, false) + await rget(null, false, { json: { sample: 13.4, bool: '0' }, method: 'post' }) }) it('should be able to set schema and responseSchema from action name', async () => { diff --git a/packages/plugin-router/src/extensions/validate/query-string-parser.ts b/packages/plugin-router/src/extensions/validate/query-string-parser.ts index 04d04a101..eb8efd726 100644 --- a/packages/plugin-router/src/extensions/validate/query-string-parser.ts +++ b/packages/plugin-router/src/extensions/validate/query-string-parser.ts @@ -1,11 +1,11 @@ -import { parse } from 'qs' +import { parse, type ParsedQs } from 'qs' import { Lifecycle } from '../../lifecycle/index' import { ServiceRequest } from '../../types/router' type QSParserAugmentedAction = ServiceRequest & { action: ServiceRequest['action'] & { - transformQuery?: (...args: any[]) => any; - transformOpts?: any; + transformQuery?: (input: Record) => ParsedQs; + transformOpts?: Parameters[1]; }; } @@ -19,14 +19,13 @@ async function preValidate(request: QSParserAugmentedAction): Promise { const { action } = request const { transformQuery = identity, transformOpts } = action - if (typeof query === 'string') { - request.query = transformQuery(parse(query, { - depth: 1, - parameterLimit: 10, - parseArrays: false, - ...transformOpts, - })) - } + // module actually handles all variations of input + request.query = transformQuery(parse(query as any, { + depth: 1, + parameterLimit: 10, + parseArrays: false, + ...transformOpts, + })) } } From ca7a125e2f16ae422d4d064210b3a5fc431875db Mon Sep 17 00:00:00 2001 From: AVVS Date: Mon, 17 Feb 2025 15:01:37 -0800 Subject: [PATCH 3/3] chore: remove unused code --- packages/plugin-router-hapi/src/attach.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/packages/plugin-router-hapi/src/attach.ts b/packages/plugin-router-hapi/src/attach.ts index fe5b6b437..657596a46 100644 --- a/packages/plugin-router-hapi/src/attach.ts +++ b/packages/plugin-router-hapi/src/attach.ts @@ -33,10 +33,6 @@ function attachRequestCountEvents(server: Server, router: Microfleet['router']) server.events.on('stop', onStop) } -declare module '@microfleet/plugin-router' { - -} - export default function attachRouter(service: Microfleet, config: RouterHapiPluginConfig): ServerRegisterPluginObject { return { plugin: {