Skip to content

Commit 03da52f

Browse files
committed
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
1 parent 99fa166 commit 03da52f

File tree

19 files changed

+630
-251
lines changed

19 files changed

+630
-251
lines changed
Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
// JSON-Schema ( draf04 ) validator
2+
import JsonSchemaWebWorker from "./validator.worker.js"
3+
import PromiseWorker from "promise-worker"
4+
import debounce from "lodash/debounce"
5+
import swagger2Schema from "./swagger2-schema.js"
6+
import oas3Schema from "./oas3-schema"
7+
8+
// Lazily created promise worker
9+
let _promiseWorker
10+
const promiseWorker = () => {
11+
if(!_promiseWorker)
12+
_promiseWorker = new PromiseWorker(new JsonSchemaWebWorker())
13+
return _promiseWorker
14+
}
15+
16+
export const addSchema = (schema, schemaPath=[]) => () => {
17+
promiseWorker().postMessage({
18+
type: "add-schema",
19+
payload: {
20+
schemaPath,
21+
schema
22+
}
23+
})
24+
}
25+
26+
// Figure out what schema we need to use ( we're making provision to be able to do sub-schema validation later on)
27+
// ...for now we just pick which base schema to use (eg: openapi-2-0, openapi-3.0, etc)
28+
export const getSchemaBasePath = () => ({ specSelectors }) => {
29+
// Eg: [openapi-3.0] or [openapi-2-0]
30+
// later on... ["openapi-2.0", "paths", "get"]
31+
const isOAS3 = specSelectors.isOAS3 ? specSelectors.isOAS3() : false
32+
const isSwagger2 = specSelectors.isSwagger2 ? specSelectors.isSwagger2() : false
33+
const isAmbiguousVersion = isOAS3 && isSwagger2
34+
35+
// Refuse to handle ambiguity
36+
if(isAmbiguousVersion)
37+
return []
38+
39+
if(isSwagger2)
40+
return ["openapi-2.0"]
41+
42+
if(isOAS3)
43+
return ["openapi-3.0"]
44+
45+
}
46+
47+
48+
export const setup = () => ({jsonSchemaValidatorActions}) => {
49+
// Add schemas , once off
50+
jsonSchemaValidatorActions.addSchema(swagger2Schema, ["openapi-2.0"])
51+
jsonSchemaValidatorActions.addSchema(oas3Schema, ["openapi-3.0"])
52+
}
53+
54+
55+
export const validate = ({spec, path=[], ...rest}) => system => {
56+
// stagger clearing errors, in case there is another debounced validation
57+
// run happening, which can occur when the user's typing cadence matches
58+
// the latency of validation
59+
// TODO: instead of using a timeout, be aware of any pending validation
60+
// promises, and use them to schedule error clearing.
61+
setTimeout(() => {
62+
system.errActions.clear({
63+
source: system.jsonSchemaValidatorSelectors.errSource()
64+
})
65+
}, 50)
66+
system.jsonSchemaValidatorActions.validateDebounced({spec, path, ...rest})
67+
}
68+
69+
// Create a debounced validate, that is lazy
70+
let _debValidate
71+
export const validateDebounced = (...args) => system => {
72+
// Lazily create one...
73+
if(!_debValidate) {
74+
_debValidate = debounce((...args) => {
75+
system.jsonSchemaValidatorActions.validateImmediate(...args)
76+
}, 200)
77+
}
78+
return _debValidate(...args)
79+
}
80+
81+
export const validateImmediate = ({spec, path=[]}) => system => {
82+
// schemaPath refers to type of schema, and later might refer to sub-schema
83+
const baseSchemaPath = system.jsonSchemaValidatorSelectors.getSchemaBasePath()
84+
85+
// No base path? Then we're unable to do anything...
86+
if(!baseSchemaPath.length)
87+
throw new Error("Ambiguous schema path, unable to run validation")
88+
89+
return system.jsonSchemaValidatorActions.validateWithBaseSchema({spec, path: [...baseSchemaPath, ...path]})
90+
}
91+
92+
export const validateWithBaseSchema = ({spec, path=[]}) => system => {
93+
const errSource = system.jsonSchemaValidatorSelectors.errSource()
94+
95+
return promiseWorker().postMessage({
96+
type: "validate",
97+
payload: {
98+
jsSpec: spec,
99+
specStr: system.specSelectors.specStr(),
100+
schemaPath: path,
101+
source: errSource,
102+
}
103+
}).then(({results, path}) => {
104+
system.jsonSchemaValidatorActions.handleResults(null, {results, path})
105+
}, err => {
106+
system.jsonSchemaValidatorActions.handleResults(err, {})
107+
})
108+
}
109+
110+
export const handleResults = (err, {results}) => system => {
111+
if(err) {
112+
// Something bad happened with validation.
113+
throw err
114+
}
115+
116+
system.errActions.clear({
117+
source: system.jsonSchemaValidatorSelectors.errSource()
118+
})
119+
120+
if(!Array.isArray(results)) {
121+
results = [results]
122+
}
123+
124+
// Filter out anything funky
125+
results = results.filter(val => typeof val === "object" && val !== null)
126+
127+
if(results.length) {
128+
system.errActions.newSpecErrBatch(results)
129+
}
130+
}
131+
132+
export default function() {
133+
return {
134+
statePlugins: {
135+
jsonSchemaValidator: {
136+
actions: {
137+
addSchema,
138+
validate,
139+
handleResults,
140+
validateDebounced,
141+
validateImmediate,
142+
validateWithBaseSchema,
143+
setup,
144+
},
145+
selectors: {
146+
getSchemaBasePath,
147+
errSource() {
148+
// Used to identify the errors generated by this plugin
149+
return "json-schema"
150+
}
151+
}
152+
},
153+
spec: {
154+
wrapActions: {
155+
validateSpec: (ori,system) => (...args) => {
156+
ori(...args)
157+
const [ spec, path ] = args
158+
system.jsonSchemaValidatorActions.validate({spec,path})
159+
}
160+
},
161+
}
162+
}
163+
}
164+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import "src/polyfills"
2+
import registerPromiseWorker from "promise-worker/register"
3+
import Validator from "./validator"
4+
5+
const validator = new Validator()
6+
7+
registerPromiseWorker(({ type, payload }) => {
8+
9+
if(type == "add-schema") {
10+
const { schema, schemaPath } = payload
11+
validator.addSchema(schema, schemaPath)
12+
return
13+
}
14+
15+
if(type == "validate") {
16+
const { jsSpec, specStr, schemaPath, source } = payload
17+
let validationResults = validator.validate({
18+
jsSpec, specStr, schemaPath, source
19+
})
20+
21+
return { results: validationResults }
22+
}
23+
24+
})

0 commit comments

Comments
 (0)