-
Notifications
You must be signed in to change notification settings - Fork 62
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Collapse multiple `required` errors into one - Collapse multiple `additionalProperties` and `unevaluatedProperties` errors into one, and print out which properties are prohibited. - Make `enum` errors print out the supported enum values. Signed-off-by: Theo Truong <[email protected]>
- Loading branch information
Showing
12 changed files
with
205 additions
and
58 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
/* | ||
* Copyright OpenSearch Contributors | ||
* SPDX-License-Identifier: Apache-2.0 | ||
* | ||
* The OpenSearch Contributors require contributions made to | ||
* this file be licensed under the Apache-2.0 license or a | ||
* compatible open source license. | ||
*/ | ||
|
||
import { Ajv2019, ErrorsTextOptions, ErrorObject } from 'ajv/dist/2019' | ||
import _ from 'lodash' | ||
|
||
export default class AjvErrorsParser { | ||
private readonly ajv: Ajv2019 | ||
private readonly options: ErrorsTextOptions | ||
|
||
constructor(ajv: Ajv2019, options: ErrorsTextOptions = {}) { | ||
this.ajv = ajv | ||
this.options = { separator: ' --- ', ...options } | ||
} | ||
|
||
parse(errors: ErrorObject[] | undefined | null): string { | ||
const error_groups = this.#group_errors(errors ?? []) | ||
const parsed_errors = [ | ||
this.#prohibited_property_error(error_groups.prohibited), | ||
this.#required_property_error(error_groups.required), | ||
this.#enum_error(error_groups.enum), | ||
...error_groups.others | ||
].filter(e => e != null) as ErrorObject[] | ||
return this.ajv.errorsText(parsed_errors, this.options) | ||
} | ||
|
||
#group_errors(errors: ErrorObject[]): { required: ErrorObject[], prohibited: ErrorObject[], enum: ErrorObject[], others: ErrorObject[] } { | ||
const categories = { | ||
required: [] as ErrorObject[], | ||
prohibited: [] as ErrorObject[], | ||
enum: [] as ErrorObject[], | ||
others: [] as ErrorObject[] | ||
} | ||
_.values(_.groupBy(errors, 'instancePath')).forEach((path_errors) => { | ||
for (const error of path_errors) { | ||
switch (error.keyword) { | ||
case 'required': | ||
categories.required.push(error) | ||
break | ||
case 'unevaluatedProperties': | ||
case 'additionalProperties': | ||
categories.prohibited.push(error) | ||
break | ||
case 'enum': | ||
categories.enum.push(error) | ||
break | ||
default: | ||
categories.others.push(error) | ||
} | ||
} | ||
}) | ||
return categories | ||
} | ||
|
||
#prohibited_property_error(errors: ErrorObject[]): ErrorObject | undefined { | ||
if (errors.length === 0) return | ||
const properties = errors.map((error) => error.params.additionalProperty ?? error.params.unevaluatedProperty).join(', ') | ||
return { | ||
keyword: 'prohibited', | ||
instancePath: errors[0].instancePath, | ||
schemaPath: errors[0].schemaPath, | ||
params: { additionalProperty: errors[0].instancePath.split('/').pop() }, | ||
message: `contains unsupported properties: ${properties}` | ||
} | ||
} | ||
|
||
#required_property_error(errors: ErrorObject[]): ErrorObject | undefined { | ||
if (errors.length === 0) return | ||
const properties = errors.map((error) => error.params.missingProperty).join(', ') | ||
return { | ||
keyword: 'required', | ||
instancePath: errors[0].instancePath, | ||
schemaPath: errors[0].schemaPath, | ||
params: { missingProperty: errors[0].instancePath.split('/').pop() }, | ||
message: `MUST contain the missing properties: ${properties}` | ||
} | ||
} | ||
|
||
#enum_error(errors: ErrorObject[]): ErrorObject | undefined { | ||
if (errors.length === 0) return | ||
const allowed_values = errors[0].params.allowedValues.join(', ') | ||
return { | ||
keyword: 'enum', | ||
instancePath: errors[0].instancePath, | ||
schemaPath: errors[0].schemaPath, | ||
params: errors[0].params, | ||
message: `MUST be equal to one of the allowed values: ${allowed_values}` | ||
} | ||
} | ||
|
||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
/* | ||
* Copyright OpenSearch Contributors | ||
* SPDX-License-Identifier: Apache-2.0 | ||
* | ||
* The OpenSearch Contributors require contributions made to | ||
* this file be licensed under the Apache-2.0 license or a | ||
* compatible open source license. | ||
*/ | ||
|
||
import { Ajv2019 } from "ajv/dist/2019"; | ||
import AjvErrorsParser from "../../src/_utils/AjvErrorsParser"; | ||
|
||
describe('AjvErrorsParser', () => { | ||
const ajv = new Ajv2019({ allErrors: true, strict: false }) | ||
const parser = new AjvErrorsParser(ajv, { separator: ' | ' , dataVar: 'Obj' }) | ||
const schema = { | ||
type: 'object', | ||
additionalProperties: false, | ||
required: [ 'a_boolean', 'a_number', 'an_array', 'an_enum', 'a_compound_object'], | ||
properties: { | ||
a_boolean: { type: 'boolean' }, | ||
a_number: { type: 'number' }, | ||
a_nullable_string: { type: ['string', 'null'] }, | ||
a_non_nullable_string: { type: 'string' }, | ||
an_array: { type: 'array', items: { type: 'string' }, minItems: 1 }, | ||
an_enum: { type: 'string', enum: ['a', 'b', 'c'] }, | ||
a_compound_object: { | ||
unevaluatedProperties: false, | ||
allOf: [ | ||
{ type: 'object', properties: { a: { type: 'string' } } }, | ||
{ type: 'object', properties: { b: { type: 'number' } } }, | ||
], | ||
} | ||
} | ||
} | ||
|
||
const data = { | ||
an_array: [], | ||
an_enum: 'd', | ||
a_compound_object: { stranger: 'danger', hello: 'world' }, | ||
space_odyssey: 42, | ||
thirteen_sentinels: 426 | ||
} | ||
|
||
const func = ajv.compile(schema) | ||
func(data) | ||
|
||
it('can parse multiple errors', () => { | ||
expect(parser.parse(func.errors)).toEqual( | ||
'Obj contains unsupported properties: space_odyssey, thirteen_sentinels, stranger, hello | ' + | ||
'Obj MUST contain the missing properties: a_boolean, a_number | ' + | ||
'Obj/an_enum MUST be equal to one of the allowed values: a, b, c | ' + | ||
'Obj/an_array must NOT have fewer than 1 items' | ||
) | ||
}); | ||
|
||
it('can parse empty errors', () => { | ||
const empty_func = ajv.compile({ type: 'object' }) | ||
empty_func({}) | ||
expect(parser.parse(empty_func.errors)).toEqual(ajv.errorsText(undefined)) | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.