-
Notifications
You must be signed in to change notification settings - Fork 2.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor validation (json-schema mostly)
Change json-schema validation to be able to push in different schemas Tweak semantic validation to allow overriding the validators that are run
- Loading branch information
Showing
19 changed files
with
630 additions
and
251 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,164 @@ | ||
// JSON-Schema ( draf04 ) validator | ||
import JsonSchemaWebWorker from "./validator.worker.js" | ||
import PromiseWorker from "promise-worker" | ||
import debounce from "lodash/debounce" | ||
import swagger2Schema from "./swagger2-schema.js" | ||
import oas3Schema from "./oas3-schema" | ||
|
||
// Lazily created promise worker | ||
let _promiseWorker | ||
const promiseWorker = () => { | ||
if(!_promiseWorker) | ||
_promiseWorker = new PromiseWorker(new JsonSchemaWebWorker()) | ||
return _promiseWorker | ||
} | ||
|
||
export const addSchema = (schema, schemaPath=[]) => () => { | ||
promiseWorker().postMessage({ | ||
type: "add-schema", | ||
payload: { | ||
schemaPath, | ||
schema | ||
} | ||
}) | ||
} | ||
|
||
// Figure out what schema we need to use ( we're making provision to be able to do sub-schema validation later on) | ||
// ...for now we just pick which base schema to use (eg: openapi-2-0, openapi-3.0, etc) | ||
export const getSchemaBasePath = () => ({ specSelectors }) => { | ||
// Eg: [openapi-3.0] or [openapi-2-0] | ||
// later on... ["openapi-2.0", "paths", "get"] | ||
const isOAS3 = specSelectors.isOAS3 ? specSelectors.isOAS3() : false | ||
const isSwagger2 = specSelectors.isSwagger2 ? specSelectors.isSwagger2() : false | ||
const isAmbiguousVersion = isOAS3 && isSwagger2 | ||
|
||
// Refuse to handle ambiguity | ||
if(isAmbiguousVersion) | ||
return [] | ||
|
||
if(isSwagger2) | ||
return ["openapi-2.0"] | ||
|
||
if(isOAS3) | ||
return ["openapi-3.0"] | ||
|
||
} | ||
|
||
|
||
export const setup = () => ({jsonSchemaValidatorActions}) => { | ||
// Add schemas , once off | ||
jsonSchemaValidatorActions.addSchema(swagger2Schema, ["openapi-2.0"]) | ||
jsonSchemaValidatorActions.addSchema(oas3Schema, ["openapi-3.0"]) | ||
} | ||
|
||
|
||
export const validate = ({spec, path=[], ...rest}) => system => { | ||
// stagger clearing errors, in case there is another debounced validation | ||
// run happening, which can occur when the user's typing cadence matches | ||
// the latency of validation | ||
// TODO: instead of using a timeout, be aware of any pending validation | ||
// promises, and use them to schedule error clearing. | ||
setTimeout(() => { | ||
system.errActions.clear({ | ||
source: system.jsonSchemaValidatorSelectors.errSource() | ||
}) | ||
}, 50) | ||
system.jsonSchemaValidatorActions.validateDebounced({spec, path, ...rest}) | ||
} | ||
|
||
// Create a debounced validate, that is lazy | ||
let _debValidate | ||
export const validateDebounced = (...args) => system => { | ||
// Lazily create one... | ||
if(!_debValidate) { | ||
_debValidate = debounce((...args) => { | ||
system.jsonSchemaValidatorActions.validateImmediate(...args) | ||
}, 200) | ||
} | ||
return _debValidate(...args) | ||
} | ||
|
||
export const validateImmediate = ({spec, path=[]}) => system => { | ||
// schemaPath refers to type of schema, and later might refer to sub-schema | ||
const baseSchemaPath = system.jsonSchemaValidatorSelectors.getSchemaBasePath() | ||
|
||
// No base path? Then we're unable to do anything... | ||
if(!baseSchemaPath.length) | ||
throw new Error("Ambiguous schema path, unable to run validation") | ||
|
||
return system.jsonSchemaValidatorActions.validateWithBaseSchema({spec, path: [...baseSchemaPath, ...path]}) | ||
} | ||
|
||
export const validateWithBaseSchema = ({spec, path=[]}) => system => { | ||
const errSource = system.jsonSchemaValidatorSelectors.errSource() | ||
|
||
return promiseWorker().postMessage({ | ||
type: "validate", | ||
payload: { | ||
jsSpec: spec, | ||
specStr: system.specSelectors.specStr(), | ||
schemaPath: path, | ||
source: errSource, | ||
} | ||
}).then(({results, path}) => { | ||
system.jsonSchemaValidatorActions.handleResults(null, {results, path}) | ||
}, err => { | ||
system.jsonSchemaValidatorActions.handleResults(err, {}) | ||
}) | ||
} | ||
|
||
export const handleResults = (err, {results}) => system => { | ||
if(err) { | ||
// Something bad happened with validation. | ||
throw err | ||
} | ||
|
||
system.errActions.clear({ | ||
source: system.jsonSchemaValidatorSelectors.errSource() | ||
}) | ||
|
||
if(!Array.isArray(results)) { | ||
results = [results] | ||
} | ||
|
||
// Filter out anything funky | ||
results = results.filter(val => typeof val === "object" && val !== null) | ||
|
||
if(results.length) { | ||
system.errActions.newSpecErrBatch(results) | ||
} | ||
} | ||
|
||
export default function() { | ||
return { | ||
statePlugins: { | ||
jsonSchemaValidator: { | ||
actions: { | ||
addSchema, | ||
validate, | ||
handleResults, | ||
validateDebounced, | ||
validateImmediate, | ||
validateWithBaseSchema, | ||
setup, | ||
}, | ||
selectors: { | ||
getSchemaBasePath, | ||
errSource() { | ||
// Used to identify the errors generated by this plugin | ||
return "json-schema" | ||
} | ||
} | ||
}, | ||
spec: { | ||
wrapActions: { | ||
validateSpec: (ori,system) => (...args) => { | ||
ori(...args) | ||
const [ spec, path ] = args | ||
system.jsonSchemaValidatorActions.validate({spec,path}) | ||
} | ||
}, | ||
} | ||
} | ||
} | ||
} |
File renamed without changes.
File renamed without changes.
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,24 @@ | ||
import "src/polyfills" | ||
import registerPromiseWorker from "promise-worker/register" | ||
import Validator from "./validator" | ||
|
||
const validator = new Validator() | ||
|
||
registerPromiseWorker(({ type, payload }) => { | ||
|
||
if(type == "add-schema") { | ||
const { schema, schemaPath } = payload | ||
validator.addSchema(schema, schemaPath) | ||
return | ||
} | ||
|
||
if(type == "validate") { | ||
const { jsSpec, specStr, schemaPath, source } = payload | ||
let validationResults = validator.validate({ | ||
jsSpec, specStr, schemaPath, source | ||
}) | ||
|
||
return { results: validationResults } | ||
} | ||
|
||
}) |
Oops, something went wrong.