From 1a811e528fdab2648aa43841ee23661ea9a9c17f Mon Sep 17 00:00:00 2001 From: Chau Tran Date: Mon, 11 Jan 2021 11:11:31 -0600 Subject: [PATCH] feat(nestjs): add MapInterceptor --- packages/nestjs/src/index.ts | 1 + packages/nestjs/src/lib/interceptors/index.ts | 1 + .../src/lib/interceptors/map.interceptor.ts | 71 +++++++++++++++++++ .../src/lib/interceptors/memoize.util.ts | 21 ++++++ 4 files changed, 94 insertions(+) diff --git a/packages/nestjs/src/index.ts b/packages/nestjs/src/index.ts index 20833a4e4..6dea53ce2 100644 --- a/packages/nestjs/src/index.ts +++ b/packages/nestjs/src/index.ts @@ -2,3 +2,4 @@ export * from './lib/automapper.module'; export * from './lib/interfaces'; export * from './lib/di'; export * from './lib/abstracts'; +export * from './lib/interceptors'; diff --git a/packages/nestjs/src/lib/interceptors/index.ts b/packages/nestjs/src/lib/interceptors/index.ts index e69de29bb..e852578c9 100644 --- a/packages/nestjs/src/lib/interceptors/index.ts +++ b/packages/nestjs/src/lib/interceptors/index.ts @@ -0,0 +1 @@ +export * from './map.interceptor'; diff --git a/packages/nestjs/src/lib/interceptors/map.interceptor.ts b/packages/nestjs/src/lib/interceptors/map.interceptor.ts index e69de29bb..b934077ef 100644 --- a/packages/nestjs/src/lib/interceptors/map.interceptor.ts +++ b/packages/nestjs/src/lib/interceptors/map.interceptor.ts @@ -0,0 +1,71 @@ +import type { MapOptions, Mapper } from '@automapper/types'; +import type { + CallHandler, + ExecutionContext, + NestInterceptor, +} from '@nestjs/common'; +import { mixin, Optional } from '@nestjs/common'; +import type { Observable } from 'rxjs'; +import { map } from 'rxjs/operators'; +import { InjectMapper } from '../di'; +import { memoize } from './memoize.util'; + +export const MapInterceptor: ( + to: unknown, + from: unknown, + options?: { isArray?: boolean; mapperName?: string } & MapOptions +) => NestInterceptor = memoize(createMapInterceptor); + +function createMapInterceptor( + to: unknown, + from: unknown, + options?: { isArray?: boolean; mapperName?: string } +): new (...args) => NestInterceptor { + const { isArray = false, mapperName, ...mapOptions } = options || {}; + + class MixinMapInterceptor implements NestInterceptor { + constructor( + @Optional() @InjectMapper(mapperName) private readonly mapper: Mapper + ) {} + + async intercept( + context: ExecutionContext, + next: CallHandler + ): Promise> { + if (!this.mapper || !to || !from) { + return next.handle(); + } + + try { + return next.handle().pipe( + map((response) => { + if (isArray) { + if (!Array.isArray(response)) return response; + return this.mapper.mapArray( + response, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + to as any, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + from as any, + mapOptions + ); + } + + return this.mapper.map( + response, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + to as any, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + from as any, + mapOptions + ); + }) + ); + } catch { + return next.handle(); + } + } + } + + return mixin(MixinMapInterceptor); +} diff --git a/packages/nestjs/src/lib/interceptors/memoize.util.ts b/packages/nestjs/src/lib/interceptors/memoize.util.ts index e69de29bb..52042be85 100644 --- a/packages/nestjs/src/lib/interceptors/memoize.util.ts +++ b/packages/nestjs/src/lib/interceptors/memoize.util.ts @@ -0,0 +1,21 @@ +const defaultKey = 'default'; + +// eslint-disable-next-line @typescript-eslint/ban-types +export function memoize(fn: Function) { + const cache = {}; + return (...args) => { + const n = + args.reduce( + (key, arg) => + key.concat('|', typeof arg === 'string' ? arg : arg.toString()), + '' + ) || defaultKey; + if (n in cache) { + return cache[n]; + } + + const result = n === defaultKey ? fn() : fn(...args); + cache[n] = result; + return result; + }; +}