Skip to content

[Feature] chaotic: выводить все обнаруженные ошибки валидации конфига за один запуск #1326

Description

@halmat123

Add a description

Описание

При разборе JSON-конфига с помощью парсера, сгенерированного chaotic, проверка прекращается после первой ошибки валидации и выбрасывается исключение.

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

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

Как воспроизвести

Commit userver, на котором воспроизводится проблема:

c9f77729c0edce7e423def2d4a4450aa7fc9d259

JSON-схема:

{
  "components": {
    "schemas": {
      "ServiceConfig": {
        "type": "object",
        "additionalProperties": false,
        "required": ["port", "workers", "mode"],
        "properties": {
          "port": {
            "type": "integer",
            "minimum": 1,
            "maximum": 65535
          },
          "workers": {
            "type": "integer",
            "minimum": 1
          },
          "mode": {
            "type": "string",
            "enum": ["development", "production"]
          }
        }
      }
    }
  }
}

JSON-конфиг с четырьмя независимыми ошибками:

{
  "port": 0,
  "workers": 0,
  "mode": "staging",
  "unexpected-option": true
}

Ошибки в конфиге:

  1. port меньше допустимого минимального значения.
  2. workers меньше допустимого минимального значения.
  3. mode не входит в список допустимых значений.
  4. unexpected-option запрещён из-за additionalProperties: false.

Разбор выполняется следующим образом:

const auto config = example::FromJsonString(
    json,
    userver::formats::parse::To<example::ServiceConfig>{}
);

Команда запуска воспроизводящего примера:

./chaotic_validation_reproducer ../invalid-config.json

Фактический результат

Несмотря на наличие четырёх независимых ошибок, выводится только ошибка поля port, после чего выбрасывается исключение:

Ошибка валидации:
Parse error at pos 13, path 'port': Error at path 'port': Invalid value, minimum=1, given=0, the latest token was : 0

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

invalid-config.json:
Ошибка валидации:
Parse error at pos 13, path 'port': Error at path 'port': Invalid value, minimum=1, given=0, the latest token was : 0

invalid-config-after-port-fix.json:
Ошибка валидации:
Parse error at pos 29, path 'workers': Error at path 'workers': Invalid value, minimum=1, given=0, the latest token was : 0

invalid-config-after-workers-fix.json:
Ошибка валидации:
Parse error at pos 50, path 'mode': Invalid enum value (staging) for type ::example::ServiceConfig::Mode, the latest token was : "staging"

invalid-config-only-extra.json:
Ошибка валидации:
Parse error at pos 76, path 'unexpected-option': unknown field 'unexpected-option' for type 'example::ServiceConfig', the latest token was ,
  "unexpected-option"

valid-config.json:
Конфиг успешно прошёл валидацию

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

Ожидаемый результат

Хотелось бы получить все независимые ошибки, которые удалось обнаружить за один проход валидации. Например, итоговый вывод мог бы выглядеть так:

Validation failed with 4 errors in config 'invalid-config.json':
- Parse error at pos 13, path 'port': Error at path 'port': Invalid value, minimum=1, given=0, the latest token was : 0
- Parse error at pos 29, path 'workers': Error at path 'workers': Invalid value, minimum=1, given=0, the latest token was : 0
- Parse error at pos 50, path 'mode': Invalid enum value (staging) for type ::example::ServiceConfig::Mode, the latest token was : "staging"
- Parse error at pos 76, path 'unexpected-option': unknown field 'unexpected-option' for type 'example::ServiceConfig', the latest token was : "unexpected-option"

Точный формат сообщения не принципиален. Основное пожелание — получить все обнаруживаемые независимые ошибки за один запуск.

Также было бы полезно указывать имя или другой идентификатор конфига, к которому относится каждая ошибка. Если за одну операцию проверяется несколько конфигов, хотелось бы получать ошибки из всех конфигов с группировкой по источнику. Например:

Validation failed with 4 errors in 2 configs:

Config 'service.json':
- path 'port': Invalid value, minimum=1, given=0
- path 'mode': Invalid enum value (staging) for type ::example::ServiceConfig::Mode

Config 'worker.json':
- path 'workers': Invalid value, minimum=1, given=0
- path 'unexpected-option': unknown field 'unexpected-option' for type 'example::ServiceConfig'

Сейчас FromJsonString получает содержимое JSON, но не знает имя исходного файла. Поэтому имя конфига могло бы передаваться вызывающим кодом как необязательный контекст или идентификатор источника.

Понятно, что после некоторых ошибок продолжить проверку невозможно. Например, если вместо объекта передана строка, проверить его вложенные поля нельзя. В таком случае достаточно вывести все ошибки, которые возможно обнаружить независимо.

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

Зачем это нужно

Для больших конфигов текущее поведение приводит к циклу:

  1. Запустить программу.
  2. Увидеть одну ошибку.
  3. Исправить её.
  4. Повторить запуск.

Это позволило бы исправлять все ошибки конфига за одну итерацию, без повторных запусков.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions