Skip to content

Commit

Permalink
refactor(validate): boolean schema to typescript
Browse files Browse the repository at this point in the history
  • Loading branch information
epoberezkin committed Aug 15, 2020
1 parent 3b267b0 commit c974266
Show file tree
Hide file tree
Showing 10 changed files with 79 additions and 163 deletions.
7 changes: 4 additions & 3 deletions lib/compile/errors.ts
Expand Up @@ -9,10 +9,11 @@ export function reportError(
const {gen, compositeRule, opts, async} = cxt.it
const errObj = errorObjectCode(cxt, error)
if (allErrors ?? (compositeRule || opts.allErrors)) {
const err = gen.name("err")
gen.code(
`const err = ${errObj};
if (vErrors === null) vErrors = [err];
else vErrors.push(err);
`const ${err} = ${errObj};
if (vErrors === null) vErrors = [${err}];
else vErrors.push(${err});
errors++;`
)
} else {
Expand Down
12 changes: 10 additions & 2 deletions lib/compile/index.js
@@ -1,7 +1,9 @@
import CodeGen from "./codegen"
import {toQuotedString} from "./util"
import {MissingRefError} from "./error_classes"
import {coerceData} from "./validate/dataType"
import {getSchemaTypes, coerceAndCheckDataType} from "./validate/dataType"
import {updateTopContext, checkNoDefault, updateContext, checkAsync} from "./validate"
import {booleanOrEmptySchema} from "./validate/boolSchema"

const equal = require("fast-deep-equal")
const ucs2length = require("./ucs2length")
Expand Down Expand Up @@ -101,7 +103,13 @@ function compile(schema, root, localRefs, baseId) {
MissingRefError,
RULES: RULES,
validate: validateGenerator,
coerceData, // TODO remove when validate is replaced
getSchemaTypes, // TODO remove when validate is replaced
coerceAndCheckDataType, // TODO remove when validate is replaced
updateTopContext, // TODO remove when validate is replaced
checkNoDefault, // TODO remove when validate is replaced
updateContext, // TODO remove when validate is replaced
checkAsync, // TODO remove when validate is replaced
booleanOrEmptySchema, // TODO remove when validate is replaced
util: util,
resolve: resolve,
resolveRef: resolveRef,
Expand Down
2 changes: 2 additions & 0 deletions lib/compile/util.ts
Expand Up @@ -2,6 +2,7 @@
module.exports = {
checkDataType,
checkDataTypes,
// TODO remove when validate is refactored
coerceToTypes,
toHash,
escapeQuotes,
Expand Down Expand Up @@ -72,6 +73,7 @@ export function checkDataTypes(
return code
}

// TODO remove when validate is refactored
const COERCE_TYPES = toHash(["string", "number", "integer", "boolean", "null"])
export function coerceToTypes(
optionCoerceTypes: undefined | boolean | "array",
Expand Down
4 changes: 2 additions & 2 deletions lib/compile/validate/applicability.ts
Expand Up @@ -5,8 +5,8 @@ export function schemaHasRulesForType({RULES, schema}: CompilationContext, ty: s
return group && group !== true && shouldUseGroup(schema, group)
}

function shouldUseGroup(schema, rulesGroup): boolean {
return rulesGroup.some((rule) => shouldUseRule(schema, rule))
function shouldUseGroup(schema, group): boolean {
return group.rules.some((rule) => shouldUseRule(schema, rule))
}

function shouldUseRule(schema, rule): boolean {
Expand Down
50 changes: 0 additions & 50 deletions lib/compile/validate/boolSchema.ts
Expand Up @@ -28,26 +28,6 @@ export function booleanOrEmptySchema(it: CompilationContext): void {
gen.code(`var valid${level} = true;`) // TODO level, var
}
}

// if (schema === false) {
// if (!isTop) {
// gen.code(`var valid${level} = false;`) // TODO level, var
// }
// // TODO probably some other interface should be used for non-keyword validation errors...
// falseSchemaError(it, !isTop)
// } else {
// if (isTop) {
// gen.code(schema.$async === true ? `return data;` : `validate.errors = null; return true;`)
// } else {
// gen.code(`var valid${level} = true;`) // TODO level, var
// }
// }
// if (isTop) {
// gen.code(
// `};
// return validate;`
// )
// }
}

function falseSchemaError(it: CompilationContext, allErrors?: boolean) {
Expand All @@ -73,33 +53,3 @@ function falseSchemaError(it: CompilationContext, allErrors?: boolean) {
function exception() {
throw new Error("this function can only be used in keyword")
}

// {{ var $keyword = 'false schema'; }}
// {{# def.setupKeyword }}
// {{? it.schema === false}}
// {{? it.isTop}}
// {{ $breakOnError = true; }}
// {{??}}
// var {{=$valid}} = false;
// {{?}}
// {{# def.error:'false schema' }}
// {{??}}
// {{? it.isTop}}
// {{? $async }}
// return data;
// {{??}}
// validate.errors = null;
// return true;
// {{?}}
// {{??}}
// var {{=$valid}} = true;
// {{?}}
// {{?}}

// {{? it.isTop}}
// };
// return validate;
// {{?}}

// {{ return out; }}
// {{?}}
11 changes: 7 additions & 4 deletions lib/compile/validate/dataType.ts
Expand Up @@ -5,8 +5,8 @@ import {schemaHasRulesForType} from "./applicability"
import {reportError} from "../errors"

export function getSchemaTypes({schema, opts}: CompilationContext): string[] {
const t = schema.type
const types: string[] = Array.isArray(t) ? t : t || []
const t: undefined | string | string[] = schema.type
const types: string[] = Array.isArray(t) ? t : t ? [t] : []
types.forEach(checkType)
if (opts.nullable) {
const hasNull = types.includes("null")
Expand All @@ -20,7 +20,7 @@ export function getSchemaTypes({schema, opts}: CompilationContext): string[] {

function checkType(t: string): void {
// TODO check that type is allowed
if (typeof t != "string") throw new Error('"type" keyword must be string or string[]')
if (typeof t != "string") throw new Error('"type" keyword must be string or string[]: ' + t)
}
}

Expand All @@ -31,7 +31,10 @@ export function coerceAndCheckDataType(it: CompilationContext, types: string[]):
opts: {coerceTypes, strictNumbers},
} = it
let coerceTo = coerceToTypes(types, coerceTypes)
if (coerceTo.length || types.length > 1 || !schemaHasRulesForType(it, types[0])) {
if (
types.length &&
(coerceTo.length || types.length > 1 || !schemaHasRulesForType(it, types[0]))
) {
const wrongType = checkDataTypes(types, `data${dataLevel || ""}`, strictNumbers, true)
gen.code(`if (${wrongType}) {`)
if (coerceTo.length) coerceData(it, coerceTo)
Expand Down
8 changes: 4 additions & 4 deletions lib/compile/validate/index.ts
Expand Up @@ -69,15 +69,15 @@ function startFunction({
)
}

function updateTopContext(it: CompilationContext): void {
export function updateTopContext(it: CompilationContext): void {
it.rootId = resolve.fullPath(it.root.schema.$id)
it.baseId = it.baseId || it.rootId
delete it.isTop

it.dataPathArr = [""]
}

function checkNoDefault({
export function checkNoDefault({
schema,
opts: {useDefaults, strictDefaults},
logger,
Expand All @@ -98,11 +98,11 @@ function initializeTop({gen}: CompilationContext): void {
)
}

function updateContext(it: CompilationContext): void {
export function updateContext(it: CompilationContext): void {
if (it.schema.$id) it.baseId = resolve.url(it.baseId, it.schema.$id)
}

function checkAsync(it: CompilationContext): void {
export function checkAsync(it: CompilationContext): void {
if (it.schema.$async && !it.async) throw new Error("async schema in sync schema")
}

Expand Down
56 changes: 28 additions & 28 deletions lib/compile/validate/validate.jst
Expand Up @@ -40,34 +40,34 @@
// {{?}}

// {{? typeof it.schema == 'boolean' || !($refKeywords || it.schema.$ref) }}
// {{ var $keyword = 'false schema'; }}
// {{# def.setupKeyword }}
// {{? it.schema === false}}
// {{? it.isTop}}
// {{ $breakOnError = true; }}
// {{??}}
// var {{=$valid}} = false;
// {{?}}
// {{# def.error:'false schema' }}
// {{??}}
// {{? it.isTop}}
// {{? $async }}
// return data;
// {{??}}
// validate.errors = null;
// return true;
// {{?}}
// {{??}}
// var {{=$valid}} = true;
// {{?}}
// {{?}}

// {{? it.isTop}}
// };
// return validate;
// {{?}}

// {{ return out; }}
// {{ var $keyword = 'false schema'; }}
// {{# def.setupKeyword }}
// {{? it.schema === false}}
// {{? it.isTop}}
// {{ $breakOnError = true; }}
// {{??}}
// var {{=$valid}} = false;
// {{?}}
// {{# def.error:'false schema' }}
// {{??}}
// {{? it.isTop}}
// {{? $async }}
// return data;
// {{??}}
// validate.errors = null;
// return true;
// {{?}}
// {{??}}
// var {{=$valid}} = true;
// {{?}}
// {{?}}

// {{? it.isTop}}
// };
// return validate;
// {{?}}

// {{ return out; }}
// {{?}}

{{? it.isTop }}
Expand Down
86 changes: 18 additions & 68 deletions lib/dot/validate.jst
Expand Up @@ -39,34 +39,12 @@
{{?}}

{{? typeof it.schema == 'boolean' || !($refKeywords || it.schema.$ref) }}
{{ var $keyword = 'false schema'; }}
{{# def.setupKeyword }}
{{? it.schema === false}}
{{? it.isTop}}
{{ $breakOnError = true; }}
{{??}}
var {{=$valid}} = false;
{{?}}
{{# def.error:'false schema' }}
{{??}}
{{? it.isTop}}
{{? $async }}
return data;
{{??}}
validate.errors = null;
return true;
{{?}}
{{??}}
var {{=$valid}} = true;
{{?}}
{{?}}

{{? it.isTop}}
};
return validate;
{{?}}

{{ return out; }}
{{
/* TODO do not clear _out */
it.gen._out = "";
it.booleanOrEmptySchema(it);
return out + it.gen._out;
}}
{{?}}


Expand All @@ -76,17 +54,9 @@
, $lvl = it.level = 0
, $dataLvl = it.dataLevel = 0
, $data = 'data';
it.rootId = it.resolve.fullPath(it.root.schema.$id);
it.baseId = it.baseId || it.rootId;
delete it.isTop;

it.dataPathArr = [undefined];

if (it.schema.default !== undefined && it.opts.useDefaults && it.opts.strictDefaults) {
var $defaultMsg = 'default is ignored in the schema root';
if (it.opts.strictDefaults === 'log') it.logger.warn($defaultMsg);
else throw new Error($defaultMsg);
}
it.updateTopContext(it);
it.checkNoDefault(it);
}}

var vErrors = null; {{ /* don't edit, used in replace */ }}
Expand All @@ -98,9 +68,8 @@
, $dataLvl = it.dataLevel
, $data = 'data' + ($dataLvl || '');

if ($id) it.baseId = it.resolve.url(it.baseId, $id);

if ($async && !it.async) throw new Error('async schema in sync schema');
it.updateContext(it);
it.checkAsync(it);
}}

var errs_{{=$lvl}} = errors;
Expand Down Expand Up @@ -157,33 +126,14 @@
{{= it.RULES.all.$comment.code(it, '$comment') }}
{{?}}

{{? $typeSchema }}
{{? it.opts.coerceTypes }}
{{ var $coerceToTypes = it.util.coerceToTypes(it.opts.coerceTypes, $typeSchema); }}
{{?}}

{{ var $rulesGroup = it.RULES.types[$typeSchema]; }}
{{? $coerceToTypes || $typeIsArray || $rulesGroup === true ||
($rulesGroup && !$shouldUseGroup($rulesGroup)) }}
{{
var $schemaPath = it.schemaPath + '.type'
, $errSchemaPath = it.errSchemaPath + '/type';
}}
{{# def.checkType }}
{{? $coerceToTypes }}
{{
/* TODO do not clear _out */
it.gen._out = "";
it.coerceData(it, $coerceToTypes);
}}
{{= it.gen._out }}
{{??}}
{{# def.error:'type' }}
{{?}}
}
{{?}}
{{?}}

{{
const types = it.getSchemaTypes(it);
/* TODO do not clear _out */
it.gen._out = "";
it.coerceAndCheckDataType(it, types);
}}
{{= it.gen._out }}

{{? it.schema.$ref && !$refKeywords }}
{{= it.RULES.all.$ref.code(it, '$ref') }}
Expand Down Expand Up @@ -222,7 +172,7 @@
{{?}}
{{? $rulesGroup.type }}
}
{{? $typeSchema && $typeSchema === $rulesGroup.type && !$coerceToTypes }}
{{? $typeSchema && $typeSchema === $rulesGroup.type && !(it.opts.coerceTypes && it.util.coerceToTypes(it.opts.coerceTypes, $typeSchema)) }}
else {
{{
var $schemaPath = it.schemaPath + '.type'
Expand Down
6 changes: 4 additions & 2 deletions spec/options/options_code.spec.js
Expand Up @@ -34,12 +34,14 @@ describe("code generation options", () => {
it("should process generated code", () => {
var ajv = new Ajv()
var validate = ajv.compile({type: "string"})
validate.toString().split("\n").length.should.equal(1)
// TODO re-enable this test when option to strip whitespace is added
// validate.toString().split("\n").length.should.equal(1)
const unprocessedLines = validate.toString().split("\n").length

var beautify = require("js-beautify").js_beautify
var ajvPC = new Ajv({processCode: beautify})
validate = ajvPC.compile({type: "string"})
validate.toString().split("\n").length.should.be.above(1)
validate.toString().split("\n").length.should.be.above(unprocessedLines)
validate("foo").should.equal(true)
validate(1).should.equal(false)
})
Expand Down

0 comments on commit c974266

Please sign in to comment.