Skip to content

Commit

Permalink
chore: improve internal test run APIs and Error handling (#1545)
Browse files Browse the repository at this point in the history
* docs

* WIP

* chore: clean up internal test running APIs

* fix order
  • Loading branch information
jquense authored Jan 3, 2022
1 parent 57b81f4 commit dde11ca
Show file tree
Hide file tree
Showing 11 changed files with 271 additions and 273 deletions.
15 changes: 7 additions & 8 deletions src/Lazy.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import isSchema from './util/isSchema';
import type { AnyObject, Callback, ValidateOptions } from './types';
import type { AnyObject, ValidateOptions } from './types';
import type { ResolveOptions } from './Condition';

import type {
Expand Down Expand Up @@ -82,13 +82,12 @@ class Lazy<T, TContext = AnyObject, TDefault = any, TFlags extends Flags = any>
return this._resolve(value, options).cast(value, options);
}

validate(
value: any,
options?: ValidateOptions,
maybeCb?: Callback,
): Promise<T> {
// @ts-expect-error missing public callback on type
return this._resolve(value, options).validate(value, options, maybeCb);
asTest(value: any, options?: ValidateOptions<TContext>) {
return this._resolve(value, options).asTest(value, options);
}

validate(value: any, options?: ValidateOptions<TContext>): Promise<T> {
return this._resolve(value, options).validate(value, options);
}

validateSync(value: any, options?: ValidateOptions<TContext>): T {
Expand Down
55 changes: 22 additions & 33 deletions src/array.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,13 @@ import isAbsent from './util/isAbsent';
import isSchema from './util/isSchema';
import printValue from './util/printValue';
import { array as locale } from './locale';
import runTests, { RunTest } from './util/runTests';
import type {
AnyObject,
InternalOptions,
Callback,
Message,
Maybe,
Optionals,
} from './types';
import ValidationError from './ValidationError';
import type Reference from './Reference';
import {
Defined,
Expand All @@ -24,9 +21,14 @@ import {
UnsetFlag,
Concat,
} from './util/types';
import Schema, { SchemaInnerTypeDescription, SchemaSpec } from './schema';
import Schema, {
RunTest,
SchemaInnerTypeDescription,
SchemaSpec,
} from './schema';
import { ResolveOptions } from './Condition';
import parseJson from 'parse-json';
import { ValidationError } from '.';

type InnerType<T> = T extends Array<infer I> ? I : never;

Expand All @@ -36,9 +38,7 @@ export type RejectorFn = (
array: readonly any[],
) => boolean;

export function create<C extends AnyObject = AnyObject, T = any>(
type?: ISchema<T, C>,
) {
export function create<C = AnyObject, T = any>(type?: ISchema<T, C>) {
return new ArraySchema<T[] | undefined, C>(type as any);
}

Expand Down Expand Up @@ -89,28 +89,22 @@ export default class ArraySchema<
protected _validate(
_value: any,
options: InternalOptions<TContext> = {},
callback: Callback,

panic: (err: Error, value: unknown) => void,
callback: (err: ValidationError[], value: unknown) => void,
) {
let errors = [] as ValidationError[];
let sync = options.sync;
let path = options.path;
// let sync = options.sync;
// let path = options.path;
let innerType = this.innerType;
let endEarly = options.abortEarly ?? this.spec.abortEarly;
// let endEarly = options.abortEarly ?? this.spec.abortEarly;
let recursive = options.recursive ?? this.spec.recursive;

let originalValue =
options.originalValue != null ? options.originalValue : _value;

super._validate(_value, options, (err, value) => {
if (err) {
if (!ValidationError.isError(err) || endEarly) {
return void callback(err, value);
}
errors.push(err);
}

super._validate(_value, options, panic, (arrayErrors, value) => {
if (!recursive || !innerType || !this._typeCheck(value)) {
callback(errors[0] || null, value);
callback(arrayErrors, value);
return;
}

Expand All @@ -122,29 +116,24 @@ export default class ArraySchema<
let item = value[idx];
let path = `${options.path || ''}[${idx}]`;

// object._validate note for isStrict explanation
let innerOptions = {
tests[idx] = innerType!.asTest(item, {
...options,
path,
strict: true,
parent: value,
// FIXME
index: idx,
originalValue: originalValue[idx],
};

tests[idx] = (_, cb) => innerType!.validate(item, innerOptions, cb);
});
}

runTests(
this.runTests(
{
sync,
path,
value,
errors,
endEarly,
tests,
},
callback,
panic,
(innerTypeErrors) =>
callback(innerTypeErrors.concat(arrayErrors), value),
);
});
}
Expand Down
81 changes: 28 additions & 53 deletions src/object.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,9 @@ import {
import { object as locale } from './locale';
import sortFields from './util/sortFields';
import sortByKeyOrder from './util/sortByKeyOrder';
import runTests from './util/runTests';
import { InternalOptions, Callback, Maybe, Message } from './types';
import ValidationError from './ValidationError';
import { InternalOptions, Maybe, Message } from './types';
import type { Defined, Thunk, NotNull, _ } from './util/types';
import type Reference from './Reference';
import Reference from './Reference';
import Schema, { SchemaObjectDescription, SchemaSpec } from './schema';
import { ResolveOptions } from './Condition';
import type {
Expand All @@ -30,6 +28,8 @@ import type {
TypeFromShape,
} from './util/objectTypes';
import parseJson from './util/parseJson';
import type { Test } from './util/createValidation';
import type ValidationError from './ValidationError';

export type { AnyObject };

Expand Down Expand Up @@ -205,14 +205,12 @@ export default class ObjectSchema<
protected _validate(
_value: any,
opts: InternalOptions<TContext> = {},
callback: Callback,
panic: (err: Error, value: unknown) => void,
next: (err: ValidationError[], value: unknown) => void,
) {
let errors = [] as ValidationError[];
let {
sync,
from = [],
originalValue = _value,
abortEarly = this.spec.abortEarly,
recursive = this.spec.recursive,
} = opts;

Expand All @@ -224,64 +222,41 @@ export default class ObjectSchema<
opts.originalValue = originalValue;
opts.from = from;

super._validate(_value, opts, (err, value) => {
if (err) {
if (!ValidationError.isError(err) || abortEarly) {
return void callback(err, value);
}
errors.push(err);
}

super._validate(_value, opts, panic, (objectErrors, value) => {
if (!recursive || !isObject(value)) {
callback(errors[0] || null, value);
next(objectErrors, value);
return;
}

originalValue = originalValue || value;

let tests = this._nodes.map((key) => (__: any, cb: Callback) => {
let tests = [] as Test[];
for (let key of this._nodes) {
let field = this.fields[key];

if (!field || Reference.isRef(field)) {
continue;
}

let path =
key.indexOf('.') === -1
? (opts.path ? `${opts.path}.` : '') + key
: `${opts.path || ''}["${key}"]`;

let field = this.fields[key];

if (field && 'validate' in field) {
field.validate(
value[key],
{
...opts,
// @ts-ignore
path,
from,
// inner fields are always strict:
// 1. this isn't strict so the casting will also have cast inner values
// 2. this is strict in which case the nested values weren't cast either
strict: true,
parent: value,
originalValue: originalValue[key],
},
cb,
);
return;
}
tests.push(
field.asTest(value[key], {
...opts,
path,
from,
parent: value,
originalValue: originalValue[key],
}),
);
}

cb(null);
this.runTests({ tests, value }, panic, (fieldErrors) => {
next(fieldErrors.sort(this._sortErrors).concat(objectErrors), value);
});

runTests(
{
sync,
tests,
value,
errors,
endEarly: abortEarly,
sort: this._sortErrors,
path: opts.path,
},
callback,
);
});
}

Expand Down
Loading

0 comments on commit dde11ca

Please sign in to comment.