@nestjs/common#Scope TypeScript Examples

The following examples show how to use @nestjs/common#Scope. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: cats.module.ts    From nestjs-mercurius with MIT License 6 votes vote down vote up
static enableRequestScope(): DynamicModule {
    return {
      module: CatsModule,
      providers: [
        {
          provide: CatsService,
          useClass: CatsRequestScopedService,
          scope: Scope.REQUEST,
        },
      ],
    };
  }
Example #2
Source File: tenancy-core.module.ts    From nestjs-tenancy with MIT License 6 votes vote down vote up
/**
     * Create tenant context provider
     *
     * @private
     * @static
     * @returns {Provider}
     * @memberof TenancyCoreModule
     */
    private static createTenantContextProvider(): Provider {
        return {
            provide: TENANT_CONTEXT,
            scope: Scope.REQUEST,
            useFactory: (
                req: Request,
                moduleOptions: TenancyModuleOptions,
                adapterHost: HttpAdapterHost,
            ) => this.getTenant(req, moduleOptions, adapterHost),
            inject: [
                REQUEST,
                TENANT_MODULE_OPTIONS,
                DEFAULT_HTTP_ADAPTER_HOST,
            ]
        }
    }
Example #3
Source File: mikro-orm.providers.ts    From nestjs with MIT License 6 votes vote down vote up
export function createEntityManagerProvider(
  scope = Scope.DEFAULT,
  entityManager: Type = EntityManager,
  contextName?: string,
): Provider<EntityManager> {
  return {
    provide: contextName ? getEntityManagerToken(contextName) : entityManager,
    scope,
    useFactory: (orm: MikroORM) => scope === Scope.DEFAULT ? orm.em : orm.em.fork(),
    inject: [contextName ? getMikroORMToken(contextName) : MikroORM],
  };
}
Example #4
Source File: logger.service.ts    From nestjs-rest-sample with GNU General Public License v3.0 6 votes vote down vote up
@Injectable({
    scope: Scope.TRANSIENT,
  })
  export class LoggerService {
    private prefix?: string;

    log(message: string) {
      let formattedMessage = message;

      if (this.prefix) {
        formattedMessage = `[${this.prefix}] ${message}`;
      }

      console.log(formattedMessage);
    }

    setPrefix(prefix: string) {
      this.prefix = prefix;
    }
  }
Example #5
Source File: userstage.service.ts    From codeclannigeria-backend with MIT License 6 votes vote down vote up
@Injectable({ scope: Scope.REQUEST })
export class UserStageService extends BaseService<UserStage> {
  constructor(
    @InjectModel(UserStage.modelName)
    protected readonly UserStageModel: ReturnModelType<typeof UserStage>,
    protected readonly userService: UsersService
  ) {
    super(UserStageModel);
  }

  async getUserStages(trackId: string): Promise<UserStage[]> {
    const currentUser = this.getUserId();
    const userStages = await this.UserStageModel.find({
      user: currentUser,
      track: trackId
    });

    return userStages;
  }
}
Example #6
Source File: users.service.ts    From codeclannigeria-backend with MIT License 6 votes vote down vote up
@Injectable({ scope: Scope.REQUEST })
export class UsersService extends BaseService<User> {
  constructor(
    @InjectModel(User.modelName)
    protected readonly userEntity: ReturnModelType<typeof User>
  ) {
    super(userEntity);
  }

  async incrementLoginAttempt(userId: string): Promise<void> {
    await this.userEntity.updateOne({ _id: userId }, { $inc: { loginAttemptCount: 1 }, updatedBy: userId as any }).exec();
  }
  async resetLoginAttempt(userId: string): Promise<void> {
    await this.userEntity.updateOne({ _id: userId }, { $set: { loginAttemptCount: 0 }, updatedBy: userId as any }).exec();
  }
}
Example #7
Source File: submissions.service.ts    From codeclannigeria-backend with MIT License 6 votes vote down vote up
@Injectable({ scope: Scope.REQUEST })
export class SubmissionsService extends BaseService<Submission> {
  constructor(
    @InjectModel(Submission.modelName)
    protected readonly EntityModel: ReturnModelType<typeof Submission>
  ) {
    super(EntityModel);
  }
}
Example #8
Source File: stages.service.ts    From codeclannigeria-backend with MIT License 6 votes vote down vote up
@Injectable({ scope: Scope.REQUEST })
export class StagesService extends BaseService<Stage> {
  constructor(
    @InjectModel(Stage.modelName)
    protected readonly EntityModel: ReturnModelType<typeof Stage>,
    @InjectModel(Track.modelName)
    protected readonly track: ReturnModelType<typeof Track>
  ) {
    super(EntityModel);
  }

  async insert(stage: Stage): Promise<DocumentType<Stage>> {
    const newStage = await super.insert(stage);
    const track = newStage.track as any;

    await this.track.findByIdAndUpdate(track.id, {
      $addToSet: { stages: newStage.id },
      updatedBy: this.getUserId()
    });

    return newStage;
  }
}
Example #9
Source File: mentor-mentee.service.ts    From codeclannigeria-backend with MIT License 6 votes vote down vote up
@Injectable({ scope: Scope.REQUEST })
export class MentorMenteeService extends BaseService<MentorMentee> {
  constructor(
    @InjectModel(MentorMentee.modelName)
    protected readonly EntityModel: ReturnModelType<typeof MentorMentee>
  ) {
    super(EntityModel);
  }
}
Example #10
Source File: courses.service.ts    From codeclannigeria-backend with MIT License 6 votes vote down vote up
@Injectable({ scope: Scope.REQUEST })
export class CoursesService extends BaseService<Course> {
  constructor(
    @InjectModel(Course.modelName)
    protected readonly EntityModel: ReturnModelType<typeof Course>
  ) {
    super(EntityModel);
  }
}
Example #11
Source File: request-scoped.guard.ts    From nestjs-mercurius with MIT License 6 votes vote down vote up
@Injectable({ scope: Scope.REQUEST })
export class Guard implements CanActivate {
  static COUNTER = 0;
  static REQUEST_SCOPED_DATA = [];

  constructor(@Inject('REQUEST_ID') private requestId: number) {
    Guard.COUNTER++;
  }

  canActivate(
    context: ExecutionContext,
  ): boolean | Promise<boolean> | Observable<boolean> {
    Guard.REQUEST_SCOPED_DATA.push(this.requestId);
    return true;
  }
}
Example #12
Source File: app.module.template.ts    From amplication with Apache License 2.0 6 votes vote down vote up
@Module({
  controllers: [],
  imports: MODULES,
  providers: [
    {
      provide: APP_INTERCEPTOR,
      scope: Scope.REQUEST,
      useClass: MorganInterceptor("combined"),
    },
  ],
})
export class AppModule {}
Example #13
Source File: service.ts    From typegraphql-nestjs with MIT License 6 votes vote down vote up
@Injectable({ scope: Scope.REQUEST })
export default class RecipeService {
  private readonly recipes: Recipe[] = [];

  getRecipes() {
    return this.recipes;
  }

  addRecipe(recipe: Recipe) {
    this.recipes.push(recipe);
  }
}
Example #14
Source File: resolvers.ts    From typegraphql-nestjs with MIT License 6 votes vote down vote up
@Injectable({ scope: Scope.REQUEST })
@Resolver()
export class RecipeResolver {
  constructor(private readonly recipeService: RecipeService) {}

  @Query(returns => [Recipe])
  recipes() {
    return this.recipeService.getRecipes();
  }

  @Mutation(returns => AddRecipeResult)
  addRecipe(@Arg("input") recipe: Recipe) {
    this.recipeService.addRecipe(recipe);
    return recipe;
  }
}
Example #15
Source File: users.service.ts    From nestjs-mercurius with MIT License 6 votes vote down vote up
@Injectable({ scope: Scope.REQUEST })
export class UsersService {
  static COUNTER = 0;
  constructor(@Inject('META') private readonly meta) {
    UsersService.COUNTER++;
  }

  findById(id: string) {
    return { id };
  }
}
Example #16
Source File: SessionManager.ts    From remix-hexagonal-architecture with MIT License 6 votes vote down vote up
@Injectable({ scope: Scope.REQUEST })
export class SessionManager {
  private storage;

  constructor(
    @Inject(SESSION_CONFIG)
    private readonly options: SessionIdStorageStrategy["cookie"],
    @Inject(REQUEST)
    private readonly request: Request
  ) {
    this.storage = createCookieSessionStorage({ cookie: options });
  }

  get() {
    return this.storage.getSession(this.request.headers.cookie);
  }

  commit(session: Session) {
    return this.storage.commitSession(session);
  }

  async destroy() {
    const session = await this.storage.getSession(this.request.headers.cookie);
    return this.storage.destroySession(session);
  }
}
Example #17
Source File: hello.module.ts    From nestjs-mercurius with MIT License 6 votes vote down vote up
@Module({
  imports: [
    GraphQLModule.forRoot({
      typePaths: [join(__dirname, '*.graphql')],
    }),
  ],
  providers: [
    HelloResolver,
    HelloService,
    UsersService,
    {
      provide: 'REQUEST_ID',
      useFactory: () => 1,
      scope: Scope.REQUEST,
    },
  ],
})
export class HelloModule {
  constructor(@Inject('META') private readonly meta) {}

  static forRoot(meta: Provider): DynamicModule {
    return {
      module: HelloModule,
      providers: [meta],
    };
  }
}
Example #18
Source File: logging.interceptor.ts    From nestjs-mercurius with MIT License 6 votes vote down vote up
@Injectable({ scope: Scope.REQUEST })
export class Interceptor implements NestInterceptor {
  static COUNTER = 0;
  static REQUEST_SCOPED_DATA = [];

  constructor(@Inject('REQUEST_ID') private requestId: number) {
    Interceptor.COUNTER++;
  }

  intercept(context: ExecutionContext, call: CallHandler): Observable<any> {
    Interceptor.REQUEST_SCOPED_DATA.push(this.requestId);
    return call.handle();
  }
}
Example #19
Source File: cats-request-scoped.service.ts    From nestjs-mercurius with MIT License 6 votes vote down vote up
@Injectable({ scope: Scope.REQUEST })
export class CatsRequestScopedService {
  static COUNTER = 0;
  private readonly cats: Cat[] = [{ id: 1, name: 'Cat', age: 5 }];

  constructor() {
    CatsRequestScopedService.COUNTER++;
  }

  create(cat: Cat): Cat {
    this.cats.push(cat);
    return cat;
  }

  findAll(): Cat[] {
    return this.cats;
  }

  findOneById(id: number): Cat {
    return this.cats.find(cat => cat.id === id);
  }
}
Example #20
Source File: tracks.service.ts    From codeclannigeria-backend with MIT License 5 votes vote down vote up
@Injectable({ scope: Scope.REQUEST })
export class TracksService extends BaseService<Track> {
  constructor(
    @InjectModel(Track.modelName)
    protected readonly EntityModel: ReturnModelType<typeof Track>,
    @InjectModel(Stage.modelName)
    protected readonly StageModel: ReturnModelType<typeof Stage>,
    @InjectModel(TrackMentor.modelName)
    protected readonly trackMentorModel: ReturnModelType<typeof TrackMentor>,
    protected readonly userService: UsersService
  ) {
    super(EntityModel);
  }

  async getMentors(trackId: string): Promise<User[]> {
    const mentors = await this.trackMentorModel
      .find({ track: trackId }, { mentor: 1 })
      .populate('mentor');
    return (mentors.map((x) => x.mentor) as unknown) as User[];
  }

  async enroll(trackId: string): Promise<User> {
    const userId = this.getUserId();
    return this.userService.updateAsync(userId, {
      $addToSet: { tracks: trackId },
      updatedBy: userId
    } as any);
  }

  async getStages(trackId: string): Promise<Stage[]> {
    return this.StageModel.find({ track: trackId }).populate('track');
  }

  // set track as not active
  async deactivateTrack(trackId: string): Promise<Track> {
    return this.updateAsync(trackId, {
      isActive: false,
      updatedBy: this.getUserId()
    });
  }
  async activateTrack(trackId: string): Promise<Track> {
    return this.updateAsync(trackId, {
      isActive: true,
      updatedBy: this.getUserId()
    });
  }
}
Example #21
Source File: logger.service.ts    From mamori-i-japan-api with BSD 2-Clause "Simplified" License 5 votes vote down vote up
// We inject a transient instance of the Logger into our feature modules
// so that each one has its own custom context.

@Injectable({ scope: Scope.TRANSIENT })
// export class AppLogger extends Logger {}
export class AppLogger implements LoggerService {
  private context?: string
  private winstonLogger: Logger

  public setContext(context: string) {
    this.context = context
  }

  constructor() {
    //
    // As of winston@3, the default logging format is JSON.
    //
    this.winstonLogger = createLogger({
      transports: [new transports.Console()],
    })
  }

  log(message: any, context?: string) {
    return this.winstonLogger.info(message, { context: context || this.context })
  }

  error(message: any, trace?: string, context?: string): any {
    return this.winstonLogger.error(message, { trace, context: context || this.context })
  }

  warn(message: any, context?: string): any {
    return this.winstonLogger.warn(message, { context: context || this.context })
  }

  debug(message: any, context?: string): any {
    return this.winstonLogger.debug(message, { context: context || this.context })
  }

  verbose(message: any, context?: string): any {
    return this.winstonLogger.verbose(message, { context: context || this.context })
  }
}
Example #22
Source File: hello.service.ts    From nestjs-mercurius with MIT License 5 votes vote down vote up
@Injectable({ scope: Scope.REQUEST })
export class HelloService {
  constructor(@Inject('META') private readonly meta) {}

  getCats(): any[] {
    return [{ id: 1, name: 'Cat', age: 5 }];
  }
}
Example #23
Source File: instance-wrapper.d.ts    From nest-jaeger with MIT License 5 votes vote down vote up
scope?: Scope;
Example #24
Source File: get-class-scope.d.ts    From nest-jaeger with MIT License 5 votes vote down vote up
export declare function getClassScope(provider: Type<unknown>): Scope;
Example #25
Source File: logger.service.ts    From nestjs-starter-rest-api with MIT License 4 votes vote down vote up
@Injectable({ scope: Scope.TRANSIENT })
export class AppLogger {
  private context?: string;
  private logger: Logger;

  public setContext(context: string): void {
    this.context = context;
  }

  constructor() {
    this.logger = createLogger({
      transports: [new transports.Console()],
    });
  }

  error(
    ctx: RequestContext,
    message: string,
    meta?: Record<string, any>,
  ): Logger {
    const timestamp = new Date().toISOString();

    return this.logger.error({
      message,
      contextName: this.context,
      ctx,
      timestamp,
      ...meta,
    });
  }

  warn(
    ctx: RequestContext,
    message: string,
    meta?: Record<string, any>,
  ): Logger {
    const timestamp = new Date().toISOString();

    return this.logger.warn({
      message,
      contextName: this.context,
      ctx,
      timestamp,
      ...meta,
    });
  }

  debug(
    ctx: RequestContext,
    message: string,
    meta?: Record<string, any>,
  ): Logger {
    const timestamp = new Date().toISOString();

    return this.logger.debug({
      message,
      contextName: this.context,
      ctx,
      timestamp,
      ...meta,
    });
  }

  verbose(
    ctx: RequestContext,
    message: string,
    meta?: Record<string, any>,
  ): Logger {
    const timestamp = new Date().toISOString();

    return this.logger.verbose({
      message,
      contextName: this.context,
      ctx,
      timestamp,
      ...meta,
    });
  }

  log(
    ctx: RequestContext,
    message: string,
    meta?: Record<string, any>,
  ): Logger {
    const timestamp = new Date().toISOString();

    return this.logger.info({
      message,
      contextName: this.context,
      ctx,
      timestamp,
      ...meta,
    });
  }
}
Example #26
Source File: joi.pipe.ts    From nestjs-joi with MIT License 4 votes vote down vote up
@Injectable({ scope: Scope.REQUEST })
export class JoiPipe implements PipeTransform {
  private readonly schema?: Joi.Schema;
  private readonly type?: Constructor;
  private readonly method?: string;
  private readonly pipeOpts: ValidatedJoiPipeOptions;

  constructor();
  constructor(pipeOpts?: JoiPipeOptions);
  constructor(type: Constructor, pipeOpts?: JoiPipeOptions);
  constructor(schema: Joi.Schema, pipeOpts?: JoiPipeOptions);
  constructor(
    @Inject(REQUEST) private readonly arg?: unknown,
    @Optional() @Inject(JOIPIPE_OPTIONS) pipeOpts?: JoiPipeOptions,
  ) {
    if (arg) {
      // Test for an actual request object, which indicates we're in "injected" mode.
      // This is the case that requires the most performant handling, which is why it
      // should be the first branch.
      if (isHttpRequest(arg)) {
        this.method = arg.method.toUpperCase();
      } else if (isGraphQlRequest(arg)) {
        // @nestjs/graphql, or rather apollo, only supports GET and POST.
        // To provide a consistent experience without hard to understand behavior
        // such as UPDATE group schemas being ignored, we will NOT set the method.
        // JoiPipe will work for this case, but ignore the method.
        // this.method = arg.req.method.toUpperCase();
      } else {
        // This is the "manually called constructor" case, where performance is
        // (ostensibly) not as big of a concern since manual JoiPipes will be
        // constructed only once at app initialization
        if (Joi.isSchema(arg)) {
          this.schema = arg as Joi.Schema;
        } else if (typeof arg === 'function') {
          this.type = arg as Constructor;
        } else {
          // Options passed as first parameter
          pipeOpts = arg as JoiPipeOptions;
        }
      }
    } else {
      // Called without arguments, do nothing
    }

    this.pipeOpts = this.parseOptions(pipeOpts);
  }

  transform(payload: unknown, metadata: ArgumentMetadata): unknown {
    const schema = this.getSchema(metadata);

    if (!schema) {
      // This happens when a metatype was passed by NestJS and it has no
      // validation decoration.
      return payload;
    }

    return JoiPipe.validate(
      payload,
      schema,
      this.pipeOpts.usePipeValidationException,
      this.pipeOpts.skipErrorFormatting,
      // It is technically impossible for this to be undefined since it is explicitely assigned
      // with a default value in parseOptions(), so it is almost impossible to test.
      /* istanbul ignore next */
      this.pipeOpts.defaultValidationOptions || DEFAULT_JOI_OPTS,
      metadata,
    );
  }

  // Called "validate" and NOT "transform", because that would make it match
  // the interface of a pipe INSTANCE and prevent NestJS from recognizing it as
  // a class constructor instead of an instance.
  private static validate<T>(
    payload: unknown,
    schema: Joi.Schema,
    usePipeValidationException: boolean,
    skipErrorFormatting: boolean,
    validationOptions: Joi.ValidationOptions,
    /* istanbul ignore next */
    metadata: ArgumentMetadata = { type: 'custom' },
  ): T {
    const { error, value } = schema.validate(
      payload,
      // This will always get overridden by whatever options have been specified
      // on the schema itself
      validationOptions,
    );

    if (error) {
      // Fixes #4
      if (Joi.isError(error)) {
        // Provide a special response with reasons
        const reasons = error.details
          .map((detail: { message: string }) => detail.message)
          .join(', ');
        const formattedMessage =
          `Request validation of ${metadata.type} ` +
          (metadata.data ? `item '${metadata.data}' ` : '') +
          `failed, because: ${reasons}`;
        if (usePipeValidationException) {
          throw new JoiPipeValidationException(
            skipErrorFormatting ? error.message : formattedMessage,
          );
        } else {
          throw new BadRequestException(skipErrorFormatting ? error : formattedMessage);
        }
      } else {
        // If error is not a validation error, it is probably a custom error thrown by the schema.
        // Pass it through to allow it to be caught by custom error handlers.
        throw error;
      }
    }

    // Everything is fine
    return value as T;
  }

  /**
   * Efficient validation of pipeOpts
   *
   * @param pipeOpts Pipe options as passed in the constructor
   * @returns Validated options with default values applied
   */
  private parseOptions(pipeOpts?: JoiPipeOptions): ValidatedJoiPipeOptions {
    // Pass type arguments to force type validation of the function arguments.
    pipeOpts = Object.assign<JoiPipeOptions, JoiPipeOptions>(
      {
        ...DEFAULT_JOI_PIPE_OPTS,
      },
      pipeOpts || {},
    );
    // Nested merge of the Joi ValidationOptions.
    // TODO This could probably be combined with the above assignment in a deep merge.
    pipeOpts.defaultValidationOptions = Object.assign<Joi.ValidationOptions, Joi.ValidationOptions>(
      {
        ...DEFAULT_JOI_OPTS,
      },
      pipeOpts.defaultValidationOptions || {},
    );

    const errors: string[] = [];

    const unknownKeys = Object.keys(pipeOpts).filter(k => !JOI_PIPE_OPTS_KEYS.includes(k));
    if (unknownKeys.length) {
      errors.push(`Unknown configuration keys: ${unknownKeys.join(', ')}`);
    }
    if (
      pipeOpts.group &&
      !(typeof pipeOpts.group === 'string' || typeof pipeOpts.group === 'symbol')
    ) {
      errors.push(`'group' must be a string or symbol`);
    }
    if (
      Object.prototype.hasOwnProperty.call(pipeOpts, 'usePipeValidationException') &&
      !(typeof pipeOpts.usePipeValidationException === 'boolean')
    ) {
      errors.push(`'usePipeValidationException' must be a boolean`);
    }
    if (
      Object.prototype.hasOwnProperty.call(pipeOpts, 'skipErrorFormatting') &&
      !(typeof pipeOpts.skipErrorFormatting === 'boolean')
    ) {
      errors.push(`'skipErrorFormatting' must be a boolean`);
    }

    if (errors.length) {
      throw new Error(`Invalid JoiPipeOptions:\n${errors.map(x => `- ${x}`).join('\n')}`);
    }

    return pipeOpts as ValidatedJoiPipeOptions;
  }

  /**
   * Determine what schema to return/construct based on the actual metadata
   * passed for this request.
   *
   * @param metadata
   */
  private getSchema(metadata: ArgumentMetadata): Joi.Schema | undefined {
    // If called in request scope mode, give preference to this mode.
    if (this.method && metadata.metatype) {
      let group: JoiValidationGroup | undefined;
      if (this.method === 'PUT' || this.method === 'PATCH') {
        group = JoiValidationGroups.UPDATE;
      } else if (this.method === 'POST') {
        group = JoiValidationGroups.CREATE;
      }

      return JoiPipe.getTypeSchema(metadata.metatype, { group });
    }

    // Prefer a static schema, if specified
    if (this.schema) {
      return this.schema;
    }

    // Prefer a static model, if specified
    if (this.type) {
      // Don't return "no schema" (undefined) if a type was explicitely specified
      return JoiPipe.getTypeSchema(this.type, { forced: true, group: this.pipeOpts.group });
    }

    // Determine the schema from the passed model
    if (metadata.metatype) {
      return JoiPipe.getTypeSchema(metadata.metatype, { group: this.pipeOpts.group });
    }

    return undefined;
  }

  /**
   * Cache map for already constructed schemas
   */
  private static readonly typeSchemaMap = new Map<unknown, Map<string, Joi.Schema | undefined>>();

  /**
   * Obtain the type schema from getTypeSchema().
   * The result is cached for better performance, using the type and options to
   * construct a cache key.
   *
   * If no properties have been decorated in the whole chain, no schema
   * will be returned so as to not throw when inadvertently "validating" using
   * inbuilt types.
   * Returning a schema can be forced to cover cases when a type has been explicitely
   * passed to JoiPipe.
   *
   * @param type The type (decorated class constructor) to construct a schema from
   * @param options An optional options object
   * @param options.forced Force an empty schema to be returned even if no properties of the type have been decorated. (defaults to false)
   * @param options.group An optional JoiValidationGroup to use during schema construction
   */
  private static getTypeSchema(
    type: Constructor,
    /* istanbul ignore next */
    { forced, group }: { forced?: boolean; group?: JoiValidationGroup } = {},
  ): Joi.Schema | undefined {
    // Do not validate basic inbuilt types. This is fast enough that we don't need
    // to cache the result.
    if (type === String || type === Object || type === Number || type === Array) {
      return;
    }

    const cacheKey = 'forced' + (forced ? '1' : '0') + (group ? 'group' + String(group) : '');
    // Check cache.
    if (this.typeSchemaMap.has(type)) {
      // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
      if (this.typeSchemaMap.get(type)!.has(cacheKey)) {
        // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
        return this.typeSchemaMap.get(type)!.get(cacheKey);
      }
    } else {
      this.typeSchemaMap.set(type, new Map());
    }

    const typeSchema = getTypeSchema(type, { group });

    // The default behavior when no properties have attached schemas is to not
    // validate, otherwise we'd get errors for types with no decorators not intended
    // to be validated when used as global pipe
    if ((!typeSchema || !Object.keys(typeSchema.describe().keys).length) && !forced) {
      // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
      this.typeSchemaMap.get(type)!.set(cacheKey, undefined);
      return undefined;
    }

    // It is assumed that a validation schema was specified because the input value
    // as such is actually required, so we're calling required() here.
    // This might be subject to change if actual use cases are found.
    const finalSchema = typeSchema.required();

    // Cache value
    // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
    this.typeSchemaMap.get(type)!.set(cacheKey, finalSchema);

    return finalSchema;
  }
}
Example #27
Source File: mikro-orm.module.test.ts    From nestjs with MIT License 4 votes vote down vote up
describe('MikroORM Module', () => {

  beforeEach(() => {
    // Clear context names before each run, so we do not throw existing exception
    CONTEXT_NAMES.splice(0, CONTEXT_NAMES.length);
  });

  describe('Single Database', () => {
    it('forRoot', async () => {
      const module = await Test.createTestingModule({
        imports: [MikroOrmModule.forRoot(testOptions)],
      }).compile();

      const orm = module.get<MikroORM>(MikroORM);
      expect(orm).toBeDefined();
      expect(orm.config.get('contextName')).toBe('default');
      expect(module.get<EntityManager>(EntityManager)).toBeDefined();
      await orm.close();
    });

    it('forRootAsync :useClass', async () => {
      const module = await Test.createTestingModule({
        imports: [ConfigModule, MikroOrmModule.forRootAsync({
          useClass: ConfigService,
          providers: [myLoggerProvider],
        })],
      }).compile();

      const orm = module.get<MikroORM>(MikroORM);
      expect(orm).toBeDefined();
      expect(orm.config.get('contextName')).toBe('default');
      expect(module.get<EntityManager>(EntityManager)).toBeDefined();
      await orm.close();
    });

    it('forRootAsync :useExisting', async () => {
      const module = await Test.createTestingModule({
        imports: [ConfigModule, MikroOrmModule.forRootAsync({
          useExisting: ConfigService,
          imports: [ConfigModule],
        })],
      }).compile();

      const orm = module.get<MikroORM>(MikroORM);
      expect(orm).toBeDefined();
      expect(orm.config.get('contextName')).toBe('default');
      expect(module.get<EntityManager>(EntityManager)).toBeDefined();
      await orm.close();
    });

    it('forRootAsync :useFactory', async () => {
      const module = await Test.createTestingModule({
        imports: [MikroOrmModule.forRootAsync({
          useFactory: (logger: Logger) => ({
            ...testOptions,
            logger: logger.log.bind(logger),
          }),
          inject: ['my-logger'],
          providers: [myLoggerProvider],
        })],
      }).compile();

      const orm = module.get<MikroORM>(MikroORM);
      expect(orm).toBeDefined();
      expect(orm.config.get('contextName')).toBe('default');
      expect(module.get<EntityManager>(EntityManager)).toBeDefined();
      await orm.close();
    });

    it('forRoot should return a new em each request with request scope', async () => {
      const module = await Test.createTestingModule({
        imports: [MikroOrmModule.forRoot({
          ...testOptions,
          scope: Scope.REQUEST,
        })],
      }).compile();

      const idSet = await getEntityManagerLoop(module);

      expect(idSet.size).toBe(5);

      await module.get<MikroORM>(MikroORM).close();
    });

    it('forRootAsync should return a new em each request with request scope', async () => {
      const module = await Test.createTestingModule({
        imports: [MikroOrmModule.forRootAsync({
          useFactory: (logger: Logger) => ({
            ...testOptions,
            logger: logger.log.bind(logger),
          }),
          inject: ['my-logger'],
          providers: [myLoggerProvider],
          scope: Scope.REQUEST,
        })],
      }).compile();

      const idSet = await getEntityManagerLoop(module);

      expect(idSet.size).toBe(5);

      await module.get<MikroORM>(MikroORM).close();
    });

    it('forRoot should return the same em each request with default scope', async () => {
      const module = await Test.createTestingModule({
        imports: [MikroOrmModule.forRoot({
          ...testOptions,
        })],
      }).compile();

      const idSet = await getEntityManagerLoop(module);

      expect(idSet.size).toBe(1);

      await module.get<MikroORM>(MikroORM).close();
    });

    it('forRootAsync should return the same em each request with default scope', async () => {
      const module = await Test.createTestingModule({
        imports: [MikroOrmModule.forRootAsync({
          useFactory: (logger: Logger) => ({
            ...testOptions,
            logger: logger.log.bind(logger),
          }),
          inject: ['my-logger'],
          providers: [myLoggerProvider],
        })],
      }).compile();

      const idSet = await getEntityManagerLoop(module);

      expect(idSet.size).toBe(1);

      await module.get<MikroORM>(MikroORM).close();
    });

    it('forFeature should return repository', async () => {
      const module = await Test.createTestingModule({
        imports: [
          MikroOrmModule.forRoot(testOptions),
          MikroOrmModule.forFeature([Foo]),
        ],
      }).compile();

      const orm = module.get<MikroORM>(MikroORM);
      const entityManager = module.get<EntityManager>(EntityManager);
      const repository = module.get<EntityRepository<Foo>>(getRepositoryToken(Foo));

      expect(orm).toBeDefined();
      expect(entityManager).toBeDefined();
      expect(repository).toBeDefined();

      await orm.close();
    });

    it('forFeature and autoLoadEntities enabled should return repository', async () => {
      const { entities, ...options } = testOptions;

      const module = await Test.createTestingModule({
        imports: [
          MikroOrmModule.forRoot({
            autoLoadEntities: true,
            ...options,
          }),
          MikroOrmModule.forFeature([Foo]),
        ],
      }).compile();

      const orm = module.get<MikroORM>(MikroORM);
      const entityManager = module.get<EntityManager>(EntityManager);
      const repository = module.get<EntityRepository<Foo>>(getRepositoryToken(Foo));

      expect(orm).toBeDefined();
      expect(entityManager).toBeDefined();
      expect(repository).toBeDefined();

      await orm.close();
    });
  });

  describe('Multiple Databases', () => {

    it('forRoot', async () => {
      const module = await Test.createTestingModule({
        imports: [
          MikroOrmModule.forRoot({
            contextName: 'database1',
            ...testOptions,
          }),
          MikroOrmModule.forRoot({
            contextName: 'database2',
            ...testOptions,
          }),
        ],
      }).compile();

      await checkProviders(module);
    });

    it('forRootAsync :useClass', async () => {
      const module = await Test.createTestingModule({
        imports: [
          ConfigModule,
          MikroOrmModule.forRootAsync({
            contextName: 'database1',
            useClass: ConfigService,
            providers: [myLoggerProvider],
          }),
          MikroOrmModule.forRootAsync({
            contextName: 'database2',
            useClass: ConfigService,
            providers: [myLoggerProvider],
          }),
        ],
      }).compile();

      await checkProviders(module);
    });

    it('forRootAsync :useExisting', async () => {
      const module = await Test.createTestingModule({
        imports: [
          ConfigModule,
          MikroOrmModule.forRootAsync({
            contextName: 'database1',
            useExisting: ConfigService,
            imports: [ConfigModule],
          }),
          MikroOrmModule.forRootAsync({
            contextName: 'database2',
            useExisting: ConfigService,
            imports: [ConfigModule],
          })],
      }).compile();

      await checkProviders(module);
    });

    it('forRootAsync :useFactory', async () => {
      const module = await Test.createTestingModule({
        imports: [
          MikroOrmModule.forRootAsync({
            contextName: 'database1',
            useFactory: (logger: Logger) => ({
              ...testOptions,
              logger: logger.log.bind(logger),
            }),
            inject: ['my-logger'],
            providers: [myLoggerProvider],
          }),
          MikroOrmModule.forRootAsync({
            contextName: 'database2',
            useFactory: (logger: Logger) => ({
              ...testOptions,
              logger: logger.log.bind(logger),
            }),
            inject: ['my-logger'],
            providers: [myLoggerProvider],
          }),
        ],
      }).compile();

      await checkProviders(module);
    });

    it('forFeature should return repositories', async () => {
      const module = await Test.createTestingModule({
        imports: [
          MikroOrmModule.forRoot({
            contextName: 'database1',
            ...testOptions,
          }),
          MikroOrmModule.forRoot({
            contextName: 'database2',
            ...testOptions,
          }),
          MikroOrmModule.forFeature([Foo], 'database1'),
          MikroOrmModule.forFeature({ entities: [Bar], contextName: 'database2' }),
        ],
      }).compile();

      const orm1 = module.get<MikroORM>(getMikroORMToken('database1'));
      const orm2 = module.get<MikroORM>(getMikroORMToken('database2'));
      const repository1 = module.get<EntityRepository<Foo>>(getRepositoryToken(Foo, 'database1'));
      const repository2 = module.get<EntityRepository<Bar>>(getRepositoryToken(Bar, 'database2'));

      expect(orm1).toBeDefined();
      expect(repository1).toBeDefined();
      expect(orm2).toBeDefined();
      expect(repository2).toBeDefined();

      await orm1.close();
      await orm2.close();
    });

    it('forFeature and autoLoadEntities enabled should return repository', async () => {
      const { entities, ...options } = testOptions;

      const module = await Test.createTestingModule({
        imports: [
          MikroOrmModule.forRoot({
            contextName: 'database1',
            autoLoadEntities: true,
            ...options,
          }),
          MikroOrmModule.forRoot({
            contextName: 'database2',
            autoLoadEntities: true,
            ...options,
          }),
          MikroOrmModule.forFeature([Foo], 'database1'),
          MikroOrmModule.forFeature({ entities: [Bar] }, 'database2'),
        ],
      }).compile();

      const orm1 = module.get<MikroORM>(getMikroORMToken('database1'));
      const orm2 = module.get<MikroORM>(getMikroORMToken('database2'));
      const repository1 = module.get<EntityRepository<Foo>>(getRepositoryToken(Foo, 'database1'));
      const repository2 = module.get<EntityRepository<Bar>>(getRepositoryToken(Bar, 'database2'));

      expect(() => module.get<EntityRepository<Foo>>(getRepositoryToken(Foo, 'database2'))).toThrow();
      expect(() => module.get<EntityRepository<Bar>>(getRepositoryToken(Bar, 'database1'))).toThrow();
      expect(orm1).toBeDefined();
      expect(repository1).toBeDefined();
      expect(orm2).toBeDefined();
      expect(repository2).toBeDefined();

      await orm1.close();
      await orm2.close();
    });
  });
});
Example #28
Source File: post.service.ts    From nestjs-rest-sample with GNU General Public License v3.0 4 votes vote down vote up
@Injectable({ scope: Scope.REQUEST })
export class PostService {
  constructor(
    @Inject(POST_MODEL) private postModel: Model<Post>,
    @Inject(COMMENT_MODEL) private commentModel: Model<Comment>,
    @Inject(REQUEST) private req: AuthenticatedRequest,
  ) { }

  findAll(keyword?: string, skip = 0, limit = 10): Observable<Post[]> {
    if (keyword) {
      return from(
        this.postModel
          .find({ title: { $regex: '.*' + keyword + '.*' } })
          .skip(skip)
          .limit(limit)
          .exec(),
      );
    } else {
      return from(this.postModel.find({}).skip(skip).limit(limit).exec());
    }
  }

  findById(id: string): Observable<Post> {
    return from(this.postModel.findOne({ _id: id }).exec()).pipe(
      mergeMap((p) => (p ? of(p) : EMPTY)),
      throwIfEmpty(() => new NotFoundException(`post:$id was not found`)),
    );
  }

  save(data: CreatePostDto): Observable<Post> {
    //console.log('req.user:'+JSON.stringify(this.req.user));
    const createPost: Promise<Post> = this.postModel.create({
      ...data,
      createdBy: { _id: this.req.user.id },
    });
    return from(createPost);
  }

  update(id: string, data: UpdatePostDto): Observable<Post> {
    return from(
      this.postModel
        .findOneAndUpdate(
          { _id: id },
          { ...data, updatedBy: { _id: this.req.user.id } },
          { new: true },
        )
        .exec(),
    ).pipe(
      mergeMap((p) => (p ? of(p) : EMPTY)),
      throwIfEmpty(() => new NotFoundException(`post:$id was not found`)),
    );
    // const filter = { _id: id };
    // const update = { ...data, updatedBy: { _id: this.req.user.id } };
    // return from(this.postModel.findOne(filter).exec()).pipe(
    //   mergeMap((post) => (post ? of(post) : EMPTY)),
    //   throwIfEmpty(() => new NotFoundException(`post:$id was not found`)),
    //   switchMap((p, i) => {
    //     return from(this.postModel.updateOne(filter, update).exec());
    //   }),
    //   map((res) => res.nModified),
    // );
  }

  deleteById(id: string): Observable<Post> {
    return from(this.postModel.findOneAndDelete({ _id: id }).exec()).pipe(
      mergeMap((p) => (p ? of(p) : EMPTY)),
      throwIfEmpty(() => new NotFoundException(`post:$id was not found`)),
    );
    // const filter = { _id: id };
    // return from(this.postModel.findOne(filter).exec()).pipe(
    //   mergeMap((post) => (post ? of(post) : EMPTY)),
    //   throwIfEmpty(() => new NotFoundException(`post:$id was not found`)),
    //   switchMap((p, i) => {
    //     return from(this.postModel.deleteOne(filter).exec());
    //   }),
    //   map((res) => res.deletedCount),
    // );
  }

  deleteAll(): Observable<any> {
    return from(this.postModel.deleteMany({}).exec());
  }

  //  actions for comments
  createCommentFor(id: string, data: CreateCommentDto): Observable<Comment> {
    const createdComment: Promise<Comment> = this.commentModel.create({
      post: { _id: id },
      ...data,
      createdBy: { _id: this.req.user.id },
    });
    return from(createdComment);
  }

  commentsOf(id: string): Observable<Comment[]> {
    const comments = this.commentModel
      .find({
        post: { _id: id },
      })
      .select('-post')
      .exec();
    return from(comments);
  }
}
Example #29
Source File: post.controller.ts    From nestjs-rest-sample with GNU General Public License v3.0 4 votes vote down vote up
@Controller({ path: 'posts', scope: Scope.REQUEST })
export class PostController {
  constructor(private postService: PostService) { }

  @Get('')
  getAllPosts(
    @Query('q') keyword?: string,
    @Query('limit', new DefaultValuePipe(10), ParseIntPipe) limit?: number,
    @Query('skip', new DefaultValuePipe(0), ParseIntPipe) skip?: number,
  ): Observable<BlogPost[]> {
    return this.postService.findAll(keyword, skip, limit);
  }

  @Get(':id')
  getPostById(@Param('id', ParseObjectIdPipe) id: string): Observable<BlogPost> {
    return this.postService.findById(id);
  }

  @Post('')
  @UseGuards(JwtAuthGuard, RolesGuard)
  @HasRoles(RoleType.USER, RoleType.ADMIN)
  createPost(
    @Body() post: CreatePostDto,
    @Res() res: Response,
  ): Observable<Response> {
    return this.postService.save(post).pipe(
      map((post) => {
        return res
          .location('/posts/' + post._id)
          .status(201)
          .send();
      }),
    );
  }

  @Put(':id')
  @UseGuards(JwtAuthGuard, RolesGuard)
  @HasRoles(RoleType.USER, RoleType.ADMIN)
  updatePost(
    @Param('id', ParseObjectIdPipe) id: string,
    @Body() post: UpdatePostDto,
    @Res() res: Response,
  ): Observable<Response> {
    return this.postService.update(id, post).pipe(
      map((post) => {
        return res.status(204).send();
      }),
    );
  }

  @Delete(':id')
  @UseGuards(JwtAuthGuard, RolesGuard)
  @HasRoles(RoleType.ADMIN)
  deletePostById(
    @Param('id', ParseObjectIdPipe) id: string,
    @Res() res: Response,
  ): Observable<Response> {
    return this.postService.deleteById(id).pipe(
      map((post) => {
        return res.status(204).send();
      }),
    );
  }

  @Post(':id/comments')
  @UseGuards(JwtAuthGuard, RolesGuard)
  @HasRoles(RoleType.USER)
  createCommentForPost(
    @Param('id', ParseObjectIdPipe) id: string,
    @Body() data: CreateCommentDto,
    @Res() res: Response,
  ): Observable<Response> {
    return this.postService.createCommentFor(id, data).pipe(
      map((comment) => {
        return res
          .location('/posts/' + id + '/comments/' + comment._id)
          .status(201)
          .send();
      }),
    );
  }

  @Get(':id/comments')
  getAllCommentsOfPost(@Param('id', ParseObjectIdPipe) id: string): Observable<Comment[]> {
    return this.postService.commentsOf(id);
  }
}