Page MenuHomePhorge

No OneTemporary

Size
8 KB
Referenced Files
None
Subscribers
None
diff --git a/lib/open_api_spex/operation2.ex b/lib/open_api_spex/operation2.ex
index 2258b38..16b33d6 100644
--- a/lib/open_api_spex/operation2.ex
+++ b/lib/open_api_spex/operation2.ex
@@ -1,100 +1,98 @@
defmodule OpenApiSpex.Operation2 do
@moduledoc """
Defines the `OpenApiSpex.Operation.t` type.
"""
alias OpenApiSpex.{
Cast,
Operation,
Parameter,
RequestBody,
Schema
}
+ alias Plug.Conn
+
alias OpenApiSpex.Cast.Error
- def cast(operation = %Operation{}, conn = %Plug.Conn{}, content_type, schemas) do
+ @spec cast(Operation.t(), Conn.t(), String.t() | nil, Schema.schemas()) ::
+ {:error, [Error.t()]} | {:ok, Conn.t()}
+ def cast(operation = %Operation{}, conn = %Conn{}, content_type, schemas) do
with {:ok, conn} <- cast_query_parameters(conn, operation, schemas),
{:ok, body} <-
cast_request_body(operation.requestBody, conn.body_params, content_type, schemas) do
{:ok, %{conn | body_params: body}}
end
end
+ ## Private functions
+
defp cast_query_parameters(conn, operation, schemas) do
parameters =
Enum.filter(operation.parameters || [], fn p ->
Map.has_key?(conn.params, Atom.to_string(p.name))
end)
with :ok <- check_query_params_defined(conn, operation.parameters),
{:ok, parameter_values} <- cast_parameters(parameters, conn.params, schemas),
conn = %{conn | params: parameter_values},
parameters =
Enum.filter(operation.parameters || [], &Map.has_key?(conn.params, &1.name)),
:ok <- validate_parameter_schemas(parameters, conn.params, schemas) do
{:ok, %{conn | params: parameter_values}}
end
end
- @spec check_query_params_defined(Conn.t(), list | nil) :: :ok | {:error, String.t()}
- defp check_query_params_defined(%Plug.Conn{}, nil = _defined_params) do
+ defp check_query_params_defined(%Conn{}, nil = _defined_params) do
:ok
end
- defp check_query_params_defined(%Plug.Conn{} = conn, defined_params)
+ defp check_query_params_defined(%Conn{} = conn, defined_params)
when is_list(defined_params) do
defined_query_params =
for param <- defined_params,
param.in == :query,
into: MapSet.new(),
do: to_string(param.name)
case validate_parameter_keys(Map.keys(conn.query_params), defined_query_params) do
{:error, name} -> {:error, [%Error{reason: :unexpected_field, name: name}]}
:ok -> :ok
end
end
- @spec validate_parameter_keys([String.t()], MapSet.t()) :: {:error, String.t()} | :ok
defp validate_parameter_keys([], _defined_params), do: :ok
defp validate_parameter_keys([name | params], defined_params) do
case MapSet.member?(defined_params, name) do
false -> {:error, name}
_ -> validate_parameter_keys(params, defined_params)
end
end
- @spec cast_parameters([Parameter.t()], map, Schema.schemas()) ::
- {:ok, map} | {:error, [Error.t()]}
defp cast_parameters([], _params, _schemas), do: {:ok, %{}}
defp cast_parameters([p | rest], params = %{}, schemas) do
with {:ok, cast_val} <-
Cast.cast(Parameter.schema(p), params[Atom.to_string(p.name)], schemas),
{:ok, cast_tail} <- cast_parameters(rest, params, schemas) do
{:ok, Map.put_new(cast_tail, p.name, cast_val)}
end
end
- @spec cast_request_body(RequestBody.t() | nil, map, String.t() | nil, Schema.schemas()) ::
- {:ok, map} | {:error, String.t()}
- defp cast_request_body(nil, _, _, _), do: {:ok, %{}}
-
- defp cast_request_body(%RequestBody{content: content}, params, content_type, schemas) do
- schema = content[content_type].schema
- Cast.cast(schema, params, schemas)
- end
-
- @spec validate_parameter_schemas([Parameter.t()], map, %{String.t() => Schema.t()}) ::
- :ok | {:error, String.t()}
defp validate_parameter_schemas([], %{} = _params, _schemas), do: :ok
defp validate_parameter_schemas([p | rest], %{} = params, schemas) do
{:ok, parameter_value} = Map.fetch(params, p.name)
with {:ok, _value} <- Cast.cast(Parameter.schema(p), parameter_value, schemas) do
validate_parameter_schemas(rest, params, schemas)
end
end
+
+ defp cast_request_body(nil, _, _, _), do: {:ok, %{}}
+
+ defp cast_request_body(%RequestBody{content: content}, params, content_type, schemas) do
+ schema = content[content_type].schema
+ Cast.cast(schema, params, schemas)
+ end
end
diff --git a/test/operation2_test.exs b/test/operation2_test.exs
index 614fcc3..729e263 100644
--- a/test/operation2_test.exs
+++ b/test/operation2_test.exs
@@ -1,145 +1,145 @@
defmodule OpenApiSpex.Operation2Test do
use ExUnit.Case
alias OpenApiSpex.{Operation, Operation2, Schema}
alias OpenApiSpex.Cast.Error
defmodule SchemaFixtures do
@user %Schema{
type: :object,
properties: %{
user: %Schema{
type: :object,
properties: %{
email: %Schema{type: :string}
}
}
}
}
@user_list %Schema{
type: :array,
items: @user
}
@schemas %{"User" => @user, "UserList" => @user_list}
def user, do: @user
def user_list, do: @user_list
def schemas, do: @schemas
end
defmodule OperationFixtures do
@user_index %Operation{
operationId: "UserController.index",
parameters: [
Operation.parameter(:name, :query, :string, "Filter by user name")
],
responses: %{
200 => Operation.response("User", "application/json", SchemaFixtures.user())
}
}
def user_index, do: @user_index
@create_user %Operation{
operationId: "UserController.create",
requestBody:
Operation.request_body("request body", "application/json", SchemaFixtures.user(),
required: true
),
responses: %{
200 => Operation.response("User list", "application/json", SchemaFixtures.user_list())
}
}
def create_user, do: @create_user
end
defmodule SpecModule do
def spec do
paths = %{
"/users" => %{
"post" => OperationFixtures.create_user()
}
}
%OpenApiSpex.OpenApi{
info: nil,
paths: paths,
components: %{
schemas: SchemaFixtures.schemas()
}
}
end
end
defmodule RenderError do
def init(_) do
nil
end
def call(_conn, _errors) do
raise "should not have errors"
end
end
describe "cast/4" do
test "cast request body" do
conn = create_conn(%{"user" => %{"email" => "foo@bar.com"}})
assert {:ok, conn} =
Operation2.cast(
OperationFixtures.create_user(),
conn,
"application/json",
SchemaFixtures.schemas()
)
assert %Plug.Conn{} = conn
end
test "cast request body - validation error" do
conn = create_conn(%{"user" => %{"email" => 123}})
assert {:error, errors} =
Operation2.cast(
OperationFixtures.create_user(),
conn,
"application/json",
SchemaFixtures.schemas()
)
assert [error] = errors
assert %Error{} = error
assert error.reason == :invalid_type
end
- test "cast query params" do
+ test "validate param name is defined" do
query_params = %{"unknown" => "asdf"}
conn =
:get
|> Plug.Test.conn("/api/users")
|> Plug.Conn.put_req_header("content-type", "application/json")
|> Map.put(:query_params, query_params)
assert {:error, errors} =
Operation2.cast(
OperationFixtures.user_index(),
conn,
"application/json",
SchemaFixtures.schemas()
)
assert [error] = errors
assert %Error{} = error
assert error.reason == :unexpected_field
assert error.name == "unknown"
end
defp create_conn(body_params) do
:post
|> Plug.Test.conn("/api/users")
|> Plug.Conn.put_req_header("content-type", "application/json")
|> Map.put(:body_params, body_params)
end
end
end

File Metadata

Mime Type
text/x-diff
Expires
Sat, Nov 30, 2:49 PM (1 d, 18 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
41477
Default Alt Text
(8 KB)

Event Timeline