swagger-api/swagger-editor

View on GitHub
src/plugins/validate-semantic/validators/form-data.js

Summary

Maintainability
C
1 day
Test Coverage
import { SOURCE } from "../actions"
import { getRootNode } from "../helpers"
const operationKeys = ["get", "post", "put", "delete", "options", "head", "patch", "trace"]

export const validateParameterFormDataCaseTypo = () => system => {
  return system.validateSelectors
    .allParameters()
    .then(nodes => {
      return nodes.reduce((acc, node) => {
        const value = node.node

        if(
          value.in &&
          typeof value.in === "string" &&
          value.in.toLowerCase() === "formdata" &&
          value.in !== "formData"
        ) {
          acc.push({
            message: `Parameter "in: ${value.in}" is invalid, did you mean "in: formData"?`,
            path: [...node.path],
            level: "error",
            source: SOURCE
          })
        }
        return acc
      }, [])
    })
}

export const validateParameterFormDataForFileTypes = () => system => {
  return system.validateSelectors
    .allParameters()
    .then(nodes => {
      return nodes.reduce((acc, node) => {
        const value = node.node

        if(value.type === "file" && value.in !== "formData") {
          acc.push({
            message: `Parameters with "type: file" must have "in: formData"`,
            path: [...node.path],
            level: "error",
            source: SOURCE
          })
        }
        return acc
      }, [])
    })
}

export const validateParameterFormDataConsumesType = () => system => {
  return system.validateSelectors
    .allPathItems()
    .then(nodes => {
      return nodes.reduce((acc, node) => {
        const pathItemValue = node.node
        const globalConsumes = getRootNode(node).node.consumes
        const pathItemParameters = pathItemValue.parameters

        const hasPathItemFormDataParameter = pathItemParameters != null && pathItemParameters.find(parameter => parameter.in === "formData")
        const hasPathItemFileParameter = pathItemParameters != null && pathItemParameters.find(parameter => parameter.type === "file")

        for (const method of operationKeys) {
          const operationValue = pathItemValue[method]

          if (operationValue) {
            const effectiveConsumes = operationValue.consumes || globalConsumes || []
            const operationParameters = operationValue.parameters || []
            const hasOperationFormDataParameter = operationParameters.find(parameter => parameter.in === "formData")
            const hasOperationFileParameter = operationParameters.find(parameter => parameter.type === "file")

            if(hasPathItemFileParameter || hasOperationFileParameter){
              if (!effectiveConsumes.includes("multipart/form-data")) {
                acc.push({
                  message: `Operations with parameters of "type: file" must include "multipart/form-data" in their "consumes" property`,
                  path: [...node.path, method],
                  level: "error",
                  source: SOURCE
                })
              }
            } else if (hasPathItemFormDataParameter || hasOperationFormDataParameter) {
              if (!effectiveConsumes.includes("application/x-www-form-urlencoded") && !effectiveConsumes.includes("multipart/form-data")) {
                acc.push({
                  message: `Operations with parameters of "in: formData" must include "application/x-www-form-urlencoded" or "multipart/form-data" in their "consumes" property`,
                  path: [...node.path, method],
                  level: "error",
                  source: SOURCE
                })
              }
            }
          }
        }

        return acc
      }, [])
    })
}

export const validateParameterArraysDontContainBodyAndFormData = () => system => {
  return system.validateSelectors
    .allParameterArrays()
    .then(paramArrays => {
      return paramArrays.reduce((acc, node) => {
        const bodyParams = node.node.filter(param => param.in === "body")
        const formDataParams = node.node.filter(param => param.in === "formData")

        if(bodyParams.length && formDataParams.length) {
          acc.push({
            message: `Parameters cannot have both a "in: body" and "in: formData", as "formData" _will_ be the body`,
            path: [...node.path],
            level: "error",
            source: SOURCE
          })
        }
        return acc
      }, [])
    })
}