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

openapi v3 -style form data not being coerced #710

Closed
chadxz opened this issue Feb 25, 2021 · 4 comments
Closed

openapi v3 -style form data not being coerced #710

chadxz opened this issue Feb 25, 2021 · 4 comments

Comments

@chadxz
Copy link
Contributor

chadxz commented Feb 25, 2021

In openapi v3, form data is now expressed as a part of the requestBody and not as parameters. As a result, any form data that is declared as a non-string type is not being coerced by openapi-framework.

I've traced the issue to this: https://github.com/kogosoftwarellc/open-api/blob/master/packages/openapi-framework/index.ts#L507-L512

namely, the coercer for my form data route is being constructed with only the parameters and not the requestBody.

Am I missing something or is this functionality currently missing and needs to be added?

chadxz added a commit to chadxz/open-api that referenced this issue Feb 25, 2021
In OpenAPI v3, formData requests are specified using the
'application/x-www-form-urlencoded' content-type inside the
`requestBody` section of the operation. This commit adds support for
coercing those formData parameters.

Related to kogosoftwarellc#710.
chadxz added a commit to chadxz/open-api that referenced this issue Feb 25, 2021
In OpenAPIv3, formData is specified as a part of the `requestBody` field
of the operation. This commit passes the `requestBody` to the coercer so
that it can pull the formData parameters out and coerce them.

Related to kogosoftwarellc#710.
chadxz added a commit to chadxz/open-api that referenced this issue Feb 25, 2021
In OpenAPI v3, formData requests are specified using the
'application/x-www-form-urlencoded' content-type inside the
`requestBody` section of the operation. This commit adds support for
coercing those formData parameters.

Related to kogosoftwarellc#710.
chadxz added a commit to chadxz/open-api that referenced this issue Feb 25, 2021
In OpenAPIv3, formData is specified as a part of the `requestBody` field
of the operation. This commit passes the `requestBody` to the coercer so
that it can pull the formData parameters out and coerce them.

Related to kogosoftwarellc#710.
@chadxz
Copy link
Contributor Author

chadxz commented Feb 25, 2021

Pushed a PR #711 for this. Will probably need some mentorship on it though.

chadxz added a commit to chadxz/open-api that referenced this issue Feb 26, 2021
chadxz added a commit to chadxz/open-api that referenced this issue Feb 26, 2021
chadxz added a commit to chadxz/open-api that referenced this issue Feb 26, 2021
@Envek
Copy link
Contributor

Envek commented Mar 1, 2021

+1 on this issue! I've faced it too. I will describe it in more details (just in case it is actually different one maybe)


So, given valid Open API 3 spec file with multipart/form-data request, declared according to Describing Request Body / File Uploads docs:

apiSchema.yml with multipart/form-data request body
openapi: 3.0.3
info:
  title: Sample Application API
  version: v1
paths:
  /fileAttachments:
    post:
      requestBody:
        content:
          multipart/form-data:
            schema:
              type: object
              properties:
                someId:
                  type: string
                  nullable: false
                status:
                  type: string
                  nullable: true
                file:
                  type: string
                  format: binary
                  nullable: false
      responses:
        204:
          description: Success

Expected result: openapi-request-validator validates request and doesn't pass requests with file or someId missing
Actual result: openapi-request-validator passes requests with missing non-nullable fields


Then I went to openapi-request-validator source and found that it expects for multipart requests that all input fields should be declared as input formdata parameters instead of request body (see this test case)

So I tried to change schema in a way that openapi-request-validator should understand: replaced request body with parameters

apiSchema.yml with formData parameters
openapi: 3.0.3
info:
  title: Sample Application API
  version: v1
paths:
  /fileAttachments:
    post:
      parameters:
        - name: someId
          in: formData
          required: true
          schema:
            type: string
        - name: status
          in: formData
          required: false
          schema:
            type: string
        - name: file
          in: formData
          required: true
          schema:
            type: string
            format: binary
      responses:
        204:
          description: Success

And success! Now it is being validated as expected.

But Swagger Editor tells me that this schema is now invalid:

Structural error at paths./fileAttachments.post.parameters.0.in
should be equal to one of the allowed values
allowedValues: path, query, header, cookie

(and 2 more errors)

Workaround: preprocess schema, converting multipart bodies into formData parameters on schema load with code like this.

Some hacky TypeScript code
import yaml from "js-yaml"
import { readFileSync } from "fs"
import $RefParser from "@apidevtools/json-schema-ref-parser"
import { OpenAPIV3 } from "openapi-types"

type OpenApiHttpMethods = "get" | "post" | "put" | "delete" | "options" | "head" | "patch" | "trace"

const apiSpec = <OpenAPIV3.Document>yaml.safeLoad(readFileSync("./apiSchema.yaml").toString())

async function preprocessOpenapiSpec(apiSpec: OpenAPIV3.Document): Promise<OpenAPIV3.Document> {
  const schema = <OpenAPIV3.Document>await $RefParser.dereference(apiSpec)

  // Replace multipart request body with formData parameters for validation
  for (const methods of Object.values(schema.paths)) {
    for (const method of ["get", "post", "put", "delete", "options", "head", "patch", "trace"]) {
      const methodOptions = methods[<OpenApiHttpMethods>method]
      if (!methodOptions) continue

      const reqBody = <OpenAPIV3.RequestBodyObject | undefined>methodOptions?.requestBody
      if (reqBody?.content && "multipart/form-data" in reqBody.content) {
        const props = (<OpenAPIV3.SchemaObject>reqBody.content["multipart/form-data"].schema)?.properties
        if (!props) continue

        methodOptions.parameters ||= []

        for (const [name, subschema] of Object.entries(props)) {
          const required = ("nullable" in subschema) ? !subschema.nullable : false
          methodOptions.parameters.push({ name, in: "formData", required, schema: subschema })
        }

        delete methodOptions?.requestBody
      }
    }
  }

  return schema
}

@chadxz
Copy link
Contributor Author

chadxz commented Mar 1, 2021 via email

jsdevel pushed a commit that referenced this issue Mar 2, 2021
* openapi-request-coercer: Support oapiv3 formdata

In OpenAPI v3, formData requests are specified using the
'application/x-www-form-urlencoded' content-type inside the
`requestBody` section of the operation. This commit adds support for
coercing those formData parameters.

Related to #710.

* openapi-framework: Pass requestBody to coercer

In OpenAPIv3, formData is specified as a part of the `requestBody` field
of the operation. This commit passes the `requestBody` to the coercer so
that it can pull the formData parameters out and coerce them.

Related to #710.

* Add express/koa tests for v3 formdata coercion

Related to #710.
@chadxz
Copy link
Contributor Author

chadxz commented Mar 2, 2021

Closing this, merged PR fixed the issue I was experiencing. @Envek feel free to open a new issue for your specific problem

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants