Skip to content

multipart/form-data validation failing due to Plug.Conn.Query.decode #441

Open
@christmoore

Description

@christmoore

A little backstory - operations which contain a multipart/form-data request_body , which within themselves contain an array field don't get sent as arrays via SwaggerUI. curl is being generated without appending to the array field:

 operation(
.....
 request_body:
      {"Multipart form", "multipart/form-data",
       MyApp.Multipart, required: true},
....
def Multipart do
...
OpenApiSpex.schema(%{
      type: :object,
      properties: %{
        array_items: %Schema{
          description: """
          array of files
          """,
          type: :array,
          minItems: 1,
          items: %Schema{
            type: :string,
            format: :binary
          }
        }
      }
}

This generates the following Swagger UI:
image

Note the curl here. array_items needs to be array_items[] in order for Plug to create an array of Plug.Upload constructs. Without this, the conn object contains:

"array_items" => %Plug.Upload{
      content_type: "application/pdf",
      filename: "empty_pdf_2.pdf",
      path: "/var/folders/lx/8gvgf9jd24x6mcc11g747j500000gn/T//plug-1648/multipart-1648671114-838101121406784-2"
    }

This is a known issue with swagger. The solution has been to add brackets to the name of the fields so that curl interpolates currently. See https://stackoverflow.com/questions/53213067/swagger-ui-open-api-3-multipart-form-data-array-problem

Taking that curl from the example above and modifying to array_items[] creates the array as expected:

"array_items" => [
      %Plug.Upload{
        content_type: "application/pdf",
        filename: "empty_pdf_1.pdf",
        path: "/var/folders/lx/8gvgf9jd24x6mcc11g747j500000gn/T//plug-1648/multipart-1648671649-87151574074633-4"
      },
      %Plug.Upload{
        content_type: "application/pdf",
        filename: "empty_pdf_2.pdf",
        path: "/var/folders/lx/8gvgf9jd24x6mcc11g747j500000gn/T//plug-1648/multipart-1648671649-724027952677069-4"
      }
    ]

To resolve this, I've got a hacky solution that involves changing the field name to include brackets:

OpenApiSpex.schema(%{
      type: :object,
      properties: %{
        :"array_items[]": %Schema{
          description: """
          array of files
          """,
          type: :array,
          minItems: 1,
          items: %Schema{
            type: :string,
            format: :binary
          }
        }
      }
}

But downstream, validations will fail with required fields for :"array_items[]" due to the way this ends up getting interpolated. Plug.Conn.Query.decode cuts out brackets. If I end up just putting :array_items in the required field, I won't be able to submit my request in Swagger due to missing a required field (Swagger sees array_items[] not array_items)

OpenApiSpex.schema(%{
      type: :object,
      properties: %{
        :"array_items[]": %Schema{
          description: """
          array of files
          """,
          type: :array,
          minItems: 1,
          items: %Schema{
            type: :string,
            format: :binary
          }
        }
      },
    required: [:"array_items[]"]
}
..... open api request sent, request passes through MULTIPART parser, which runs through Plug.Conn.Query for decoding array_items[] to array_items list

params: %{
    "array_items" => [
      %Plug.Upload{
        content_type: "application/pdf",
        filename: "empty_pdf_1.pdf",
        path: "/var/folders/lx/8gvgf9jd24x6mcc11g747j500000gn/T//plug-1648/multipart-1648673751-336866617448758-3"
      },
      %Plug.Upload{
        content_type: "application/pdf",
        filename: "empty_pdf_2.pdf",
        path: "/var/folders/lx/8gvgf9jd24x6mcc11g747j500000gn/T//plug-1648/multipart-1648673751-11087878838975-3"
      }
    ]
  }
....
.... validation error
{
  "errors": [
    {
      "detail": "Missing field: array_items[]",
      "source": {
        "pointer": "/array_items[]"
      },
      "title": "Invalid value"
    }
  ]
}

Ideally validations follow the conventions for removing brackets on field names for multipart data. Ergo, if required: [:"array_items[]"] is set, on validation it will be checked against array_items


EDIT: It's Plug.Conn.Query.decode that cuts out brackets from field names.

iex(1)> Plug.Conn.Query.decode "a[]=1&a[]=2"
%{"a" => ["1", "2"]}
iex(2)> Plug.Conn.Query.encode %{"a" => ["1", "2"]}
"a[]=1&a[]=2"
iex(3)> Plug.Conn.Query.decode "a[1]=1&a[2]=2"
%{"a" => %{"1" => "1", "2" => "2"}}
iex(4)> Plug.Conn.Query.encode %{"a" => %{"1" => "1", "2" => "2"}}
"a[1]=1&a[2]=2"

https://hexdocs.pm/plug/Plug.Conn.Query.html#content

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions