Page MenuHomePhorge

No OneTemporary

Size
6 KB
Referenced Files
None
Subscribers
None
diff --git a/examples/phoenix_app/lib/phoenix_app_web/controllers/user_controller.ex b/examples/phoenix_app/lib/phoenix_app_web/controllers/user_controller.ex
index 0446588..2809b0a 100644
--- a/examples/phoenix_app/lib/phoenix_app_web/controllers/user_controller.ex
+++ b/examples/phoenix_app/lib/phoenix_app_web/controllers/user_controller.ex
@@ -1,83 +1,83 @@
defmodule PhoenixAppWeb.UserController do
use PhoenixAppWeb, :controller
import OpenApiSpex.Operation, only: [parameter: 5, request_body: 4, response: 3]
alias OpenApiSpex.Operation
alias PhoenixApp.{Accounts, Accounts.User}
alias PhoenixAppWeb.Schemas
plug OpenApiSpex.Plug.CastAndValidate
def open_api_operation(action) do
apply(__MODULE__, :"#{action}_operation", [])
end
def index_operation() do
%Operation{
tags: ["users"],
summary: "List users",
description: "List all useres",
operationId: "UserController.index",
responses: %{
200 => response("User List Response", "application/json", Schemas.UsersResponse)
}
}
end
def index(conn, _params) do
users = Accounts.list_users()
render(conn, "index.json", users: users)
end
def create_operation() do
%Operation{
tags: ["users"],
summary: "Create user",
description: "Create a user",
operationId: "UserController.create",
parameters: [
Operation.parameter(:group_id, :path, :integer, "Group ID", example: 1)
],
requestBody:
request_body("The user attributes", "application/json", Schemas.UserRequest,
required: true
),
responses: %{
201 => response("User", "application/json", Schemas.UserResponse)
}
}
end
def create(conn = %{body_params: %Schemas.UserRequest{user: user_params}}, %{
group_id: _group_id
}) do
with {:ok, %User{} = user} <- Accounts.create_user(user_params) do
conn
|> put_status(:created)
|> put_resp_header("location", user_path(conn, :show, user))
|> render("show.json", user: user)
end
end
@doc """
API Spec for :show action
"""
def show_operation() do
%Operation{
tags: ["users"],
summary: "Show user",
description: "Show a user by ID",
operationId: "UserController.show",
parameters: [
- parameter(:id, :path, :integer, "User ID", example: 123, minimum: 1, required: true)
+ parameter(:id, :path, :integer, "User ID", example: 123, required: true)
],
responses: %{
200 => response("User", "application/json", Schemas.UserResponse)
}
}
end
def show(conn, %{id: id}) do
user = Accounts.get_user!(id)
render(conn, "show.json", user: user)
end
end
diff --git a/lib/open_api_spex/plug/cast_and_validate.ex b/lib/open_api_spex/plug/cast_and_validate.ex
index bc1375c..287e345 100644
--- a/lib/open_api_spex/plug/cast_and_validate.ex
+++ b/lib/open_api_spex/plug/cast_and_validate.ex
@@ -1,126 +1,131 @@
defmodule OpenApiSpex.Plug.CastAndValidate do
@moduledoc """
Module plug that will cast and validate the `Conn.params` and `Conn.body_params` according to the schemas defined for the operation.
The operation_id can be given at compile time as an argument to `init`:
plug OpenApiSpex.Plug.CastAndValidate, 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.CastAndValidate
...
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.CastAndValidate, render_error: MyApp.RenderError
...
end
defmodule MyApp.RenderError do
def init(opts), do: opts
def call(conn, reason) do
msg = Jason.encode!(%{error: reason})
conn
|> Conn.put_resp_content_type("application/json")
|> Conn.send_resp(400, msg)
end
end
"""
@behaviour Plug
alias Plug.Conn
@impl Plug
def init(opts) do
opts
|> Map.new()
|> Map.put_new(:render_error, OpenApiSpex.Plug.JsonRenderError)
end
@impl Plug
def call(conn = %{private: %{open_api_spex: private_data}}, %{
operation_id: operation_id,
render_error: render_error
}) do
spec = private_data.spec
operation = private_data.operation_lookup[operation_id]
content_type =
- Conn.get_req_header(conn, "content-type")
- |> Enum.at(0, "")
- |> String.split(";")
- |> Enum.at(0)
+ case Conn.get_req_header(conn, "content-type") do
+ [header_value | _] ->
+ header_value
+ |> String.split(";")
+ |> Enum.at(0)
+
+ _ ->
+ nil
+ end
private_data = Map.put(private_data, :operation_id, operation_id)
conn = Conn.put_private(conn, :open_api_spex, private_data)
with {:ok, conn} <- OpenApiSpex.cast_and_validate(spec, operation, conn, content_type) do
conn
else
{: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: private_data
}
},
opts
) do
operation =
case private_data.operation_lookup[{controller, action}] do
nil ->
operationId = controller.open_api_operation(action).operationId
operation = private_data.operation_lookup[operationId]
operation_lookup =
private_data.operation_lookup
|> Map.put({controller, action}, operation)
OpenApiSpex.Plug.Cache.adapter().put(
private_data.spec_module,
{private_data.spec, operation_lookup}
)
operation
operation ->
operation
end
if operation.operationId do
call(conn, Map.put(opts, :operation_id, operation.operationId))
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

File Metadata

Mime Type
text/x-diff
Expires
Mon, Nov 25, 4:11 AM (1 d, 12 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
39573
Default Alt Text
(6 KB)

Event Timeline