Skip to content

Commit

Permalink
improve(validation): array schema items (swagger-api#1765)
Browse files Browse the repository at this point in the history
* add failing tests

* implement cross-version array schema validation
  • Loading branch information
shockey authored May 23, 2018
1 parent d211d5e commit 55059f9
Show file tree
Hide file tree
Showing 5 changed files with 202 additions and 19 deletions.
4 changes: 3 additions & 1 deletion src/plugins/validate-semantic/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import * as refsOAS3ValidateActions from "./validators/oas3/refs"
import * as refs2and3ValidateActions from "./validators/2and3/refs"
import * as parameters2and3ValidateActions from "./validators/2and3/parameters"
import * as paths2and3ValidateActions from "./validators/2and3/paths"
import * as schemas2and3ValidateActions from "./validators/2and3/schemas"

export default function SemanticValidatorsPlugin({getSystem}) {

Expand Down Expand Up @@ -57,7 +58,8 @@ export default function SemanticValidatorsPlugin({getSystem}) {
...operationsOAS3ValidateActions,
...refsOAS3ValidateActions,
...parameters2and3ValidateActions,
...paths2and3ValidateActions
...paths2and3ValidateActions,
...schemas2and3ValidateActions
}
},
}
Expand Down
4 changes: 4 additions & 0 deletions src/plugins/validate-semantic/selectors.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,10 @@ export const isOAS3RequestBody = (state, node) => (sys) => {
}

export const isParameterSchema = (state, node) => (sys) => {
if(sys.specSelectors.isOAS3 && sys.specSelectors.isOAS3()) {
// OAS3
return node.key === "schema" && sys.validateSelectors.isParameter(node.parent)
}
// parameter.x.in != body
if(sys.validateSelectors.isParameter(node) && node.node.in !== "body") {
return true
Expand Down
24 changes: 24 additions & 0 deletions src/plugins/validate-semantic/validators/2and3/schemas.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
export const validate2And3TypeArrayRequiresItems = () => (system) => {
return system.validateSelectors
.allSchemas()
.then(nodes => {
return nodes.reduce((acc, node) => {
const schemaObj = node.node
const { type, items } = schemaObj || {}
if(type === "array" && typeof items === "undefined") {
acc.push({
message: "Schemas with 'type: array', require a sibling 'items: ' field",
path: node.path,
level: "error",
})
} else if(type === "array" && (typeof items !== "object" || Array.isArray(items))) {
acc.push({
message: "`items` must be an object",
path: [...node.path, "items"],
level: "error",
})
}
return acc
}, [])
})
}
18 changes: 0 additions & 18 deletions src/plugins/validate-semantic/validators/schema.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,24 +33,6 @@ export const validateMinAndMax = () => (system) => {
})
}

export const validateTypeArrayRequiresItems = () => (system) => {
return system.validateSelectors
.allSchemas()
.then(nodes => {
return nodes.reduce((acc, node) => {
const schemaObj = node.node
if(schemaObj.type === "array" && typeof schemaObj.items === "undefined") {
acc.push({
message: "Schemas with 'type: array', require a sibling 'items: ' field",
path: node.path,
level: "error",
})
}
return acc
}, [])
})
}

export const validateTypeKeyShouldBeString = () => (system) => {
return system.validateSelectors
.allSchemas()
Expand Down
171 changes: 171 additions & 0 deletions test/plugins/validate-semantic/2and3/schemas.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
import expect from "expect"
import validateHelper from "../validate-helper.js"

describe(`validation plugin - semantic - 2and3 schemas`, () => {
describe(`array schemas must have an Object value in "items"`, () => {
it("should return an error for an array items value in Swagger 2", () => {
const spec = {
swagger: "2.0",
"paths": {
"/pets": {
"get": {
"parameters": [
{
name: "myParam",
in: "query",
type: "array",
items: [{ type: "object" }]
}
]
}
}
}
}

return validateHelper(spec)
.then(system => {
const allErrors = system.errSelectors.allErrors().toJS()
const firstError = allErrors[0]
expect(allErrors.length).toEqual(1)
expect(firstError.path).toEqual(["paths", "/pets", "get", "parameters", "0", "items"])
expect(firstError.message).toEqual("`items` must be an object")
})
})
it("should return an error for an array items value in OpenAPI 3", () => {
const spec = {
openapi: "3.0.0",
"paths": {
"/pets": {
"get": {
"parameters": [
{
name: "myParam",
in: "query",
schema: {
type: "array",
items: [{ type: "object" }]
}
},
]
}
}
}
}

return validateHelper(spec)
.then(system => {
const allErrors = system.errSelectors.allErrors().toJS()
const firstError = allErrors[0]
expect(allErrors.length).toEqual(1)
expect(firstError.path).toEqual(["paths", "/pets", "get", "parameters", "0", "schema", "items"])
expect(firstError.message).toEqual("`items` must be an object")
})
})
it("should return an error for a missing items value for an array schema in Swagger 2", () => {
const spec = {
swagger: "2.0",
"paths": {
"/pets": {
"get": {
"parameters": [
{
name: "myParam",
in: "query",
type: "array"
}
]
}
}
}
}

return validateHelper(spec)
.then(system => {
const allErrors = system.errSelectors.allErrors().toJS()
const firstError = allErrors[0]
expect(allErrors.length).toEqual(1)
expect(firstError.message).toEqual("Schemas with 'type: array', require a sibling 'items: ' field")
expect(firstError.path).toEqual(["paths", "/pets", "get", "parameters", "0"])
})
})
it("should return an error for a missing items value for an array schema in OpenAPI 3", () => {
const spec = {
openapi: "3.0.0",
"paths": {
"/pets": {
"get": {
"parameters": [
{
name: "myParam",
in: "query",
schema: {
type: "array"
}
},
]
}
}
}
}

return validateHelper(spec)
.then(system => {
const allErrors = system.errSelectors.allErrors().toJS()
const firstError = allErrors[0]
expect(allErrors.length).toEqual(1)
expect(firstError.path).toEqual(["paths", "/pets", "get", "parameters", "0", "schema"])
expect(firstError.message).toEqual("Schemas with 'type: array', require a sibling 'items: ' field")
})
})
it("should not return an error for a missing items value for a non-array schema in Swagger 2", () => {
const spec = {
swagger: "2.0",
"paths": {
"/pets": {
"get": {
"parameters": [
{
name: "myParam",
in: "query",
type: "object"
}
]
}
}
}
}

return validateHelper(spec)
.then(system => {
const allErrors = system.errSelectors.allErrors().toJS()
expect(allErrors.length).toEqual(0)
})
})
it("should not return an error for a missing items value for a non-array schema in OpenAPI 3", () => {
const spec = {
openapi: "3.0.0",
"paths": {
"/pets": {
"get": {
"parameters": [
{
name: "myParam",
in: "query",
schema: {
type: "object"
}
},
]
}
}
}
}

return validateHelper(spec)
.then(system => {
const allErrors = system.errSelectors.allErrors().toJS()
expect(allErrors.length).toEqual(0)
})
})
})
})

0 comments on commit 55059f9

Please sign in to comment.