Skip to content

Commit

Permalink
refactor validation (json-schema mostly)
Browse files Browse the repository at this point in the history
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
ponelat committed Jan 28, 2019
1 parent 99fa166 commit 03da52f
Show file tree
Hide file tree
Showing 19 changed files with 630 additions and 251 deletions.
164 changes: 164 additions & 0 deletions src/plugins/json-schema-validator/index.js
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})
}
},
}
}
}
}
24 changes: 24 additions & 0 deletions src/plugins/json-schema-validator/validator.worker.js
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 }
}

})
Loading

0 comments on commit 03da52f

Please sign in to comment.