Open superruzafa opened 3 years ago
@superruzafa Same here, looks like the root cause is the same as #92. OpenApiSpex is changing the phoenix params and this causes issues with anything expecting them to be there.
Looks like when we CastAndValidate
a schema with type: :string, format: :binary
it should allow a %Plug.Upload{}
struct to be passed through.
https://swagger.io/docs/specification/describing-request-body/file-upload/
Hey, coming across this a year+ later. Is it possible that the OpenApiSpex.Cast.OneOf
is not handling it correctly. I'm pretty sure I have my schema correct, but when I don't use a oneOf, it works, but otherwise I get an "Enum. protocol not implemented for Plug.Upload" error.
image: %Schema{
oneOf: [%Schema{type: :string, format: :binary}, Base64ObjectImageUpload]
}
I am also using a MediaType
with a custom override encoding:
content: %{
"multipart/form-data" => %OpenApiSpex.MediaType{
schema: __MODULE__,
encoding: %{"image" => %OpenApiSpex.Encoding{contentType: "image/png"}}
},
"application/json" => %OpenApiSpex.MediaType{schema: __MODULE__}
},
Does this seem likely, or more likely that I just messed up my oneOf
😅
@petermueller can you test #455 and see if it solves your issue?
@lucacorti, can do 👍🏻 I'll report back in the morning
Unfortunately no. Same error :-/
(EDIT: This was after I blew away the deps and _build just in case)
[error] #PID<0.4803.0> running MyApp.Endpoint (connection #PID<0.4802.0>, stream id 1) terminated
Server: local.myapp.com:80 (http)
Request: PUT /api/v2/my_resource/55/
** (exit) an exception was raised:
** (Protocol.UndefinedError) protocol Enumerable not implemented for %Plug.Upload{content_type: "image/png", filename: "C84A8E27-EEE4-4439-8007-4AC937E9AD93.png", path: "/var/folders/wh/s4xndcwj56vdh19q0cd9628w0000gn/T/plug-1664/multipart-1664895328-565347161995327-1"} of type Plug.Upload (a struct)
(elixir 1.13.4) lib/enum.ex:1: Enumerable.impl_for!/1
(elixir 1.13.4) lib/enum.ex:143: Enumerable.reduce/3
(elixir 1.13.4) lib/enum.ex:4144: Enum.reduce/3
(open_api_spex 3.13.0) lib/open_api_spex/cast/object.ex:150: OpenApiSpex.Cast.Object.get_additional_properties/2
(open_api_spex 3.13.0) lib/open_api_spex/cast/object.ex:132: OpenApiSpex.Cast.Object.cast_additional_properties/2
(open_api_spex 3.13.0) lib/open_api_spex/cast/object.ex:24: OpenApiSpex.Cast.Object.cast/1
(open_api_spex 3.13.0) lib/open_api_spex/cast/one_of.ex:17: anonymous fn/2 in OpenApiSpex.Cast.OneOf.cast/1
(elixir 1.13.4) lib/enum.ex:2396: Enum."-reduce/3-lists^foldl/2-0-"/3
(open_api_spex 3.13.0) lib/open_api_spex/cast/one_of.ex:13: OpenApiSpex.Cast.OneOf.cast/1
(open_api_spex 3.13.0) lib/open_api_spex/cast/object.ex:170: OpenApiSpex.Cast.Object.cast_property/2
(open_api_spex 3.13.0) lib/open_api_spex/cast/object.ex:113: anonymous fn/4 in OpenApiSpex.Cast.Object.cast_properties/1
(stdlib 4.0.1) maps.erl:411: :maps.fold_1/3
(open_api_spex 3.13.0) lib/open_api_spex/cast/object.ex:112: OpenApiSpex.Cast.Object.cast_properties/1
(open_api_spex 3.13.0) lib/open_api_spex/cast/object.ex:28: OpenApiSpex.Cast.Object.cast/1
(open_api_spex 3.13.0) lib/open_api_spex/operation2.ex:37: OpenApiSpex.Operation2.cast/5
(open_api_spex 3.13.0) lib/open_api_spex/plug/cast_and_validate.ex:82: OpenApiSpex.Plug.CastAndValidate.call/2
(myapp 0.1.0) lib/my_app/controllers/my_resource_controller.ex:1: MyApp.MyResourceController.phoenix_controller_pipeline/2
(phoenix 1.6.11) lib/phoenix/router.ex:354: Phoenix.Router.__call__/2
(myapp 0.1.0) lib/my_app/endpoint.ex:1: MyApp.Endpoint.plug_builder_call/2
(myapp 0.1.0) lib/my_app/endpoint.ex:1: MyApp.Endpoint."call (overridable 3)"/2
operation :update,
summary: "My Description",
parameters: [MyApp.Schemas.PathParameters.id_param(:id, "My Resource ID")],
request_body: UpdateMyResourceRequest.request_body()
def update(conn, %{id: id}) do
# ...
defmodule MyApp.Schemas.UpdateMyResourceRequest. do
@moduledoc """
The `UpdateMyResourceRequest` `OpenApiSpex` Schema
"""
alias OpenApiSpex.Schema
require OpenApiSpex
OpenApiSpex.schema(%{
description: "Updates my resources",
type: :object,
properties: %{
thing_a: %Schema{
type: :string,
description: "thing_a"
},
thing_b: %Schema{
type: :string,
description: "thing_b"
},
thing_c_dt: %Schema{
type: :string,
description: "thing_c_dt",
format: :"date-time",
nullable: false
},
thing_d_enum: %Schema{
type: :string,
enum: [:asdf, :qwer]
},
# image: %Schema{type: :string, format: :binary}
image: %Schema{
oneOf: [%Schema{type: :string, format: :binary}, Base64ObjectImageUpload]
}
},
example: %{
"thing_a" => "asdf",
"thing_c_dt" => "2021-01-30 15:00:00-04:00",
"image" => "thisistheimagebinary"
}
})
def request_body do
%OpenApiSpex.RequestBody{
description: "Update My Resource",
content: %{
"multipart/form-data" => %OpenApiSpex.MediaType{
schema: __MODULE__,
encoding: %{"image" => %OpenApiSpex.Encoding{contentType: "image/png"}}
},
"application/json" => %OpenApiSpex.MediaType{schema: __MODULE__}
},
required: true
}
end
end
Hi.
This is more a question than an issue.
In the Phoenix framework, when doing a file upload I used to fetch the
%Plug.Upload
structure from the 2nd parameter passed to the controller's action:I created a spec in order to allow file uploads as noted in similar issues:
After calling
OpenApiSpex.cast_and_validate/4
I noticed that the upload is deleted from the 2nd parameter passed to the controller.I also check that the upload can be accessed from the conn's
body_params
field, but my question is: is there any way to preserve it in the controller's 2nd parameter?Thanks.