From 9951c755498651affd9e707ee64d8232ed8d39c1 Mon Sep 17 00:00:00 2001 From: Shinwoo PARK Date: Thu, 27 Mar 2025 01:35:01 +0900 Subject: [PATCH] feat: complete proxy to fastify patch --- packages/nestlogged-fastify/src/index.ts | 17 +-- .../nestlogged-fastify/src/logged/class.ts | 81 ++++++++++++++ .../nestlogged-fastify/src/logged/index.ts | 2 + .../src/logged/methods/function.ts | 90 +++++++++++++++ .../src/logged/methods/guard.ts | 80 ++++++++++++++ .../src/logged/methods/index.ts | 5 + .../src/logged/methods/interceptor.ts | 81 ++++++++++++++ .../src/logged/methods/middleware.ts | 81 ++++++++++++++ .../src/logged/methods/route.ts | 104 ++++++++++++++++++ .../override.ts} | 0 10 files changed, 534 insertions(+), 7 deletions(-) create mode 100644 packages/nestlogged-fastify/src/logged/class.ts create mode 100644 packages/nestlogged-fastify/src/logged/index.ts create mode 100644 packages/nestlogged-fastify/src/logged/methods/function.ts create mode 100644 packages/nestlogged-fastify/src/logged/methods/guard.ts create mode 100644 packages/nestlogged-fastify/src/logged/methods/index.ts create mode 100644 packages/nestlogged-fastify/src/logged/methods/interceptor.ts create mode 100644 packages/nestlogged-fastify/src/logged/methods/middleware.ts create mode 100644 packages/nestlogged-fastify/src/logged/methods/route.ts rename packages/nestlogged-fastify/src/{override-patched.ts => logged/override.ts} (100%) diff --git a/packages/nestlogged-fastify/src/index.ts b/packages/nestlogged-fastify/src/index.ts index 63979d3..3fb0e1c 100644 --- a/packages/nestlogged-fastify/src/index.ts +++ b/packages/nestlogged-fastify/src/index.ts @@ -1,10 +1,4 @@ export { - LoggedRoute, - LoggedFunction, - LoggedController, - LoggedInjectable, - LoggedGuard, - LoggedInterceptor, ScopedLogger, InjectLogger, LoggedParam, @@ -14,4 +8,13 @@ export { Logged, Returns, getRequestLogger -} from 'nestlogged'; \ No newline at end of file +} from 'nestlogged'; +export { + LoggedRoute, + LoggedFunction, + LoggedController, + LoggedInjectable, + LoggedGuard, + LoggedInterceptor, + LoggedMiddleware, +} from './logged' \ No newline at end of file diff --git a/packages/nestlogged-fastify/src/logged/class.ts b/packages/nestlogged-fastify/src/logged/class.ts new file mode 100644 index 0000000..9559151 --- /dev/null +++ b/packages/nestlogged-fastify/src/logged/class.ts @@ -0,0 +1,81 @@ +import { + Injectable, + Controller, + ControllerOptions, + ScopeOptions, +} from '@nestjs/common'; +import { loggerInit, RevRequestMethod } from 'nestlogged/lib/logged/utils'; +import { LoggedRoute, LoggedFunction } from './methods'; + +export function LoggedInjectable( + options?: ScopeOptions & { verbose?: boolean }, +) { + return (target: any) => { + loggerInit(target.prototype); + + const logger = target.prototype.logger; + + const methods = Object.getOwnPropertyNames(target.prototype); + + methods.forEach((method) => { + if ( + method !== 'constructor' && + typeof target.prototype[method] === 'function' + ) { + if (options && options.verbose) + logger.log(`LoggedFunction applied to ${method}`); + LoggedFunction()(target.prototype, method, { + value: target.prototype[method], + }); + } + }); + + Injectable(options)(target); + }; +} + +export function LoggedController(): (target: any) => void; +export function LoggedController( + prefix: string | string[], +): (target: any) => void; +export function LoggedController( + options: ControllerOptions & { verbose?: boolean }, +): (target: any) => void; + +export function LoggedController(param?: any): (target: any) => void { + return (target: any) => { + loggerInit(target.prototype); + + const logger = target.prototype.logger; + + const methods = Object.getOwnPropertyNames(target.prototype); + + let verbose = + typeof param === 'object' && Object.keys(param).includes('verbose') + ? param.verbose + : false; + + methods.forEach((method) => { + if ( + method !== 'constructor' && + typeof target.prototype[method] === 'function' + ) { + if (verbose) { + const path = Reflect.getMetadata('path', target.prototype[method]); + const httpMethod = Reflect.getMetadata( + 'method', + target.prototype[method], + ); + logger.log( + `LoggedRoute applied to ${method} (${RevRequestMethod[httpMethod]} ${path})`, + ); + } + LoggedRoute()(target.prototype, method, { + value: target.prototype[method], + }); + } + }); + + Controller(param)(target); + }; +} diff --git a/packages/nestlogged-fastify/src/logged/index.ts b/packages/nestlogged-fastify/src/logged/index.ts new file mode 100644 index 0000000..67b66e2 --- /dev/null +++ b/packages/nestlogged-fastify/src/logged/index.ts @@ -0,0 +1,2 @@ +export * from './methods'; +export { LoggedController, LoggedInjectable } from './class'; diff --git a/packages/nestlogged-fastify/src/logged/methods/function.ts b/packages/nestlogged-fastify/src/logged/methods/function.ts new file mode 100644 index 0000000..cfea25d --- /dev/null +++ b/packages/nestlogged-fastify/src/logged/methods/function.ts @@ -0,0 +1,90 @@ +import { Logger } from '@nestjs/common'; +import { OverrideBuildOptions, loggerInit } from 'nestlogged/lib/logged/utils'; +import { LoggedMetadata, nestLoggedMetadata } from 'nestlogged/lib/logged/metadata'; +import { + loggedParam, + scopedLogger, + returns, + ReturnsReflectData, + LoggedParamReflectData, +} from 'nestlogged/lib/reflected'; +import { overrideBuild } from '../override'; + +export function LoggedFunction, R>( + options?: Partial, +) { + return ( + _target: any, + key: string, + descriptor: TypedPropertyDescriptor<(...args: F) => R | Promise>, + ) => { + loggerInit(_target); + + const logger: Logger = _target.logger; + + const fn = descriptor.value; + + if (!fn || typeof fn !== 'function') { + logger.warn( + `LoggedFunction decorator applied to non-function property: ${key}`, + ); + return; + } + + const logMetadata: LoggedMetadata | undefined = Reflect.getOwnMetadata( + nestLoggedMetadata, + _target, + key, + ); + if (logMetadata) { + // already applied, override instead + logMetadata.updateOption(options); + return; + } + const newMetadata = new LoggedMetadata(options); + + const all = Reflect.getMetadataKeys(fn).map((k) => [ + k, + Reflect.getMetadata(k, fn), + ]); + + const scopedLoggerInjectableParam: number = Reflect.getOwnMetadata( + scopedLogger, + _target, + key, + ); + + const loggedParams: LoggedParamReflectData[] = Reflect.getOwnMetadata( + loggedParam, + _target, + key, + ); + + const returnsData: ReturnsReflectData[] | true = Reflect.getOwnMetadata( + returns, + fn, + ); + + const overrideFunction = overrideBuild( + 'function', + fn, + logger, + { + scopedLoggerInjectableParam, + loggedParams, + }, + key, + returnsData, + newMetadata, + ); + + _target[key] = overrideFunction; + descriptor.value = overrideFunction; + + Reflect.defineMetadata(nestLoggedMetadata, newMetadata, _target, key); + all.forEach(([k, v]) => { + Reflect.defineMetadata(k, v, _target[key]); + Reflect.defineMetadata(k, v, descriptor.value); + }); + }; +} diff --git a/packages/nestlogged-fastify/src/logged/methods/guard.ts b/packages/nestlogged-fastify/src/logged/methods/guard.ts new file mode 100644 index 0000000..d68ba34 --- /dev/null +++ b/packages/nestlogged-fastify/src/logged/methods/guard.ts @@ -0,0 +1,80 @@ +import { ExecutionContext, Logger } from '@nestjs/common'; +import { OverrideBuildOptions, loggerInit } from 'nestlogged/lib/logged/utils'; +import { LoggedMetadata, nestLoggedMetadata } from 'nestlogged/lib/logged/metadata'; +import { scopedLogger, returns, ReturnsReflectData } from 'nestlogged/lib/reflected'; +import { overrideBuild } from '../override'; + +export function LoggedGuard, R>( + options?: Partial, +) { + return ( + _target: any, + key: string, + descriptor: TypedPropertyDescriptor< + (context: ExecutionContext, ...args: F) => R + >, + ) => { + loggerInit(_target); + + const logger: Logger = _target.logger; + + const fn = descriptor.value; + + if (!fn || typeof fn !== 'function') { + logger.warn( + `LoggedGuard decorator applied to non-function property: ${key}`, + ); + return; + } + + const logMetadata: LoggedMetadata | undefined = Reflect.getOwnMetadata( + nestLoggedMetadata, + _target, + key, + ); + if (logMetadata) { + // already applied, override instead + logMetadata.updateOption(options); + return; + } + const newMetadata = new LoggedMetadata(options); + + const all = Reflect.getMetadataKeys(fn).map((k) => [ + k, + Reflect.getMetadata(k, fn), + ]); + + const scopedLoggerInjectableParam: number = Reflect.getOwnMetadata( + scopedLogger, + _target, + key, + ); + + const returnsData: ReturnsReflectData[] | true = Reflect.getOwnMetadata( + returns, + fn, + ); + + const overrideFunction = overrideBuild( + 'guard', + fn, + logger, + { + scopedLoggerInjectableParam, + loggedParams: [], + }, + _target.constructor.name, + returnsData, + newMetadata, + ); + + _target[key] = overrideFunction; + descriptor.value = overrideFunction; + + Reflect.defineMetadata(nestLoggedMetadata, newMetadata, _target, key); + all.forEach(([k, v]) => { + Reflect.defineMetadata(k, v, _target[key]); + Reflect.defineMetadata(k, v, descriptor.value); + }); + }; +} diff --git a/packages/nestlogged-fastify/src/logged/methods/index.ts b/packages/nestlogged-fastify/src/logged/methods/index.ts new file mode 100644 index 0000000..9aeedf1 --- /dev/null +++ b/packages/nestlogged-fastify/src/logged/methods/index.ts @@ -0,0 +1,5 @@ +export { LoggedFunction } from './function'; +export { LoggedRoute } from './route'; +export { LoggedGuard } from './guard'; +export { LoggedInterceptor } from './interceptor'; +export { LoggedMiddleware } from './middleware'; diff --git a/packages/nestlogged-fastify/src/logged/methods/interceptor.ts b/packages/nestlogged-fastify/src/logged/methods/interceptor.ts new file mode 100644 index 0000000..39cb5d6 --- /dev/null +++ b/packages/nestlogged-fastify/src/logged/methods/interceptor.ts @@ -0,0 +1,81 @@ +import { OverrideBuildOptions } from 'nestlogged/lib/logged/utils'; +import { ExecutionContext, Logger } from '@nestjs/common'; +import { loggerInit } from 'nestlogged/lib/logged/utils'; +import { LoggedMetadata, nestLoggedMetadata } from 'nestlogged/lib/logged/metadata'; +import { scopedLogger, returns, ReturnsReflectData } from 'nestlogged/lib/reflected'; +import { overrideBuild } from '../override'; + +export function LoggedInterceptor, R>( + options?: Partial, +) { + return ( + _target: any, + key: string, + descriptor: TypedPropertyDescriptor< + (context: ExecutionContext, ...args: F) => R + >, + ) => { + loggerInit(_target); + + const logger: Logger = _target.logger; + + const fn = descriptor.value; + + if (!fn || typeof fn !== 'function') { + logger.warn( + `LoggedInterceptor decorator applied to non-function property: ${key}`, + ); + return; + } + + const logMetadata: LoggedMetadata | undefined = Reflect.getOwnMetadata( + nestLoggedMetadata, + _target, + key, + ); + if (logMetadata) { + // already applied, override instead + logMetadata.updateOption(options); + return; + } + const newMetadata = new LoggedMetadata(options); + + const all = Reflect.getMetadataKeys(fn).map((k) => [ + k, + Reflect.getMetadata(k, fn), + ]); + + const scopedLoggerInjectableParam: number = Reflect.getOwnMetadata( + scopedLogger, + _target, + key, + ); + + const returnsData: ReturnsReflectData[] | true = Reflect.getOwnMetadata( + returns, + fn, + ); + + const overrideFunction = overrideBuild( + 'interceptor', + fn, + logger, + { + scopedLoggerInjectableParam, + loggedParams: [], + }, + _target.constructor.name, + returnsData, + newMetadata, + ); + + _target[key] = overrideFunction; + descriptor.value = overrideFunction; + + Reflect.defineMetadata(nestLoggedMetadata, newMetadata, _target, key); + all.forEach(([k, v]) => { + Reflect.defineMetadata(k, v, _target[key]); + Reflect.defineMetadata(k, v, descriptor.value); + }); + }; +} diff --git a/packages/nestlogged-fastify/src/logged/methods/middleware.ts b/packages/nestlogged-fastify/src/logged/methods/middleware.ts new file mode 100644 index 0000000..caaff1a --- /dev/null +++ b/packages/nestlogged-fastify/src/logged/methods/middleware.ts @@ -0,0 +1,81 @@ +import { OverrideBuildOptions } from 'nestlogged/lib/logged/utils'; +import { Logger } from '@nestjs/common'; +import { loggerInit } from 'nestlogged/lib/logged/utils'; +import { LoggedMetadata, nestLoggedMetadata } from 'nestlogged/lib/logged/metadata'; +import { scopedLogger, returns, ReturnsReflectData } from 'nestlogged/lib/reflected'; +import { overrideBuild } from '../override'; + +export function LoggedMiddleware, R>( + options?: Partial, +) { + return ( + _target: any, + key: string, + descriptor: TypedPropertyDescriptor< + (...args: F) => R + >, + ) => { + loggerInit(_target); + + const logger: Logger = _target.logger; + + const fn = descriptor.value; + + if (!fn || typeof fn !== 'function') { + logger.warn( + `LoggedMiddleware decorator applied to non-function property: ${key}`, + ); + return; + } + + const logMetadata: LoggedMetadata | undefined = Reflect.getOwnMetadata( + nestLoggedMetadata, + _target, + key, + ); + if (logMetadata) { + // already applied, override instead + logMetadata.updateOption(options); + return; + } + const newMetadata = new LoggedMetadata(options); + + const all = Reflect.getMetadataKeys(fn).map((k) => [ + k, + Reflect.getMetadata(k, fn), + ]); + + const scopedLoggerInjectableParam: number = Reflect.getOwnMetadata( + scopedLogger, + _target, + key, + ); + + const returnsData: ReturnsReflectData[] | true = Reflect.getOwnMetadata( + returns, + fn, + ); + + const overrideFunction = overrideBuild( + 'middleware', + fn, + logger, + { + scopedLoggerInjectableParam, + loggedParams: [], + }, + _target.constructor.name, + returnsData, + newMetadata, + ); + + _target[key] = overrideFunction; + descriptor.value = overrideFunction; + + Reflect.defineMetadata(nestLoggedMetadata, newMetadata, _target, key); + all.forEach(([k, v]) => { + Reflect.defineMetadata(k, v, _target[key]); + Reflect.defineMetadata(k, v, descriptor.value); + }); + }; +} diff --git a/packages/nestlogged-fastify/src/logged/methods/route.ts b/packages/nestlogged-fastify/src/logged/methods/route.ts new file mode 100644 index 0000000..e877141 --- /dev/null +++ b/packages/nestlogged-fastify/src/logged/methods/route.ts @@ -0,0 +1,104 @@ +import { RequestMethod } from '@nestjs/common'; +import { OverrideBuildOptions, loggerInit, RevRequestMethod } from 'nestlogged/lib/logged/utils'; +import { LoggedMetadata, nestLoggedMetadata } from 'nestlogged/lib/logged/metadata'; +import { + loggedParam, + scopedLogger, + returns, + ReturnsReflectData, + LoggedParamReflectData, +} from 'nestlogged/lib/reflected'; +import { overrideBuild } from '../override'; +import { createRouteParamDecorator } from 'nestlogged/lib/internals/nest'; + +export function LoggedRoute, R>( + route?: string, + options?: Partial, +) { + return ( + _target: any, + key: string, + descriptor: TypedPropertyDescriptor<(...args: F) => R>, + ) => { + loggerInit(_target); + + const logger = _target.logger; + + const fn = descriptor.value; + + if (!fn || typeof fn !== 'function') { + logger.warn( + `LoggedRoute decorator applied to non-function property: ${key}`, + ); + return; + } + + const logMetadata: LoggedMetadata | undefined = Reflect.getOwnMetadata( + nestLoggedMetadata, + _target, + key, + ); + if (logMetadata) { + // already applied, override instead + logMetadata.updateOption(options); + return; + } + const newMetadata = new LoggedMetadata(options); + + const all = Reflect.getMetadataKeys(fn).map((k) => [ + k, + Reflect.getMetadata(k, fn), + ]); + + const httpPath: string = Reflect.getMetadata('path', fn); + const httpMethod: RequestMethod = Reflect.getMetadata('method', fn); + + const fullRoute = `${_target.constructor.name}::${route ?? httpPath}[${ + RevRequestMethod[httpMethod] + }]`; + + const scopedLoggerInjectableParam: number = Reflect.getOwnMetadata( + scopedLogger, + _target, + key, + ); + // if @InjectLogger exists, fake nestjs as it is @Req() + if (scopedLoggerInjectableParam !== undefined) { + createRouteParamDecorator(0)()(_target, key, scopedLoggerInjectableParam); + } + + const loggedParams: LoggedParamReflectData[] = Reflect.getOwnMetadata( + loggedParam, + _target, + key, + ); + + const returnsData: ReturnsReflectData[] | true = Reflect.getOwnMetadata( + returns, + fn, + ); + + const overrideFunction = overrideBuild( + 'route', + fn, + logger, + { + scopedLoggerInjectableParam, + loggedParams, + }, + key, + returnsData, + newMetadata, + fullRoute, + ); + + _target[key] = overrideFunction; + descriptor.value = overrideFunction; + + Reflect.defineMetadata(nestLoggedMetadata, newMetadata, _target, key); + all.forEach(([k, v]) => { + Reflect.defineMetadata(k, v, _target[key]); + Reflect.defineMetadata(k, v, descriptor.value); + }); + }; +} diff --git a/packages/nestlogged-fastify/src/override-patched.ts b/packages/nestlogged-fastify/src/logged/override.ts similarity index 100% rename from packages/nestlogged-fastify/src/override-patched.ts rename to packages/nestlogged-fastify/src/logged/override.ts