Given an OpenAPI definition, I would like to validate a request (query params, path params, etc.) using @cfworker/json-schema
.
I've developed a way to do it, but it requires me to use the validate
function instead of new Validator
.
A simple OpenAPI definition might look like this:
{
"openapi": "3.1.0",
"components": {
"parameters": {
"taskId": {
"name": "taskId",
"in": "path",
"required": true,
"schema": {
"type": "integer",
"minimum": 100
}
}
},
"schemas": {
"task": {
"type": "object",
"properties": {
"id": {
"type": "integer",
"minimum": 100
},
"completed": {
"type": "boolean"
}
}
}
}
},
"paths": {
"/tasks/{taskId}": {
"parameters": [
{ "$ref": "#/components/parameters/taskId" }
],
"patch": {
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/task"
}
}
}
}
}
}
}
}
A few things to note, if you're new to OpenAPI:
- It's not a valid JSON-Schema document, so any validation will need some setup anyway.
- Within OpenAPI there's also some type coercion that needs to happen prior to validation. E.g. path parameters can have different "styles" which would need to get cast to different data structures.
Some technical issues with schema validation:
- The
$ref
instances point to an object that's not necessarily a schema. See the reference to#/components/parameters/taskId
instead of#/components/parameters/taskId/schema
for example.
I could not figure out a way to instantiate a Validator
instance based on an OpenAPI definition, so instead I followed this approach:
// Use the raw `validate` instead
import { validate } from '@cfworker/json-schema'
import definition from './open-api-definition.json'
// I have some utils to do this, but basically you need to map each
// actual schema to its own hash reference, which looks like this, for
// the example above
const lookup = {
'#/components/parameters/taskId': definition.components.parameters.taskId.schema,
'#/components/schemas/task': definition.components.schemas.task,
'#/paths/%2Ftasks%2F%7BtaskId%7D/patch/requestBody/content/application%2Fjson': definition.paths['/tasks/{taskId}'].patch.requestBody.content['application/json'].schema
}
// Based on the request you'd build the `lookup` key
const requestKey = [
'paths',
encodeURIComponent(request.path),
request.method.toLowerCase(),
'content',
request.headers.get('Accept')
].join('/')
const { valid, errors } = validate(
{ id: 123, completed: true },
lookup['#/' + requestKey],
'2019-09',
lookup,
false
)