Page MenuHomePhorge

No OneTemporary

Size
7 KB
Referenced Files
None
Subscribers
None
diff --git a/lib/open_api_spex/cast_parameters.ex b/lib/open_api_spex/cast_parameters.ex
index 90ceb9a..4ac7228 100644
--- a/lib/open_api_spex/cast_parameters.ex
+++ b/lib/open_api_spex/cast_parameters.ex
@@ -1,59 +1,35 @@
defmodule OpenApiSpex.CastParameters do
@moduledoc false
alias OpenApiSpex.{Cast, Operation, Parameter, Schema}
- alias OpenApiSpex.Cast.Error
+ alias OpenApiSpex.Cast.{Error, Object}
alias Plug.Conn
@spec cast(Plug.Conn.t(), Operation.t(), Schema.schemas()) ::
{:error, [Error.t()]} | {:ok, Conn.t()}
def cast(conn, operation, schemas) do
- parameters =
- Enum.filter(operation.parameters || [], fn p ->
- Map.has_key?(conn.params, Atom.to_string(p.name))
- end)
+ # Taken together as a set, operation parameters are similar to an object schema type.
+ # Convert parameters to an object schema, then delegate to `Cast.Object.cast/1`
- with :ok <- check_query_params_defined(conn, operation.parameters),
- {:ok, parameter_values} <- cast_known_parameters(parameters, conn.params, schemas) do
- {:ok, %{conn | params: parameter_values}}
- end
- end
-
- ## Private functions
+ properties =
+ (operation.parameters || [])
+ |> Enum.map(fn parameter -> {parameter.name, Parameter.schema(parameter)} end)
+ |> Map.new()
- defp check_query_params_defined(%Conn{}, nil = _defined_params) do
- :ok
- end
-
- 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)
+ required =
+ (operation.parameters || [])
+ |> Enum.filter(& &1.required)
+ |> Enum.map(& &1.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
-
- 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
+ object_schema = %Schema{
+ type: :object,
+ properties: properties,
+ required: required
+ }
- defp cast_known_parameters([], _params, _schemas), do: {:ok, %{}}
+ ctx = %Cast{value: conn.params, schema: object_schema, schemas: schemas}
- defp cast_known_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_known_parameters(rest, params, schemas) do
- {:ok, Map.put_new(cast_tail, p.name, cast_val)}
+ with {:ok, params} <- Object.cast(ctx) do
+ {:ok, %{conn | params: params}}
end
end
end
diff --git a/test/operation2_test.exs b/test/operation2_test.exs
index 588a2f9..d56dd40 100644
--- a/test/operation2_test.exs
+++ b/test/operation2_test.exs
@@ -1,157 +1,172 @@
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"),
Operation.parameter(:age, :query, :integer, "User's age")
],
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 - invalid data type" 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 "validate undefined query param name" do
query_params = %{"unknown" => "asdf"}
assert {:error, [error]} = do_index_cast(query_params)
assert %Error{} = error
assert error.reason == :unexpected_field
assert error.name == "unknown"
end
test "validate invalid data type for query param" do
query_params = %{"age" => "asdf"}
assert {:error, [error]} = do_index_cast(query_params)
assert %Error{} = error
assert error.reason == :invalid_type
assert error.type == :integer
assert error.value == "asdf"
end
- defp do_index_cast(query_params) do
+ test "validate missing required query param" do
+ parameter =
+ Operation.parameter(:name, :query, :string, "Filter by user name", required: true)
+
+ operation = %{OperationFixtures.user_index() | parameters: [parameter]}
+
+ assert {:error, [error]} = do_index_cast(%{}, operation: operation)
+ assert %Error{} = error
+ assert error.reason == :missing_field
+ assert error.name == :name
+ end
+
+ defp do_index_cast(query_params, opts \\ []) do
conn =
:get
|> Plug.Test.conn("/api/users?" <> URI.encode_query(query_params))
|> Plug.Conn.put_req_header("content-type", "application/json")
|> Plug.Conn.fetch_query_params()
+ operation = opts[:operation] || OperationFixtures.user_index()
+
Operation2.cast(
- OperationFixtures.user_index(),
+ operation,
conn,
"application/json",
SchemaFixtures.schemas()
)
end
defp create_conn(body_params) do
:post
|> Plug.Test.conn("/api/users")
|> Plug.Conn.put_req_header("content-type", "application/json")
+ |> Plug.Conn.fetch_query_params()
|> Map.put(:body_params, body_params)
end
end
end

File Metadata

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

Event Timeline