diff --git a/src/index.ts b/src/index.ts index adeb503..df98b47 100644 --- a/src/index.ts +++ b/src/index.ts @@ -5,4 +5,13 @@ export { LoggedInjectable, } from "./logged"; export { ScopedLogger } from "./logger"; -export { InjectLogger, LoggedParam, ScopeKey, ShouldScoped, Returns } from "./reflected"; +export { + InjectLogger, + LoggedParam, + LoggedHeaders, + LoggedBody, + LoggedQuery, + ScopeKey, + ShouldScoped, + Returns +} from "./reflected"; diff --git a/src/reflected.ts b/src/reflected.ts index ce08c66..7219c67 100644 --- a/src/reflected.ts +++ b/src/reflected.ts @@ -1,3 +1,50 @@ +import {RouteParamtypes} from '@nestjs/common/enums/route-paramtypes.enum'; +import {assignMetadata, ParamData, PipeTransform, RouteParamMetadata, Type} from "@nestjs/common"; +import {isNil, isString} from "@nestjs/common/utils/shared.utils"; + +const ROUTE_ARGS_METADATA = '__routeArguments__'; +export const HEADERS_METADATA = '__headers__'; + +function createRouteParamDecorator(paramtype: RouteParamtypes) { + return (data?: ParamData): ParameterDecorator => + (target, key, index) => { + const args = + Reflect.getMetadata(ROUTE_ARGS_METADATA, target.constructor, key) || {}; + Reflect.defineMetadata( + ROUTE_ARGS_METADATA, + assignMetadata>( + args, + paramtype, + index, + data, + ), + target.constructor, + key, + ); + }; +} + +const createPipesRouteParamDecorator = + (paramtype: RouteParamtypes) => + ( + data?: any, + ...pipes: (Type | PipeTransform)[] + ): ParameterDecorator => + (target, key, index) => { + const args = + Reflect.getMetadata(ROUTE_ARGS_METADATA, target.constructor, key) || {}; + const hasParamData = isNil(data) || isString(data); + const paramData = hasParamData ? data : undefined; + const paramPipes = hasParamData ? pipes : [data, ...pipes]; + + Reflect.defineMetadata( + ROUTE_ARGS_METADATA, + assignMetadata(args, paramtype, index, paramData, ...paramPipes), + target.constructor, + key, + ); + }; + export type Path = string | string[]; export type Paths = Path[]; @@ -39,7 +86,12 @@ export function InjectLogger( Reflect.defineMetadata(scopedLogger, parameterIndex, target, propertyKey); } -export function LoggedParam(name: string, options?: IncludeExcludePath) { +type ParameterDecoratorType = (target: any, propertyKey: string | symbol, parameterIndex: number) => void + +function createLoggedFunctionParam( + name: string, + options?: IncludeExcludePath +): ParameterDecoratorType { return ( target: any, propertyKey: string | symbol, @@ -71,6 +123,114 @@ export function LoggedParam(name: string, options?: IncludeExcludePath) { }; } +type LoggedParamReturns = (name: string, options?: IncludeExcludePath) => ParameterDecoratorType; + +export const Logged: LoggedParamReturns = (name, options) => + createLoggedFunctionParam(name, options) + +type Pipe = Type | PipeTransform + +export function LoggedParam(): LoggedParamReturns; +export function LoggedParam( + ...pipes: Pipe[] +): LoggedParamReturns; +export function LoggedParam( + property: string, + ...pipes: Pipe[] +): LoggedParamReturns; +export function LoggedParam( + property?: string | Pipe, + ...pipes: Pipe[] +): LoggedParamReturns { + return (name, options) => { + return (target, propertyKey, parameterIndex) => { + createPipesRouteParamDecorator(RouteParamtypes.PARAM)( + property, + ...pipes, + )( + target, + propertyKey, + parameterIndex, + ); + createLoggedFunctionParam(name, options)( + target, + propertyKey, + parameterIndex, + ) + } + } +} + +export function LoggedQuery(): LoggedParamReturns; +export function LoggedQuery( + ...pipes: Pipe[] +): LoggedParamReturns; +export function LoggedQuery( + property: string, + ...pipes: Pipe[] +): LoggedParamReturns; +export function LoggedQuery( + property?: string | Pipe, + ...pipes: Pipe[] +): LoggedParamReturns { + return (name, options) => { + return (target, propertyKey, parameterIndex) => { + createPipesRouteParamDecorator(RouteParamtypes.QUERY)( + property, ...pipes + )( + target, propertyKey, parameterIndex, + ); + + createLoggedFunctionParam(name, options)( + target, propertyKey, parameterIndex, + ); + } + } +} + +export function LoggedBody(): LoggedParamReturns; +export function LoggedBody( + ...pipes: Pipe[] +): LoggedParamReturns; +export function LoggedBody( + property: string, + ...pipes: Pipe[] +): LoggedParamReturns; +export function LoggedBody( + property?: string | Pipe, + ...pipes: Pipe[] +): LoggedParamReturns { + return (name, options) => { + return (target, propertyKey, parameterIndex) => { + createPipesRouteParamDecorator(RouteParamtypes.BODY)( + property, + ...pipes, + )( + target, propertyKey, parameterIndex + ); + + createLoggedFunctionParam(name, options)( + target, propertyKey, parameterIndex, + ); + } + } +} + +export function LoggedHeaders(property?: string): LoggedParamReturns { + return (name, options) => { + return (target, propertyKey, parameterIndex) => { + createRouteParamDecorator(RouteParamtypes.HEADERS)(property)( + target, propertyKey, parameterIndex, + ); + + createLoggedFunctionParam(name, options)( + target, propertyKey, parameterIndex, + ) + } + } +}; + + export function ScopeKey( name: string, options?: { path?: Path; priority?: number } diff --git a/src/test/index.ts b/src/test/index.ts index 2fd4422..daa8e7c 100644 --- a/src/test/index.ts +++ b/src/test/index.ts @@ -2,7 +2,7 @@ import { LoggedFunction, LoggedInjectable } from "../logged"; import { ScopedLogger } from "../logger"; import { InjectLogger, - LoggedParam, + Logged, Returns, ScopeKey, ShouldScoped, @@ -24,41 +24,41 @@ const testObject: TestObject = { @LoggedInjectable() class LoggedClass { - async testParameterLoggingWithoutInjection(@LoggedParam("key") key: number) { + async testParameterLoggingWithoutInjection(@Logged("key") key: number) { console.log(key); } async testMultiParameterLoggingWithoutInjection( - @LoggedParam("key") key: number, - @LoggedParam("key2") key2: string + @Logged("key") key: number, + @Logged("key2") key2: string ) { console.log(key, key2); } async testParameterLoggingWithInjection( - @LoggedParam("key") key: number, + @Logged("key") key: number, @InjectLogger logger?: ScopedLogger ) { logger.log(key.toString()); } async testMultiParameterLoggingWithInjection( - @LoggedParam("key") key: number, - @LoggedParam("key2") key2: string, + @Logged("key") key: number, + @Logged("key2") key2: string, @InjectLogger logger?: ScopedLogger ) { logger.log(key.toString() + key2); } async testObjectParameterLogging( - @LoggedParam("key") key: TestObject, + @Logged("key") key: TestObject, @InjectLogger logger?: ScopedLogger ) { logger.log(JSON.stringify(key)); } async testObjectParameterDotIncludeLogging( - @LoggedParam("key", { includePath: ["a", "b.c", "d.0", "e"] }) + @Logged("key", { includePath: ["a", "b.c", "d.0", "e"] }) key: TestObject, @InjectLogger logger?: ScopedLogger ) { @@ -66,7 +66,7 @@ class LoggedClass { } async testObjectParameterArrayIncludeLogging( - @LoggedParam("key", { includePath: [["a"], ["b", "c"], ["d", "0"], ["e"]] }) + @Logged("key", { includePath: [["a"], ["b", "c"], ["d", "0"], ["e"]] }) key: TestObject, @InjectLogger logger?: ScopedLogger ) { @@ -74,7 +74,7 @@ class LoggedClass { } async testObjectParameterDotExcludeLogging( - @LoggedParam("key", { excludePath: ["a", "b.c", "d.0", "e"] }) + @Logged("key", { excludePath: ["a", "b.c", "d.0", "e"] }) key: TestObject, @InjectLogger logger?: ScopedLogger ) { @@ -82,7 +82,7 @@ class LoggedClass { } async testObjectParameterArrayExcludeLogging( - @LoggedParam("key", { excludePath: [["a"], ["b", "c"], ["d", "0"], ["e"]] }) + @Logged("key", { excludePath: [["a"], ["b", "c"], ["d", "0"], ["e"]] }) key: TestObject, @InjectLogger logger?: ScopedLogger ) { @@ -90,22 +90,22 @@ class LoggedClass { } async testScopedLogging( - @LoggedParam("key") @ScopeKey("scopekey") key: string, - @LoggedParam("key2") key2: number, + @Logged("key") @ScopeKey("scopekey") key: string, + @Logged("key2") key2: number, @InjectLogger logger?: ScopedLogger ) { logger.log(key + key2.toString()); } async testPathScopedLogging( - @LoggedParam("key") @ScopeKey("scopekey", { path: "b.c" }) key: TestObject, + @Logged("key") @ScopeKey("scopekey", { path: "b.c" }) key: TestObject, @InjectLogger logger?: ScopedLogger ) { logger.log(JSON.stringify(key)); } async testOrScopedLogging( - @LoggedParam("key") + @Logged("key") @ScopeKey("scopekey-a", { path: "a" }) @ScopeKey("scopekey-b", { path: "b" }) key: { a: string } | { b: string }, @@ -115,7 +115,7 @@ class LoggedClass { } async testPriorityScopedLogging( - @LoggedParam("key") + @Logged("key") @ScopeKey("scopekey-a", { path: "a", priority: 0.5 }) @ScopeKey("scopekey-b", { path: "b" }) // default 1 key: { a?: string; b?: string }, @@ -126,7 +126,7 @@ class LoggedClass { } async testOptionalScopedLogging( - @LoggedParam("key") + @Logged("key") @ScopeKey("scopekey") key?: string, @InjectLogger logger?: ScopedLogger @@ -136,7 +136,7 @@ class LoggedClass { @ShouldScoped // Warn if there is no valid scopekey async testShouldScopedLogging( - @LoggedParam("key") + @Logged("key") @ScopeKey("scopekey") key?: string, @InjectLogger logger?: ScopedLogger @@ -146,7 +146,7 @@ class LoggedClass { @Returns({ result: "http.result", userId: "body.user.id" }) async testReturnLogging( - @LoggedParam("userId") + @Logged("userId") userId: string, @InjectLogger logger?: ScopedLogger ) { @@ -168,7 +168,7 @@ class LoggedClass { @Returns({ result: "http.result", userId: "body.user.id" }) async testMissingReturnLogging( - @LoggedParam("userId") + @Logged("userId") userId: string, @InjectLogger logger?: ScopedLogger ) { @@ -186,7 +186,7 @@ class LoggedClass { @Returns() async testRawObjectReturnLogging( - @LoggedParam("userId") + @Logged("userId") userId: string, @InjectLogger logger?: ScopedLogger ) { @@ -204,7 +204,7 @@ class LoggedClass { @Returns() async testRawValueReturnLogging( - @LoggedParam("userId") + @Logged("userId") userId: string, @InjectLogger logger?: ScopedLogger ) { @@ -217,7 +217,7 @@ class LoggedClass { } async testLoggerRootLogging(@InjectLogger logger?: ScopedLogger) { - this.testLoggerRootLogging2(logger); + await this.testLoggerRootLogging2(logger); } testSyncLogging(@InjectLogger logger?: ScopedLogger) { @@ -227,21 +227,21 @@ class LoggedClass { class LoggedMethodsClass { @LoggedFunction - async testParameterLoggingWithoutInjection(@LoggedParam("key") key: number) { + async testParameterLoggingWithoutInjection(@Logged("key") key: number) { console.log(key); } @LoggedFunction async testMultiParameterLoggingWithoutInjection( - @LoggedParam("key") key: number, - @LoggedParam("key2") key2: string + @Logged("key") key: number, + @Logged("key2") key2: string ) { console.log(key, key2); } @LoggedFunction async testParameterLoggingWithInjection( - @LoggedParam("key") key: number, + @Logged("key") key: number, @InjectLogger logger?: ScopedLogger ) { logger.log(key.toString()); @@ -249,8 +249,8 @@ class LoggedMethodsClass { @LoggedFunction async testMultiParameterLoggingWithInjection( - @LoggedParam("key") key: number, - @LoggedParam("key2") key2: string, + @Logged("key") key: number, + @Logged("key2") key2: string, @InjectLogger logger?: ScopedLogger ) { logger.log(key.toString() + key2); @@ -258,7 +258,7 @@ class LoggedMethodsClass { @LoggedFunction async testObjectParameterLogging( - @LoggedParam("key") key: TestObject, + @Logged("key") key: TestObject, @InjectLogger logger?: ScopedLogger ) { logger.log(JSON.stringify(key)); @@ -266,7 +266,7 @@ class LoggedMethodsClass { @LoggedFunction async testObjectParameterDotIncludeLogging( - @LoggedParam("key", { includePath: ["a", "b.c", "d.0", "e"] }) + @Logged("key", { includePath: ["a", "b.c", "d.0", "e"] }) key: TestObject, @InjectLogger logger?: ScopedLogger ) { @@ -275,7 +275,7 @@ class LoggedMethodsClass { @LoggedFunction async testObjectParameterArrayIncludeLogging( - @LoggedParam("key", { includePath: [["a"], ["b", "c"], ["d", "0"], ["e"]] }) + @Logged("key", { includePath: [["a"], ["b", "c"], ["d", "0"], ["e"]] }) key: TestObject, @InjectLogger logger?: ScopedLogger ) { @@ -284,7 +284,7 @@ class LoggedMethodsClass { @LoggedFunction async testObjectParameterDotExcludeLogging( - @LoggedParam("key", { excludePath: ["a", "b.c", "d.0", "e"] }) + @Logged("key", { excludePath: ["a", "b.c", "d.0", "e"] }) key: TestObject, @InjectLogger logger?: ScopedLogger ) { @@ -293,7 +293,7 @@ class LoggedMethodsClass { @LoggedFunction async testObjectParameterArrayExcludeLogging( - @LoggedParam("key", { excludePath: [["a"], ["b", "c"], ["d", "0"], ["e"]] }) + @Logged("key", { excludePath: [["a"], ["b", "c"], ["d", "0"], ["e"]] }) key: TestObject, @InjectLogger logger?: ScopedLogger ) { @@ -302,8 +302,8 @@ class LoggedMethodsClass { @LoggedFunction async testScopedLogging( - @LoggedParam("key") @ScopeKey("scopekey") key: string, - @LoggedParam("key2") key2: number, + @Logged("key") @ScopeKey("scopekey") key: string, + @Logged("key2") key2: number, @InjectLogger logger?: ScopedLogger ) { logger.log(key + key2.toString()); @@ -311,7 +311,7 @@ class LoggedMethodsClass { @LoggedFunction async testPathScopedLogging( - @LoggedParam("key") @ScopeKey("scopekey", { path: "b.c" }) key: TestObject, + @Logged("key") @ScopeKey("scopekey", { path: "b.c" }) key: TestObject, @InjectLogger logger?: ScopedLogger ) { logger.log(JSON.stringify(key)); @@ -319,7 +319,7 @@ class LoggedMethodsClass { @LoggedFunction async testOrScopedLogging( - @LoggedParam("key") + @Logged("key") @ScopeKey("scopekey-a", { path: "a" }) @ScopeKey("scopekey-b", { path: "b" }) key: { a: string } | { b: string }, @@ -330,7 +330,7 @@ class LoggedMethodsClass { @LoggedFunction async testPriorityScopedLogging( - @LoggedParam("key") + @Logged("key") @ScopeKey("scopekey-a", { path: "a", priority: 0.5 }) @ScopeKey("scopekey-b", { path: "b" }) // default 1 key: { a?: string; b?: string }, @@ -342,7 +342,7 @@ class LoggedMethodsClass { @LoggedFunction async testOptionalScopedLogging( - @LoggedParam("key") + @Logged("key") @ScopeKey("scopekey") key?: string, @InjectLogger logger?: ScopedLogger @@ -353,7 +353,7 @@ class LoggedMethodsClass { @LoggedFunction @ShouldScoped // Warn if there is no valid scopekey async testShouldScopedLogging( - @LoggedParam("key") + @Logged("key") @ScopeKey("scopekey") key?: string, @InjectLogger logger?: ScopedLogger @@ -364,7 +364,7 @@ class LoggedMethodsClass { @LoggedFunction @Returns({ result: "http.result", userId: "body.user.id" }) async testReturnLogging( - @LoggedParam("userId") + @Logged("userId") userId: string, @InjectLogger logger?: ScopedLogger ) { @@ -387,7 +387,7 @@ class LoggedMethodsClass { @LoggedFunction @Returns({ result: "http.result", userId: "body.user.id" }) async testMissingReturnLogging( - @LoggedParam("userId") + @Logged("userId") userId: string, @InjectLogger logger?: ScopedLogger ) { @@ -406,7 +406,7 @@ class LoggedMethodsClass { @LoggedFunction @Returns() async testRawObjectReturnLogging( - @LoggedParam("userId") + @Logged("userId") userId: string, @InjectLogger logger?: ScopedLogger ) { @@ -425,7 +425,7 @@ class LoggedMethodsClass { @LoggedFunction @Returns() async testRawValueReturnLogging( - @LoggedParam("userId") + @Logged("userId") userId: string, @InjectLogger logger?: ScopedLogger ) { @@ -440,7 +440,7 @@ class LoggedMethodsClass { @LoggedFunction async testLoggerRootLogging(@InjectLogger logger?: ScopedLogger) { - this.testLoggerRootLogging2(logger); + await this.testLoggerRootLogging2(logger); } @LoggedFunction