Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(validateParam): validate JSON parameter values + support Parameter.content #5657

Merged
merged 4 commits into from
Oct 11, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions src/core/components/parameter-row.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export default class ParameterRow extends Component {
let enumValue

if(isOAS3) {
let schema = getParameterSchema(parameterWithMeta, { isOAS3 })
let { schema } = getParameterSchema(parameterWithMeta, { isOAS3 })
enumValue = schema.get("enum")
} else {
enumValue = parameterWithMeta ? parameterWithMeta.get("enum") : undefined
Expand Down Expand Up @@ -98,7 +98,7 @@ export default class ParameterRow extends Component {

const paramWithMeta = specSelectors.parameterWithMetaByIdentity(pathMethod, rawParam) || Map()

const schema = getParameterSchema(paramWithMeta, { isOAS3: specSelectors.isOAS3() })
const { schema } = getParameterSchema(paramWithMeta, { isOAS3: specSelectors.isOAS3() })

const parameterMediaType = paramWithMeta
.get("content", Map())
Expand Down Expand Up @@ -209,9 +209,10 @@ export default class ParameterRow extends Component {
const ExamplesSelectValueRetainer = getComponent("ExamplesSelectValueRetainer")
const Example = getComponent("Example")

let { schema } = getParameterSchema(param, { isOAS3 })
let paramWithMeta = specSelectors.parameterWithMetaByIdentity(pathMethod, rawParam) || Map()

let format = param.get("format")
let schema = getParameterSchema(param, { isOAS3 })
let type = schema.get("type")
let isFormData = inType === "formData"
let isFormDataSupported = "FormData" in win
Expand Down
35 changes: 20 additions & 15 deletions src/core/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -501,12 +501,14 @@ export const validatePattern = (val, rxPattern) => {
export const validateParam = (param, value, { isOAS3 = false, bypassRequiredCheck = false } = {}) => {

let errors = []
let required = param.get("required")

let paramDetails = getParameterSchema(param, { isOAS3 })
let paramRequired = param.get("required")

let { schema: paramDetails, parameterContentMediaType } = getParameterSchema(param, { isOAS3 })

if(!paramDetails) return errors

let required = paramDetails.get("required")
let maximum = paramDetails.get("maximum")
let minimum = paramDetails.get("minimum")
let type = paramDetails.get("type")
Expand All @@ -520,7 +522,7 @@ export const validateParam = (param, value, { isOAS3 = false, bypassRequiredChec
then we should do our validation routine.
Only bother validating the parameter if the type was specified.
*/
if ( type && (required || value) ) {
if ( type && (paramRequired || required || value) ) {
// These checks should evaluate to true if there is a parameter
let stringCheck = type === "string" && value
let arrayCheck = type === "array" && Array.isArray(value) && value.length
Expand All @@ -533,29 +535,32 @@ export const validateParam = (param, value, { isOAS3 = false, bypassRequiredChec
let objectCheck = type === "object" && typeof value === "object" && value !== null
let objectStringCheck = type === "object" && typeof value === "string" && value

// if(type === "object" && typeof value === "string") {
// // Disabled because `validateParam` doesn't consider the MediaType of the
// // `Parameter.content` hint correctly.
// try {
// JSON.parse(value)
// } catch(e) {
// errors.push("Parameter string value must be valid JSON")
// return errors
// }
// }

const allChecks = [
stringCheck, arrayCheck, arrayListCheck, arrayStringCheck, fileCheck,
booleanCheck, numberCheck, integerCheck, objectCheck, objectStringCheck,
]

const passedAnyCheck = allChecks.some(v => !!v)

if (required && !passedAnyCheck && !bypassRequiredCheck ) {
if ((paramRequired || required) && !passedAnyCheck && !bypassRequiredCheck ) {
errors.push("Required field is not provided")
return errors
}

if (
type === "object" &&
typeof value === "string" &&
(parameterContentMediaType === null ||
parameterContentMediaType === "application/json")
) {
try {
JSON.parse(value)
} catch (e) {
errors.push("Parameter string value must be valid JSON")
return errors
}
}

if (pattern) {
let err = validatePattern(value, pattern)
if (err) errors.push(err)
Expand Down
43 changes: 34 additions & 9 deletions src/helpers/get-parameter-schema.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@ const swagger2SchemaKeys = Im.Set.of(
"multipleOf"
)

/**
* @typedef {Object} ParameterSchemaDescriptor
* @property {Immutable.Map} schema - the parameter schema
* @property {string|null} parameterContentMediaType - the effective media type, for `content`-based OpenAPI 3.0 Parameters, or `null` otherwise
*/

/**
* Get the effective schema value for a parameter, or an empty Immutable.Map if
* no suitable schema can be found.
Expand All @@ -35,18 +41,29 @@ const swagger2SchemaKeys = Im.Set.of(
* @param {object} config
* @param {boolean} config.isOAS3 Whether the parameter is from an OpenAPI 2.0
* or OpenAPI 3.0 definition
* @return {Immutable.Map} The desired schema
* @return {ParameterSchemaDescriptor} Information about the parameter schema
*/
export default function getParameterSchema(parameter, { isOAS3 } = {}) {
// Return empty Map if `parameter` isn't a Map
if (!Im.Map.isMap(parameter)) return Im.Map()
if (!Im.Map.isMap(parameter)) {
return {
schema: Im.Map(),
parameterContentMediaType: null,
}
}

if (!isOAS3) {
// Swagger 2.0
if (parameter.get("in") === "body") {
return parameter.get("schema", Im.Map())
return {
schema: parameter.get("schema", Im.Map()),
parameterContentMediaType: null,
}
} else {
return parameter.filter((v, k) => swagger2SchemaKeys.includes(k))
return {
schema: parameter.filter((v, k) => swagger2SchemaKeys.includes(k)),
parameterContentMediaType: null,
}
}
}

Expand All @@ -57,11 +74,19 @@ export default function getParameterSchema(parameter, { isOAS3 } = {}) {
.get("content", Im.Map({}))
.keySeq()

return parameter.getIn(
["content", parameterContentMediaTypes.first(), "schema"],
Im.Map()
)
const parameterContentMediaType = parameterContentMediaTypes.first()

return {
schema: parameter.getIn(
["content", parameterContentMediaType, "schema"],
Im.Map()
),
parameterContentMediaType,
}
}

return parameter.get("schema", Im.Map())
return {
schema: parameter.get("schema", Im.Map()),
parameterContentMediaType: null,
}
}
20 changes: 13 additions & 7 deletions test/mocha/core/helpers/get-parameter-schema.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,22 @@
*/

import expect from "expect"
import Im, { fromJS } from "immutable"
import { fromJS } from "immutable"
import getParameterSchema from "../../../../src/helpers/get-parameter-schema"

describe("getParameterSchema", () => {
it("should return an empty Map when given no parameters", () => {
const result = getParameterSchema()

expect(result).toEqual(fromJS({}))
expect(result.schema.toJS()).toEqual({})
expect(result.parameterContentMediaType).toEqual(null)
})

it("should return an empty Map when given an empty Map", () => {
const result = getParameterSchema(fromJS({}))

expect(result).toEqual(fromJS({}))
expect(result.schema.toJS()).toEqual({})
expect(result.parameterContentMediaType).toEqual(null)
})

it("should return a schema for a Swagger 2.0 query parameter", () => {
Expand All @@ -34,12 +36,13 @@ describe("getParameterSchema", () => {
})
)

expect(result.toJS()).toEqual({
expect(result.schema.toJS()).toEqual({
type: "array",
items: {
type: "string",
},
})
expect(result.parameterContentMediaType).toEqual(null)
})

it("should return a schema for a Swagger 2.0 body parameter", () => {
Expand All @@ -58,12 +61,13 @@ describe("getParameterSchema", () => {
})
)

expect(result.toJS()).toEqual({
expect(result.schema.toJS()).toEqual({
type: "array",
items: {
type: "string",
},
})
expect(result.parameterContentMediaType).toEqual(null)
})

it("should return a schema for an OpenAPI 3.0 query parameter", () => {
Expand All @@ -87,12 +91,13 @@ describe("getParameterSchema", () => {
}
)

expect(result.toJS()).toEqual({
expect(result.schema.toJS()).toEqual({
type: "array",
items: {
type: "string",
},
})
expect(result.parameterContentMediaType).toEqual(null)
})

it("should return a schema for an OpenAPI 3.0 query parameter with `content`", () => {
Expand Down Expand Up @@ -126,7 +131,7 @@ describe("getParameterSchema", () => {
}
)

expect(result.toJS()).toEqual({
expect(result.schema.toJS()).toEqual({
type: "object",
required: ["lat", "long"],
properties: {
Expand All @@ -138,5 +143,6 @@ describe("getParameterSchema", () => {
},
},
})
expect(result.parameterContentMediaType).toEqual(`application/json`)
})
})
Loading