Как полностью настроить декоратор Param в NestJS для отображения свойств справа в SwaggerUI

Я работаю над небольшим проектом NestJS по созданию событий и билетов и управлению ими.

Итак, я использую UUID в параметре запроса моего контроллера, например, для получения, обновления и удаления моих объектов в моей базе данных:

@Get(':eventId')
async findOne(
  @UserId() userId: string,
  @Param('eventId') eventId: string,
): Promise<EventEntity> {
  return this.eventService.findOne(userId, eventId);
}

До этого момента проблем нет.

Затем я реализовал собственный декоратор для проверки формата UUID. Вот фрагмент кода:

export const IsUUIDParam = createParamDecorator(
  (data: string, ctx: ExecutionContext): string => {
    const request = ctx.switchToHttp().getRequest();
    const uuid: string = request.params[data];

    if (!uuid) {
      return uuid;
    }

    // This regex checks if the string is a valid UUIDv4
    const cmp: RegExpMatchArray = uuid.match(
      '^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$',
    );
    if (cmp === null) {
      throw new BadRequestException(`Invalid ${data} format`);
    }

    return uuid;
  },
);

И я использую новый декоратор в своем контроллере:

@Get(':eventId')
async findOne(
  @UserId() userId: string,
  @IsUUIDParam('eventId') eventId: string,
): Promise<EventEntity> {
  return this.eventService.findOne(userId, eventId);
}

Пользовательский декоратор работает хорошо, но сейчас Swagger не отображает необходимый параметр.

Скриншот Swagger, не отображающего необходимый параметр

Поэтому я последовал этому посту о переполнении стека, чтобы реализовать документацию в моем собственном декораторе.

Вот мой новый пользовательский декоратор:

export const IsUUIDParam = createParamDecorator(
  (data: string, ctx: ExecutionContext): string => {
    const request = ctx.switchToHttp().getRequest();
    const uuid: string = request.params[data];

    if (!uuid) {
      return uuid;
    }

    // This regex checks if the string is a valid UUIDv4
    const cmp: RegExpMatchArray = uuid.match(
      '^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$',
    );
    if (cmp === null) {
      throw new BadRequestException(`Invalid ${data} format`);
    }

    return uuid;
  },
  [
    (target, key): void => {
      // The code below define the Swagger documentation for the custom decorator
      const explicit =
        Reflect.getMetadata(DECORATORS.API_PARAMETERS, target[key]) ?? [];

      Reflect.defineMetadata(
        DECORATORS.API_PARAMETERS,
        [
          ...explicit,
          {
            in: 'path',
            name: 'uuid',
            required: true,
            type: 'string',
          },
        ],
        target[key],
      );
    },
  ],
);

Но теперь в документации Swagger отображается только uuid:

Скриншот Swagger, отображающий uuid вместо обязательного параметра

Но я хочу отображать eventId или имя параметра в общем виде (например, ticketId где-то еще в другом контроллере).

Я пытался получить что-то из свойств target и key, но ничего не нашел. Я ничего не нашел ни в Интернете, ни в

Знаете ли вы, как я могу решить свою проблему?

Пожалуйста, не загружайте изображения кода/данных/ошибок. Добавьте свое изображение непосредственно в вопрос, мы не должны размещать ссылку на изображение.
Vikrant 18.05.2024 07:37
Зод: сила проверки и преобразования данных
Зод: сила проверки и преобразования данных
Сегодня я хочу познакомить вас с библиотекой Zod и раскрыть некоторые ее особенности, например, возможности валидации и трансформации данных, а также...
Как заставить Remix работать с Mantine и Cloudflare Pages/Workers
Как заставить Remix работать с Mantine и Cloudflare Pages/Workers
Мне нравится библиотека Mantine Component , но заставить ее работать без проблем с Remix бывает непросто.
Угловой продивер
Угловой продивер
Оригинал этой статьи на турецком языке. ChatGPT используется только для перевода на английский язык.
TypeScript против JavaScript
TypeScript против JavaScript
TypeScript vs JavaScript - в чем различия и какой из них выбрать?
Синхронизация localStorage в масштабах всего приложения с помощью пользовательского реактивного хука useLocalStorage
Синхронизация localStorage в масштабах всего приложения с помощью пользовательского реактивного хука useLocalStorage
Не все нужно хранить на стороне сервера. Иногда все, что вам нужно, это постоянное хранилище на стороне клиента для хранения уникальных для клиента...
Что такое ленивая загрузка в Angular и как ее применять
Что такое ленивая загрузка в Angular и как ее применять
Ленивая загрузка - это техника, используемая в Angular для повышения производительности приложения путем загрузки модулей только тогда, когда они...
0
1
125
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Наконец я нашел ответ.

Мы можем решить эту проблему, инкапсулировав функцию createParamDecorator() в стрелочную функцию, принимающую строковый параметр (здесь параметр data).

export const IsUUIDParam = (data: string) => // new arrow function that takes a string 
  createParamDecorator(
    // the object _ allow us to declare to our IDE that the parameter won't be used, and so it doesn't display a warning message
    (_: string, ctx: ExecutionContext): string => {
      const request = ctx.switchToHttp().getRequest();
      const uuid: string = request.params[data];

      if (!uuid) {
        return uuid;
      }

      // This regex checks if the string is a valid UUIDv4
      const cmp: RegExpMatchArray = uuid.match(
        '^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$',
      );
      if (cmp === null) {
        throw new BadRequestException(`Invalid ${data} format`);
      }

      return uuid;
    },
    [
      (target, key): void => {
        // The code below define the Swagger documentation for the custom decorator
        const explicit =
          Reflect.getMetadata(DECORATORS.API_PARAMETERS, target[key]) ?? [];

        Reflect.defineMetadata(
          DECORATORS.API_PARAMETERS,
          [
            ...explicit,
            {
              in: 'path',
              // use the new data object here
              name: data,
              required: true,
              type: 'string',
            },
          ],
          target[key],
        );
      },
    ],
  // Do not forget to add the parenthesis at the end to execute the arrow function
  )();

Благодаря этому объект data (содержащий строку UUIDv4) доступен везде в стрелочной функции и, следовательно, во второй части функции createParamDecorator().

Мы можем изменить первый аргумент функции createParamDecorator() на знак подчеркивания (_), чтобы избежать предупреждающих сообщений в нашей IDE, поскольку мы больше не используем этот параметр.

Затем мы можем обновить свойство name в нашем декораторе с помощью data, чтобы отображать заданное имя (динамическим образом). Наконец, добавьте скобку в конце функции стрелки, чтобы выполнить ее (()).

В контроллере ничего не меняется, мы все равно можем вызвать наш собственный декоратор с помощью следующего кода:

@Get(':eventId')
async findOne(
  @UserId() userId: string,
  @IsUUIDParam('eventId') eventId: string,
): Promise<EventEntity> {
  return this.eventService.findOne(userId, eventId);
}

Вот снимок экрана результата Swagger для сущности события:

Скриншот результата Swagger — GET /event/:eventId

И для объекта билета:

Скриншот результата Swagger — GET /ticket/:ticketId

Примечание. Если вы хотите еще больше настроить декоратор (например, добавить собственное описание), вы можете добавить параметр после data в прототипе функции, указать второй параметр при вызове функции в контроллере и, наконец, использовать новый параметр. куда вы хотите в функции стрелки.

И вуаля! Мы можем динамически отображать строковый параметр нашего пользовательского декоратора в Swagger.

Другие вопросы по теме