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 3ce576b..2258b38 100644
--- a/lib/open_api_spex/operation2.ex
+++ b/lib/open_api_spex/operation2.ex
@@ -1,92 +1,100 @@
defmodule OpenApiSpex.Operation2 do
@moduledoc """
Defines the `OpenApiSpex.Operation.t` type.
"""
alias OpenApiSpex.{
Cast,
Operation,
Parameter,
RequestBody,
Schema
}
+ alias OpenApiSpex.Cast.Error
+
def cast(operation = %Operation{}, conn = %Plug.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
+
+ 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),
- {:ok, body} <-
- cast_request_body(operation.requestBody, conn.body_params, content_type, schemas) do
- {:ok, %{conn | params: parameter_values, body_params: body}}
+ :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
:ok
end
defp check_query_params_defined(%Plug.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, param} -> {:error, "Undefined query parameter: #{inspect(param)}"}
+ {: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([param | params], defined_params) do
- case MapSet.member?(defined_params, param) do
- false -> {:error, param}
+ 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, %{String.t() => Schema.t()}) ::
- {:ok, map} | {:error, String.t()}
+ @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
end
diff --git a/test/operation2_test.exs b/test/operation2_test.exs
index c6b327d..614fcc3 100644
--- a/test/operation2_test.exs
+++ b/test/operation2_test.exs
@@ -1,109 +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}
}
}
}
}
- @schemas %{"User" => @user}
+ @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", "application/json", SchemaFixtures.user())
+ 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
- # TODO Verify query params are casted
+ 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:33 PM (1 d, 18 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
41447
Default Alt Text
(8 KB)

Event Timeline