atls / nestjs

NestJS Custom Workshop
BSD 3-Clause "New" or "Revised" License
33 stars 8 forks source link

[feature] map objects nested into array validation errors by MapErrorsInterceptor #179

Closed unmyke closed 3 years ago

unmyke commented 3 years ago

С чем связан запрос на фичу?

Необходимо изменить отображение объекта ошибки, возращенного методом MapErrorsInterceptor#intercept в случаях перехвата ошибок валидации, вызванных декоратором поля @ValidateNetsed({ each: true }).

Расскажите как вы это себе видите

MapToErrorsInterceptor отвечает за отображение ошибок, возникающих при запуске хендлеров контроллера, в частности, ошибок валидации запросов клиента. Для этого MapToErrorsInterceptor перехватывает ошибки класса ValidationError из пакета class-validator и отображает в ожидаемый клиентом формат.

Поле запроса клиента может является массивом объектов, каждый из которых необходимо свалириовать. Текущая реализация метода intercept в этом случае вернет объект, который вызывает ошибки сериализации.

Для больших подробностей расмотрим следующий пример:

import { IsString, validateOrReject } from 'class-vaidator'
import { from }                       from 'rxjs'

class NestedBar {
  @IsString()
  bar: any
}

class Foo {
  @ValidateNested({ each: true })
  nestedBars: Array<NestedBar>
}

const nestedBar1 = new NestedBar()
nestedBar1.bar = 1
const nestedBar2 = new NestedBar()
nestedBar2.bar = 'string'
const nestedBars = [nestedBar1, nestedBar2]
const foo = new Foo()
foo.nestedBars = nestedBars

const interceptor = new MapToErrorsInterceptor()
const handler = {
  handle = () => from(validateOrReject(foo))
}
// print ValidatioError to be intercepted by MapToErrorsInterceptor
interceptor.intercept({}, handler).pipe(console.log)

/*
  [
    {
      property: "nestedBar",
      target: {
        nestedBar: [
          { bar: 1 },
          { bar: "str" },
          { bar: null }
        ]
      },
      value: [
        { bar: 1 },
        { bar: "str" },
        { bar: null }
      ],
      children: [
        {
          property: "0",
          target: [
            { bar: 1 },
            { bar: "str" },
            { bar: null }
          ],
          value: { bar: 1  }
          children: [
            {
              property: "bar",
              target: { bar: 1 },
              value: 1
              children: [],
              constraints: {
                isString: "each value in bar must be a string"
              },
            }
          ],
        },
        {
          property: "2",
          target: [
            { bar: 1 },
            { bar: "str" },
            { bar: null }
          ],
          value: { bar: null },
          children: [
            {
              property: "bar",
              target: { bar: null },
              value: null,
              children: [],
              constraints: {
                isString: "each value in bar must be a string"
              },
            }
          ],
        }
      ],
    }
  ]
*/

Поля выходного объекта require('class-validator').validate, которые содержат ошибки валидации для невалидых объектов входного массива, будут отображены в объекты с динамическим ключом - индексом проверяемого элемента в массиве. Такая форма несериализуема для передачи протоколами со статической типизацией полей. Необходимо добавить поддержку отображения ValidationError для таких случаев.

Пример выходного объекта для отправки на клиент:

{
  errors: {
    nestedBar: [
      { bar: 'each value in bar must be a string' },
      {},
      { bar: 'each value in bar must be a string' }
    ]
  }
}

Пример текущей реализации выходного объекта для отправки на клиент:

{
  errors: {
    nestedBar: {
      '0': { bar: 'each value in bar must be a string' },
      '2': { bar: 'each value in bar must be a string' }
    }
  }
}