swagger-api/swagger-editor

View on GitHub
src/plugins/validate-semantic/validators/oas3/refs.js

Summary

Maintainability
F
4 days
Test Coverage
import { escapeJsonPointerToken } from "../../../refs-util"

export const validateOAS3RefsForRequestBodiesReferenceRequestBodyPositions = () => sys => {
  return sys.validateSelectors
    .allOAS3RequestBodies()
    .then(nodes => {
      return nodes.reduce((acc, node) => {
        const value = node.node
        const ref = value.$ref

        if (!ref) {
          return acc
        }

        const [refPath = ""] = ref.split("#")
        const pathArr = refPath.split("/") || []

        // Ignore external references
        if (ref.startsWith("#/")) {
          // Local cases
          if (refPath.endsWith("requestBody") && (refPath.startsWith("/paths") || refPath.startsWith("/components"))){
            return acc
          }

          // Starting with #/compontents/schemas is not allowed
          if (ref.startsWith("#/components/schemas")) {
            acc.push({
              level: "error",
              message: `requestBody $refs cannot point to '#/components/schemas/…', they must point to '#/components/requestBodies/…'`,
              path: [...node.path, "$ref"]
            })
          } else
          if (ref.startsWith("#/components") && !ref.startsWith("#/components/requestBodies/")) {
            acc.push({
              level: "error",
              message: `requestBody $refs must point to a position where a requestBody can be legally placed`,
              path: [...node.path, "$ref"]
            })
          }

          // Extensions are valid
          if (ref.startsWith("#/") && pathArr.some(element => element.startsWith("x-"))){
            return acc
          }
        }
        return acc

      }, [])
    })
}

export const validateOAS3RequestBodyRefsReferenceAllowableSchemaPositions = () => sys => {
  return sys.validateSelectors
    .allOAS3RequestBodySchemas()
    .then(nodes => {
      return nodes.reduce((acc, node) => {
        const value = node.node
        const ref = value.$ref

        if(!ref) {
          return acc
        }

        const [, refPath = ""] = ref.split("#")
        const pathArr = refPath.split("/") || []
        const parentRefKey = pathArr.slice(-2)[0]
        const targetRefKey = pathArr.slice(-1)[0]
        if(
          targetRefKey !== "schema"
          && parentRefKey !== "schemas"
          && ref.startsWith("#/")
        ) {
          acc.push({
            level: "error",
            message: `requestBody schema $refs must point to a position where a Schema Object can be legally placed`,
            path: [...node.path, "$ref"]
          })
        }
        return acc
      }, [])
    })
}

export const validateOAS3ParameterRefsReferenceParameterPositions = () => sys => {
  return sys.validateSelectors
    .allParameters()
    .then(nodes => {
      return nodes.reduce((acc, node) => {
        const value = node.node
        const ref = value.$ref

        if(!ref) {
          return acc
        }

        /**
         * We only do the check for local JSON Pointers. In order to check remote JSON Pointer
         * we would have to resolve it and assert for the shape of the referenced structure.
         */
        if (ref.startsWith("#/")) {
          const foundParameter = nodes.find((node) => {
            const parameterPointer = `#/${node.path.map(escapeJsonPointerToken).join("/")}`
            return parameterPointer === ref
          })

          if (typeof foundParameter === "undefined") {
            acc.push({
              level: "error",
              message: `OAS3 parameter $refs should point to Parameter Object and not ${ref}`,
              path: [...node.path, "$ref"]
            })
          }
        }

        return acc
      }, [])
    })
}

export const validateOAS3RefsForHeadersReferenceHeadersPositions = () => sys => {
  return sys.validateSelectors
    .allHeaders()
    .then(nodes => {
      return nodes.reduce((acc, node) => {
        const value = node.node
        const ref = value.$ref

        if(!ref) {
          return acc
        }

        /**
         * We only do the check for local JSON Pointers. In order to check remote JSON Pointer
         * we would have to resolve it and assert for the shape of the referenced structure.
         */
        if (ref.startsWith("#/")) {
          const foundHeader = nodes.find((node) => {
            const headerPointer = `#/${node.path.map(escapeJsonPointerToken).join("/")}`
            return headerPointer === ref
          })

          if (typeof foundHeader === "undefined") {
            acc.push({
              level: "error",
              message: `OAS3 header $refs should point to Header Object and not ${ref}`,
              path: [...node.path, "$ref"]
            })
          }
        }

        return acc
      }, [])
    })
}