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

Upgrade to OpenApi 3.0 gives issues with multipart/form-data #348

Open
vchellakudam opened this issue Jul 21, 2020 · 16 comments · May be fixed by #451
Open

Upgrade to OpenApi 3.0 gives issues with multipart/form-data #348

vchellakudam opened this issue Jul 21, 2020 · 16 comments · May be fixed by #451
Labels
Milestone

Comments

@vchellakudam
Copy link

Hi everyone!

While trying to upgrade my swagger definition from Swagger 2 to OpenApi 3.0, I found the following issue with multipart/form-data input parameter:

These params in my spec file:

consumes 'multipart/form-data'
produces 'application/json'
parameter name: :file, in: :formData, type: :file
parameter name: :kind, in: :formData, type: :string

is expected to generate (https://swagger.io/docs/specification/describing-request-body/multipart-requests):

"requestBody": {
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "properties": {
                  "file": {
                    "type": "string",
                    "format": "binary"
                  },
                  "kind": {
                    "type": "string"
                  }
                }
              }
            }
          }
        }

but it generates:

        "requestBody": {
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "string"
              }
            }
          }
        }

Even the test-app (https://github.com/rswag/rswag/blob/master/test-app/swagger/v1/swagger.json) generated similar swagger.json for /blogs/{id}/upload which goes against the OpenApi 3 multipart requests specification.

@oblakeerickson
Copy link
Member

What if you change in: :formData to in: :body?

@andrepcg
Copy link

andrepcg commented Sep 19, 2020

What if you change in: :formData to in: :body?

@oblakeerickson I'm having this issue as well and that does not fix it.

For me it always assumes the first parameter and uses it to populate requestBody.

i.e.

If my first parameter is parameter name: 'video', in: :body, type: :binary, required: true then that's what appears in the requestBody (no other parameter appears)

EDIT

It seems to be related to

schema_param = value.dig(:parameters)&.find { |p| (p[:in] == :body || p[:in] == :formData) && p[:schema] }

It only finds the first value from the parameters array to corresponds to that check

@andrepcg
Copy link

@BookOfGreg any idea?

@yao0204
Copy link

yao0204 commented Sep 24, 2020

if works for me if I write with schema

parameter name: :photo, in: :formData, schema: {
        type: :object,
        properties: {
          file: { type: :file },
          provider_tag: { type: :string },
        },
        required: %w[file provider_tag]
      }

The yml is generated correctly, but my spec does not pass, because it adds a photo params outside my params like this"photos" => { "file": ..., "provider_tag": ...}

And it seems that it's only like this when it is consumes "multipart/form-data", when it's "application/json", I have to put it in schema with a name too, but the name itself is not added to my parameters.

Any ideas?

@yao0204
Copy link

yao0204 commented Sep 24, 2020

By looking into the code, it seems that for construct the yml (for form_data and json), you want us to write out only once the in: :body params, and list the properties inside the schema

But when constructing the payload for the test, for json it's ok, you also consider it to be written out once, but for form_data, you consider it to be written out as many times as your params

def build_form_payload(parameters, example)

I think we should standardize the behavior, and simply change build_form_payload method to take only the first in: :Dataform params as well, and you should write out all your params as the properties in the schema.

form_param = parameters.find { |p| p[:in] == :formData }
example.send(body_param[:name])

What do you guys think? happy to open a PR if necessary

@vchellakudam
Copy link
Author

I am currently doing the workaround as @yao0204.

By adding a wrapper object attachment around file and kind:

      consumes 'multipart/form-data'
      produces 'application/json'
      parameter name: :attachment, in: :formData, schema: {
        type: :object,
        properties: {
          file: { type: :string, format: :binary},
          kind: { type: :string }
        }
      }

the expected json is generated:

        "requestBody": {
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "properties": {
                  "file": {
                    "type": "string",
                    "format": "binary"
                  },
                  "kind": {
                    "type": "string"
                  }
                }
              }
            }
          }
        }

But, this additional wrapper object in params is breaking the integration specs.

@jamie jamie added this to the Gem 2.4.0 milestone Oct 19, 2020
@henry40408
Copy link

I am currently doing the workaround as @yao0204.

By adding a wrapper object attachment around file and kind:

      consumes 'multipart/form-data'
      produces 'application/json'
      parameter name: :attachment, in: :formData, schema: {
        type: :object,
        properties: {
          file: { type: :string, format: :binary},
          kind: { type: :string }
        }
      }

the expected json is generated:

        "requestBody": {
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "properties": {
                  "file": {
                    "type": "string",
                    "format": "binary"
                  },
                  "kind": {
                    "type": "string"
                  }
                }
              }
            }
          }
        }

But, this additional wrapper object in params is breaking the integration specs.

this workaround works for me and my integration spec still pass

@oblakeerickson
Copy link
Member

I'm also running into issues documenting our uploads endpoint with multipart/form-data. I'm not able to get the tests to pass though with the above workarounds, so I'm just skipping this test for now with xit: #140 so that I can still get the documentation to generate.

This for sure will be on my list of things that will go into the 2.4.0 release.

What do you guys think? happy to open a PR if necessary

@yao0204 if you are still up for it, yes, a PR would very much be welcome here.

@zuccamia
Copy link

I ran into the same issue, but with application/json.
My params in spec file are:

parameter name: :user, in: :body, schema: {
  type: :object,
  properties: {
    email: { type: :string },
    password: { type: :string },
    birth_date: { type: :string }
  }
}

and in my controller file are defined as:

def user_params
  params.permit(:email, :password, :birth_date)
end

My integration test pass with this but request from Swagger returns unpermitted param: user. A user param is created to wrap my params like so"user" => { "email"=>..., "password"=>..., "birth_date":...}.
I then try changing def user_params in my controller file to params.require(:user).permit(:email, :password, :birth_date). But this breaks both integration test and Swagger request; my password param (which must be present in my user model) got filtered (for security) and went missing in the received params, returning just "user" => { "email"=>..., "birth_date":...} (for some reasons I don't understand yet...).

Downgrading to Swagger 2.0 solved it for me 🤔

@stale
Copy link

stale bot commented Jun 12, 2022

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. If the issue is still relevant to you, please leave a comment stating so to keep the issue from being closed. Thank you for your contributions.

@stale stale bot added the wontfix label Jun 12, 2022
@littleforest
Copy link

This is still an issue for me. I can either get the documentation to display correctly, but the tests won't pass, or I can write tests that pass but the documentation is incorrect.

@stale stale bot removed the wontfix label Jun 16, 2022
@agildav
Copy link

agildav commented Jun 19, 2022

Still an issue for me as well

@ngouy
Copy link
Contributor

ngouy commented Sep 1, 2022

Same here. we should have a fix for that...

@alookatommorow
Copy link

same, still an issue

@Bandes
Copy link

Bandes commented Sep 27, 2022

I am running into this as well

@mlh758 mlh758 linked a pull request Sep 28, 2022 that will close this issue
@hosamaly
Copy link

I think I found a workaround that works with rswag 2.8, although it's probably the most unusual code I've ever written!

Using a parameter whose name is the empty string, the test passes and the docs are generated correctly:

parameter name: :'', in: :formData, schema: {
  type: :object,
  properties: {
    file: { type: :file },
    kind: { type: :string },
  },
}

let(:'') { { file: a_file, kind: 'pdf' } }

@romanblanco romanblanco removed this from the Gem 2.X.0 milestone Apr 1, 2023
@romanblanco romanblanco added the bug label Apr 4, 2023
@romanblanco romanblanco linked a pull request Oct 31, 2023 that will close this issue
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.