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
}
Ошибки в конфиге:
port меньше допустимого минимального значения.
workers меньше допустимого минимального значения.
mode не входит в список допустимых значений.
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, но не знает имя исходного файла. Поэтому имя конфига могло бы передаваться вызывающим кодом как необязательный контекст или идентификатор источника.
Понятно, что после некоторых ошибок продолжить проверку невозможно. Например, если вместо объекта передана строка, проверить его вложенные поля нельзя. В таком случае достаточно вывести все ошибки, которые возможно обнаружить независимо.
Если разработчики не захотят менять текущее поведение для всех пользователей, можно добавить отдельную настройку или функцию, которая по запросу включает сбор всех ошибок вместо остановки на первой.
Зачем это нужно
Для больших конфигов текущее поведение приводит к циклу:
- Запустить программу.
- Увидеть одну ошибку.
- Исправить её.
- Повторить запуск.
Это позволило бы исправлять все ошибки конфига за одну итерацию, без повторных запусков.
Add a description
Описание
При разборе JSON-конфига с помощью парсера, сгенерированного
chaotic, проверка прекращается после первой ошибки валидации и выбрасывается исключение.Если конфиг содержит несколько независимых ошибок, их приходится исправлять по одной: исправить первую ошибку, повторно запустить программу, увидеть следующую ошибку и так далее.
Было бы удобно получать все независимые ошибки, которые можно обнаружить за один проход валидации.
Как воспроизвести
Commit userver, на котором воспроизводится проблема:
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 }Ошибки в конфиге:
portменьше допустимого минимального значения.workersменьше допустимого минимального значения.modeне входит в список допустимых значений.unexpected-optionзапрещён из-заadditionalProperties: false.Разбор выполняется следующим образом:
Команда запуска воспроизводящего примера:
Фактический результат
Несмотря на наличие четырёх независимых ошибок, выводится только ошибка поля
port, после чего выбрасывается исключение:Чтобы проверить наличие остальных ошибок, первая найденная ошибка исправлялась перед каждым следующим запуском. Получилась следующая последовательность:
Таким образом, все четыре ошибки присутствуют в исходном конфиге одновременно, но каждая следующая ошибка становится видна только после исправления предыдущей и повторного запуска.
Ожидаемый результат
Хотелось бы получить все независимые ошибки, которые удалось обнаружить за один проход валидации. Например, итоговый вывод мог бы выглядеть так:
Точный формат сообщения не принципиален. Основное пожелание — получить все обнаруживаемые независимые ошибки за один запуск.
Также было бы полезно указывать имя или другой идентификатор конфига, к которому относится каждая ошибка. Если за одну операцию проверяется несколько конфигов, хотелось бы получать ошибки из всех конфигов с группировкой по источнику. Например:
Сейчас
FromJsonStringполучает содержимое JSON, но не знает имя исходного файла. Поэтому имя конфига могло бы передаваться вызывающим кодом как необязательный контекст или идентификатор источника.Понятно, что после некоторых ошибок продолжить проверку невозможно. Например, если вместо объекта передана строка, проверить его вложенные поля нельзя. В таком случае достаточно вывести все ошибки, которые возможно обнаружить независимо.
Если разработчики не захотят менять текущее поведение для всех пользователей, можно добавить отдельную настройку или функцию, которая по запросу включает сбор всех ошибок вместо остановки на первой.
Зачем это нужно
Для больших конфигов текущее поведение приводит к циклу:
Это позволило бы исправлять все ошибки конфига за одну итерацию, без повторных запусков.