有没有办法在动态模块中动态创建提供者?

时间:2019-12-26 04:01:22

标签: typescript nestjs

原因是这样的,我想在Nestjs中创建装饰器驱动的http service,如下所示:

@RemoteService()
export class UserRemoteService {

  @Get('user/:id')
  public get(@Param() id: number): Observable<UserEntity> { return null };

  @Get('user/username/:username')
  public getByUsername(@Param() username: string): Observable<UserEntity> { return null }

  @Post('user')
  public add(@Body() user: UserEntity): Observable<UserEntity> { return null };

}

我的第一个版本代码如下


export class RemoteServiceModuleOptions {
  baseURL: string;
  headers?: { [key: string]: any };
  imports?: Array<Type<any>>;
  services?: Array<Type<any>>;
}

@Module({ })
export class RemoteServiceModule {

  public static forRoot(options: RemoteServiceModuleOptions): DynamicModule {
    options.imports || (options.imports = []);
    options.headers || (options.headers = {});
    options.services || (options.services = []);
    return {
      module: RemoteServiceModule,
      imports: [
        HttpModule.register({
          baseURL: options.baseURL,
        }),
        ...options.imports,
      ],
      providers: [
        ...options.services.map(Service => RemoteServiceModule.createRemoteServiceProvider(Service)),
      ],
      exports: [
        ...options.services,
      ],
    };
  }

  private static createRemoteServiceProvider(Service: Type<any>) {
    const mappings: Map<string, any> = Reflect.getMetadata('mappings', Service);
    const paramtypes: Array<any> = Reflect.getMetadata('design:paramtypes', Service) || [];
    class __Service extends Service {}
    return {
      inject: [HttpService, ...paramtypes],
      provide: Service,
      useFactory: (http: HttpService, ...injects: Array<any>) => {
        Array.from(mappings.entries()).forEach(([propertyKey, config]) => {
          __Service.prototype[propertyKey] = function() {
            const url = RemoteServiceModule.parseURL(config.url, config.params, arguments);
            const data = arguments[config.body];
            const method = config.method;
            return http.request({ url, data, method });
          };
        });
        return new __Service(...injects);
      },
    };
  }

  private static parseURL(url: string, params: Array<{ value: any, index: number }>, args: IArguments) {
    let index = 0;
    return (<string>url).replace(/:(\w+)/g, (_, param) => {
      const result = (params || []).find(({ value }) => value === param);
      if(result) {
        return index++, args[result.index];
      } else {
        return args[index++];
      }
    });
  }

}

到目前为止,一切正常,现在我想动态配置baseURL,因此我需要另一个名为RemoteServiceModule的静态方法forRootAsync,这样的问题是,我不能动态创建提供者,因为RemoteServiceModuleOptions是异步的。

export interface RemoteServiceModuleAsyncOptions extends Pick<ModuleMetadata, 'imports'> {
  inject?: Array<any>;
  useFactory?: (...args: Array<any>) => Promise<RemoteServiceModuleOptions> | RemoteServiceModuleOptions;
}

public static forRootAsync(options: RemoteServiceModuleAsyncOptions): DynamicModule {
  options.inject || (options.inject = []);
  options.imports || (options.imports = []);
  return {
    module: RemoteServiceModule,
    imports: [...options.imports],
    providers: [
      {
        inject: [...options.inject],
        provide: RemoteServiceModuleOptions,
        useFactory: options.useFactory,
      },
      // can not use RemoteServiceModuleOptions here
    ],
  };
}

有什么解决办法,谢谢!

0 个答案:

没有答案