Skip to content

Commit

Permalink
Improve validation error for oneOf
Browse files Browse the repository at this point in the history
  • Loading branch information
SamyPesse committed Oct 9, 2024
1 parent 9497a83 commit 81a5f03
Show file tree
Hide file tree
Showing 3 changed files with 112 additions and 11 deletions.
80 changes: 72 additions & 8 deletions src/compileValueSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,10 @@ function compileOneOfSchema(compiler: Compiler, schema: OpenAPIOneOfSchema) {

// Declare the variable to use as a result, then iterate over each schema
const resultIdentifier = builders.identifier('result');
const errorIdentifier = builders.identifier('error');
nodes.push(
builders.variableDeclaration('let', [builders.variableDeclarator(resultIdentifier)]),
builders.variableDeclaration('let', [builders.variableDeclarator(errorIdentifier)]),
);

schema.oneOf.forEach((subSchema, index) => {
Expand All @@ -125,14 +127,50 @@ function compileOneOfSchema(compiler: Compiler, schema: OpenAPIOneOfSchema) {

nodes.push(
builders.ifStatement(
builders.unaryExpression(
'!',
builders.binaryExpression(
'instanceof',
altIdentifier,
ValidationErrorIdentifier,
),
builders.binaryExpression(
'instanceof',
altIdentifier,
ValidationErrorIdentifier,
),
builders.blockStatement([
// Repalce the error if the new error is more specific (longer path)
builders.ifStatement(
builders.logicalExpression(
'||',
builders.binaryExpression(
'===',
errorIdentifier,
builders.identifier('undefined'),
),
builders.binaryExpression(
'<',
builders.memberExpression(
builders.memberExpression(
errorIdentifier,
builders.identifier('path'),
),
builders.identifier('length'),
),
builders.memberExpression(
builders.memberExpression(
altIdentifier,
builders.identifier('path'),
),
builders.identifier('length'),
),
),
),
builders.blockStatement([
builders.expressionStatement(
builders.assignmentExpression(
'=',
errorIdentifier,
altIdentifier,
),
),
]),
),
]),
builders.blockStatement([
...(index > 0
? [
Expand Down Expand Up @@ -165,7 +203,33 @@ function compileOneOfSchema(compiler: Compiler, schema: OpenAPIOneOfSchema) {
resultIdentifier,
builders.identifier('undefined'),
),
builders.blockStatement([builders.returnStatement(error('expected to match one'))]),
builders.blockStatement([
// If the path is longer than one level deep, then we return the error from the evaluation
// other we say that we expected to only match one of the schemas
builders.ifStatement(
builders.logicalExpression(
'&&',
errorIdentifier,
builders.binaryExpression(
'>',
builders.memberExpression(
builders.memberExpression(
errorIdentifier,
builders.identifier('path'),
),
builders.identifier('length'),
),
builders.binaryExpression(
'+',
builders.memberExpression(path, builders.identifier('length')),
builders.literal(1),
),
),
),
builders.blockStatement([builders.returnStatement(errorIdentifier)]),
builders.returnStatement(error('expected to match one')),
),
]),
),
);

Expand Down
18 changes: 15 additions & 3 deletions src/tests/__snapshots__/compileValueSchema.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -1074,19 +1074,31 @@ function obj2(path, value, context) {
}
function obj0(path, value, context) {
let result;
let error;
const alt0 = obj1(path, value, context);
if (!(alt0 instanceof ValidationError)) {
if (alt0 instanceof ValidationError) {
if (error === undefined || error.path.length < alt0.path.length) {
error = alt0;
}
} else {
result = alt0;
}
const alt1 = obj2(path, value, context);
if (!(alt1 instanceof ValidationError)) {
if (alt1 instanceof ValidationError) {
if (error === undefined || error.path.length < alt1.path.length) {
error = alt1;
}
} else {
if (result !== undefined) {
return new ValidationError(path, 'expected to only match one of the schemas');
}
result = alt1;
}
if (result === undefined) {
return new ValidationError(path, 'expected to match one');
if (error && error.path.length > path.length + 1) {
return error;
} else
return new ValidationError(path, 'expected to match one');
}
return result;
}"
Expand Down
25 changes: 25 additions & 0 deletions tests/gitbook.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -311,4 +311,29 @@ describe('componentSchemas', () => {
'expected "build" to be defined',
);
});

test('should return a best-effort error for a oneOf', () => {
const validate = componentSchemas['DocumentBlocksEssentials'];
expect(validate).toBeInstanceOf(Function);

const error = validate([], {
object: 'block',
type: 'paragraph',
nodes: [
// Invalid
{
object: 'block',
type: 'paragraph',
nodes: [
{
object: 'text',
text: 'Hello, world!',
},
],
},
],
});

expect(error instanceof ValidationError ? error.path : null).toEqual(['nodes', 0]);
});
});

0 comments on commit 81a5f03

Please sign in to comment.