defmodule OpenApiSpex.Plug.Cast do
@moduledoc """
Module plug that will cast the `Conn.params` and `Conn.body_params` according to the schemas defined for the operation.
Note that when using this plug, the body params are no longer merged into `Conn.params` and must be read from `Conn.body_params`
separately.
The operation_id can be given at compile time as an argument to `init`:
plug OpenApiSpex.Plug.Cast, operation_id: "MyApp.ShowUser"
For phoenix applications, the operation_id can be obtained at runtime automatically.
defmodule MyAppWeb.UserController do
use Phoenix.Controller
plug OpenApiSpex.Plug.Cast
...
end
If you want customize the error response, you can provide the `:render_error` option to register a plug which creates
a custom response in the case of a validation error.
## Example
defmodule MyAppWeb.UserController do
use Phoenix.Controller
plug OpenApiSpex.Plug.Cast,
render_error: MyApp.RenderError
...
end
defmodule MyApp.RenderError do
def init(opts), do: opts
def call(conn, reason) do
msg = %{error: reason} |> Poison.encode!()
conn
|> Conn.put_resp_content_type("application/json")
|> Conn.send_resp(400, msg)
end
end
"""
@behaviour Plug
alias OpenApiSpex.Cast.Utils
alias OpenApiSpex.Plug.PutApiSpec
@impl Plug
@deprecated "Use OpenApiSpex.Plug.CastAndValidate instead"
def init(opts) do
opts
|> Map.new()
|> Map.put_new(:render_error, OpenApiSpex.Plug.DefaultRenderError)
end
@impl Plug
@deprecated "Use OpenApiSpex.Plug.CastAndValidate instead"
def call(conn = %{private: %{open_api_spex: _private_data}}, %{
operation_id: operation_id,
render_error: render_error
}) do
{spec, operation_lookup} = PutApiSpec.get_spec_and_operation_lookup(conn)
operation = operation_lookup[operation_id]
content_type = Utils.content_type_from_header(conn)
# credo:disable-for-next-line
case apply(OpenApiSpex, :cast, [spec, operation, conn, content_type]) do
{:ok, conn} ->
conn
{:error, reason} ->
opts = render_error.init(reason)
conn
|> render_error.call(opts)
|> Plug.Conn.halt()
end
end
def call(
conn = %{
private: %{phoenix_controller: controller, phoenix_action: action, open_api_spex: _pd}
},
opts
) do
operation_id = controller.open_api_operation(action).operationId
if operation_id do
call(conn, Map.put(opts, :operation_id, operation_id))
else
raise "operationId was not found in action API spec"
end
end
def call(_conn = %{private: %{open_api_spex: _pd}}, _opts) do
raise ":operation_id was neither provided nor inferred from conn. Consider putting plug OpenApiSpex.Plug.Cast rather into your phoenix controller."
end
def call(_conn, _opts) do
raise ":open_api_spex was not found under :private. Maybe OpenApiSpex.Plug.PutApiSpec was not called before?"
end
end