Как интегрировать внедрение зависимостей с пользовательскими декораторами - PullRequest
0 голосов
/ 21 января 2019

Я пытаюсь создать декоратор, который требует внедрения зависимости. Например:

@Injectable()
class UserService{
  @TimeoutAndCache(1000)
  async getUser(id:string):Promise<User>{
     // Make a call to db to get all Users
  }
}

@TimeoutAndCache возвращает новое обещание, которое выполняет следующее:

  1. если вызов длится дольше 1000 мс, возвращает отклонение, а после завершения вызова он сохраняется в Redis (чтобы его можно было извлечь в следующий раз).
  2. Если вызов занимает менее 1000 мс, просто возвращает результат
export const TimeoutAndCache = function timeoutCache(ts: number, namespace) {
  return function log(
    target: object,
    propertyKey: string,
    descriptor: TypedPropertyDescriptor<any>,
  ) {
    const originalMethod = descriptor.value; // save a reference to the original method
    descriptor.value = function(...args: any[]) {
      // pre
      let timedOut = false;
      // run and store result
      const result: Promise<object> = originalMethod.apply(this, args);
      const task = new Promise((resolve, reject) => {
        const timer = setTimeout(() => {
          if (!timedOut) {
            timedOut = true;
            console.log('timed out before finishing');
            reject('timedout');
          }
        }, ts);
        result.then(res => {
          if (timedOut) {
            // store in cache
            console.log('store in cache');
          } else {
            clearTimeout(timer);
            // return the result
            resolve(res);
          }
        });
      });
      return task;
    };
    return descriptor;
  };
};

Мне нужно добавить RedisService, чтобы сохранить полученный результат. Один из способов, которым я мог бы внедрить Redis Service в UserService, но кажется довольно уродливым.

1 Ответ

0 голосов
/ 26 января 2019

Вам следует рассмотреть возможность использования Interceptor вместо пользовательского декоратора, поскольку они запускаются ранее в конвейере Nest и поддерживают внедрение зависимостей по умолчанию.

Однако, поскольку вы хотите как передать значения (для тайм-аута кэша), так и разрешить зависимости, вам придется использовать шаблон mixin.

import {
  ExecutionContext,
  Injectable,
  mixin,
  NestInterceptor,
} from '@nestjs/common';
import { Observable } from 'rxjs';
import { TestService } from './test/test.service';

@Injectable()
export abstract class CacheInterceptor implements NestInterceptor {
  protected abstract readonly cacheDuration: number;

  constructor(private readonly testService: TestService) {}

  intercept(
    context: ExecutionContext,
    call$: Observable<any>,
  ): Observable<any> {
    // Whatever your logic needs to be

    return call$;
  }
}

export const makeCacheInterceptor = (cacheDuration: number) =>
  mixin(
    // tslint:disable-next-line:max-classes-per-file
    class extends CacheInterceptor {
      protected readonly cacheDuration = cacheDuration;
    },
  );

Тогда вы будетев состоянии применить Перехватчик к вашему обработчику аналогичным образом:

@Injectable()
class UserService{
  @UseInterceptors(makeCacheInterceptor(1000))
  async getUser(id:string):Promise<User>{
     // Make a call to db to get all Users
  }
}
...