Page MenuHomePhorge

No OneTemporary

Size
33 KB
Referenced Files
None
Subscribers
None
diff --git a/lib/open_api_spex.ex b/lib/open_api_spex.ex
index 1f24393..d979999 100644
--- a/lib/open_api_spex.ex
+++ b/lib/open_api_spex.ex
@@ -1,251 +1,255 @@
defmodule OpenApiSpex do
@moduledoc """
Provides the entry-points for defining schemas, validating and casting.
"""
alias OpenApiSpex.{
Components,
OpenApi,
Operation,
Operation2,
Reference,
Schema,
SchemaException,
SchemaResolver,
SchemaConsistency
}
alias OpenApiSpex.Cast.Error
@doc """
Adds schemas to the api spec from the modules specified in the Operations.
Eg, if the response schema for an operation is defined with:
responses: %{
200 => Operation.response("User", "application/json", UserResponse)
}
Then the `UserResponse.schema()` function will be called to load the schema, and
a `Reference` to the loaded schema will be used in the operation response.
See `OpenApiSpex.schema` macro for a convenient syntax for defining schema modules.
"""
@spec resolve_schema_modules(OpenApi.t()) :: OpenApi.t()
def resolve_schema_modules(spec = %OpenApi{}) do
SchemaResolver.resolve_schema_modules(spec)
end
@doc """
Cast and validate a value against a given Schema.
"""
def cast_value(value, schema = %schema_mod{}) when schema_mod in [Schema, Reference] do
OpenApiSpex.Cast.cast(schema, value)
end
@doc """
Cast and validate a value against a given Schema belonging to a given OpenApi spec.
"""
def cast_value(value, schema = %schema_mod{}, spec = %OpenApi{})
when schema_mod in [Schema, Reference] do
OpenApiSpex.Cast.cast(schema, value, spec.components.schemas)
end
def cast_and_validate(
spec = %OpenApi{},
operation = %Operation{},
conn = %Plug.Conn{},
content_type \\ nil
) do
Operation2.cast(operation, conn, content_type, spec.components)
end
@doc """
Cast params to conform to a `OpenApiSpex.Schema`.
See `OpenApiSpex.Schema.cast/3` for additional examples and details.
"""
@spec cast(OpenApi.t(), Schema.t() | Reference.t(), any) :: {:ok, any} | {:error, String.t()}
@deprecated "Use OpenApiSpecs.Cast.cast/3 or cast/2 instead"
def cast(spec = %OpenApi{}, schema = %Schema{}, params) do
Schema.cast(schema, params, spec.components.schemas)
end
def cast(spec = %OpenApi{}, schema = %Reference{}, params) do
Schema.cast(schema, params, spec.components.schemas)
end
@doc """
Cast all params in `Plug.Conn` to conform to the schemas for `OpenApiSpex.Operation`.
Returns `{:ok, Plug.Conn.t}` with `params` and `body_params` fields updated if successful,
or `{:error, reason}` if casting fails.
`content_type` may optionally be supplied to select the `requestBody` schema.
"""
@spec cast(OpenApi.t(), Operation.t(), Plug.Conn.t(), content_type | nil) ::
{:ok, Plug.Conn.t()} | {:error, String.t()}
when content_type: String.t()
@deprecated "Use OpenApiSpecs.Cast.cast_and_validate/3 instead"
def cast(spec = %OpenApi{}, operation = %Operation{}, conn = %Plug.Conn{}, content_type \\ nil) do
Operation.cast(operation, conn, content_type, spec.components.schemas)
end
@doc """
Validate params against `OpenApiSpex.Schema`.
See `OpenApiSpex.Schema.validate/3` for examples of error messages.
"""
@spec validate(OpenApi.t(), Schema.t() | Reference.t(), any) :: :ok | {:error, String.t()}
@deprecated "Use OpenApiSpecs.Cast.cast_value/3 or cast_value/2 instead"
def validate(spec = %OpenApi{}, schema = %Schema{}, params) do
Schema.validate(schema, params, spec.components.schemas)
end
def validate(spec = %OpenApi{}, schema = %Reference{}, params) do
Schema.validate(schema, params, spec.components.schemas)
end
@doc """
Validate all params in `Plug.Conn` against `OpenApiSpex.Operation` `parameter` and `requestBody` schemas.
`content_type` may be optionally supplied to select the `requestBody` schema.
"""
@spec validate(OpenApi.t(), Operation.t(), Plug.Conn.t(), content_type | nil) ::
:ok | {:error, String.t()}
when content_type: String.t()
@deprecated "Use OpenApiSpex.cast_and_validate/4 instead"
def validate(
spec = %OpenApi{},
operation = %Operation{},
conn = %Plug.Conn{},
content_type \\ nil
) do
Operation.validate(operation, conn, content_type, spec.components.schemas)
end
def path_to_string(%Error{} = error) do
Error.path_to_string(error)
end
+ def error_message(%Error{} = error) do
+ Error.message(error)
+ end
+
@doc """
Declares a struct based `OpenApiSpex.Schema`
- defines the schema/0 callback
- ensures the schema is linked to the module by "x-struct" extension property
- defines a struct with keys matching the schema properties
- defines a @type `t` for the struct
- derives a `Jason.Encoder` and/or `Poison.Encoder` for the struct
See `OpenApiSpex.Schema` for additional examples and details.
## Example
require OpenApiSpex
defmodule User do
OpenApiSpex.schema %{
title: "User",
description: "A user of the app",
type: :object,
properties: %{
id: %Schema{type: :integer, description: "User ID"},
name: %Schema{type: :string, description: "User name", pattern: ~r/[a-zA-Z][a-zA-Z0-9_]+/},
email: %Schema{type: :string, description: "Email address", format: :email},
inserted_at: %Schema{type: :string, description: "Creation timestamp", format: :'date-time'},
updated_at: %Schema{type: :string, description: "Update timestamp", format: :'date-time'}
},
required: [:name, :email],
example: %{
"id" => 123,
"name" => "Joe User",
"email" => "joe@gmail.com",
"inserted_at" => "2017-09-12T12:34:55Z",
"updated_at" => "2017-09-13T10:11:12Z"
}
}
end
"""
defmacro schema(body) do
quote do
@compile {:report_warnings, false}
@behaviour OpenApiSpex.Schema
@schema struct(
OpenApiSpex.Schema,
Map.put(Map.delete(unquote(body), :__struct__), :"x-struct", __MODULE__)
)
def schema, do: @schema
@derive Enum.filter([Poison.Encoder, Jason.Encoder], &Code.ensure_loaded?/1)
defstruct Schema.properties(@schema)
@type t :: %__MODULE__{}
Map.from_struct(@schema) |> OpenApiSpex.validate_compiled_schema()
# Throwing warnings to prevent runtime bugs (like in issue #144)
@schema
|> SchemaConsistency.warnings()
|> Enum.each(&IO.warn("Inconsistent schema: #{&1}", Macro.Env.stacktrace(__ENV__)))
end
end
@doc """
Validate the compiled schema's properties to ensure the schema is not improperly
defined. Only errors which would cause a given schema to _always_ fail should be
raised here.
"""
def validate_compiled_schema(schema) do
Enum.each(schema, fn prop_and_val ->
:ok = validate_compiled_schema(prop_and_val, schema)
end)
end
def validate_compiled_schema({_, %Schema{} = schema}, _parent) do
validate_compiled_schema(schema)
end
@doc """
Used for validating the schema at compile time, otherwise we're forced
to raise errors for improperly defined schemas at runtime.
"""
def validate_compiled_schema({:discriminator, %{propertyName: property, mapping: _}}, %{
anyOf: schemas
})
when is_list(schemas) do
Enum.each(schemas, fn schema ->
case schema do
%Schema{title: title} when is_binary(title) -> :ok
_ -> error!(:discriminator_schema_missing_title, schema, property_name: property)
end
end)
end
def validate_compiled_schema({:discriminator, %{propertyName: _, mapping: _}}, schema) do
case {schema.anyOf, schema.allOf, schema.oneOf} do
{nil, nil, nil} ->
error!(:discriminator_missing_composite_key, schema)
_ ->
:ok
end
end
def validate_compiled_schema({_property, _value}, _schema), do: :ok
@doc """
Raises compile time errors for improperly defined schemas.
"""
@spec error!(atom(), Schema.t(), keyword()) :: no_return()
@spec error!(atom(), Schema.t()) :: no_return()
def error!(error, schema, details \\ []) do
raise SchemaException, %{error: error, schema: schema, details: details}
end
@doc """
Resolve a schema or reference to a schema.
"""
@spec resolve_schema(Schema.t() | Reference.t(), Components.schemas_map()) :: Schema.t()
def resolve_schema(%Schema{} = schema, _), do: schema
def resolve_schema(%Reference{} = ref, schemas), do: Reference.resolve_schema(ref, schemas)
end
diff --git a/test/plug/cast_and_validate/custom_error_user_controller_test.exs b/test/plug/cast_and_validate/custom_error_user_controller_test.exs
index 7824d2a..d4ca93e 100644
--- a/test/plug/cast_and_validate/custom_error_user_controller_test.exs
+++ b/test/plug/cast_and_validate/custom_error_user_controller_test.exs
@@ -1,39 +1,37 @@
defmodule OpenApiSpex.Plug.CastAndValidate.CustomErrorUserControllerTest do
use ExUnit.Case, async: true
describe "query params - param with custom error handling" do
test "Valid Param" do
conn =
:get
- |> Plug.Test.conn("/api/cast_and_validate_test/custom_error_users?validParam=true")
+ |> Plug.Test.conn("/api/custom_error_users?validParam=true")
|> OpenApiSpexTest.Router.call([])
assert conn.status == 200
end
@tag :capture_log
test "Invalid value" do
conn =
:get
- |> Plug.Test.conn("/api/cast_and_validate_test/custom_error_users?validParam=123")
+ |> Plug.Test.conn("/api/custom_error_users?validParam=123")
|> OpenApiSpexTest.Router.call([])
assert conn.status == 400
assert conn.resp_body == "Invalid boolean. Got: string"
end
@tag :capture_log
test "Invalid Param" do
conn =
:get
- |> Plug.Test.conn(
- "/api/cast_and_validate_test/custom_error_users?validParam=123&inValidParam=123&inValid2=hi"
- )
+ |> Plug.Test.conn("/api/custom_error_users?validParam=123&inValidParam=123&inValid2=hi")
|> OpenApiSpexTest.Router.call([])
assert conn.status == 400
assert conn.resp_body == "Unexpected field: inValid2"
end
end
end
diff --git a/test/plug/cast_and_validate_test.exs b/test/plug/cast_and_validate_test.exs
index 98c4676..c2c5467 100644
--- a/test/plug/cast_and_validate_test.exs
+++ b/test/plug/cast_and_validate_test.exs
@@ -1,126 +1,159 @@
-defmodule OpenApiSpex.Plug.CastAndValidateTest do
- use ExUnit.Case, async: true
+defmodule OpenApiSpex.Plug.CastTest do
+ use ExUnit.Case
describe "query params - basics" do
test "Valid Param" do
conn =
:get
- |> Plug.Test.conn("/api/cast_and_validate_test/users?validParam=true")
+ |> Plug.Test.conn("/api/users?validParam=true")
|> OpenApiSpexTest.Router.call([])
assert conn.status == 200
end
- @tag :capture_log
test "Invalid value" do
conn =
:get
- |> Plug.Test.conn("/api/cast_and_validate_test/users?validParam=123")
+ |> Plug.Test.conn("/api/users?validParam=123")
|> OpenApiSpexTest.Router.call([])
assert conn.status == 422
end
- @tag :capture_log
test "Invalid Param" do
conn =
:get
- |> Plug.Test.conn(
- "/api/cast_and_validate_test/users?validParam=123&inValidParam=123&inValid2=hi"
- )
+ |> Plug.Test.conn("/api/users?validParam=123&inValidParam=123&inValid2=hi")
|> OpenApiSpexTest.Router.call([])
assert conn.status == 422
+ error_resp = Jason.decode!(conn.resp_body)
- assert conn.resp_body ==
- "{\"errors\":[{\"message\":\"Unexpected field: inValid2\",\"source\":{\"pointer\":\"/inValid2\"},\"title\":\"Invalid value\"}]}"
+ assert error_resp == %{
+ "errors" => [
+ %{
+ "message" => "Unexpected field: inValid2",
+ "source" => %{"pointer" => "/inValid2"},
+ "title" => "Invalid value"
+ }
+ ]
+ }
end
- @tag :capture_log
test "with requestBody" do
body =
Jason.encode!(%{
phone_number: "123-456-789",
postal_address: "123 Lane St"
})
conn =
:post
- |> Plug.Test.conn("/api/cast_and_validate_test/users/123/contact_info", body)
+ |> Plug.Test.conn("/api/users/123/contact_info", body)
|> Plug.Conn.put_req_header("content-type", "application/json")
|> OpenApiSpexTest.Router.call([])
assert conn.status == 200
end
end
+ describe "query params - param with custom error handling" do
+ test "Valid Param" do
+ conn =
+ :get
+ |> Plug.Test.conn("/api/custom_error_users?validParam=true")
+ |> OpenApiSpexTest.Router.call([])
+
+ assert conn.status == 200
+ end
+
+ test "Invalid value" do
+ conn =
+ :get
+ |> Plug.Test.conn("/api/custom_error_users?validParam=123")
+ |> OpenApiSpexTest.Router.call([])
+
+ assert conn.status == 400
+ end
+
+ test "Invalid Param" do
+ conn =
+ :get
+ |> Plug.Test.conn("/api/custom_error_users?validParam=123&inValidParam=123&inValid2=hi")
+ |> OpenApiSpexTest.Router.call([])
+
+ assert conn.status == 400
+ assert conn.resp_body == "Unexpected field: inValid2"
+ end
+ end
+
describe "body params" do
test "Valid Request" do
request_body = %{
"user" => %{
"id" => 123,
"name" => "asdf",
"email" => "foo@bar.com",
"updated_at" => "2017-09-12T14:44:55Z"
}
}
conn =
:post
- |> Plug.Test.conn("/api/cast_and_validate_test/users", Jason.encode!(request_body))
+ |> Plug.Test.conn("/api/users", Jason.encode!(request_body))
|> Plug.Conn.put_req_header("content-type", "application/json; charset=UTF-8")
|> OpenApiSpexTest.Router.call([])
assert conn.body_params == %OpenApiSpexTest.Schemas.UserRequest{
user: %OpenApiSpexTest.Schemas.User{
id: 123,
name: "asdf",
email: "foo@bar.com",
updated_at: ~N[2017-09-12T14:44:55Z] |> DateTime.from_naive!("Etc/UTC")
}
}
assert Jason.decode!(conn.resp_body) == %{
"data" => %{
"email" => "foo@bar.com",
"id" => 1234,
"inserted_at" => nil,
"name" => "asdf",
"updated_at" => "2017-09-12T14:44:55Z"
}
}
end
- @tag :capture_log
test "Invalid Request" do
request_body = %{
"user" => %{
"id" => 123,
"name" => "*1234",
"email" => "foo@bar.com",
"updated_at" => "2017-09-12T14:44:55Z"
}
}
conn =
:post
- |> Plug.Test.conn("/api/cast_and_validate_test/users", Jason.encode!(request_body))
+ |> Plug.Test.conn("/api/users", Jason.encode!(request_body))
|> Plug.Conn.put_req_header("content-type", "application/json")
conn = OpenApiSpexTest.Router.call(conn, [])
assert conn.status == 422
- resp_body = Jason.decode!(conn.resp_body)
-
- assert resp_body == %{
- "errors" => [
- %{
- "message" => "Invalid format. Expected ~r/[a-zA-Z][a-zA-Z0-9_]+/",
- "source" => %{"pointer" => "/user/name"},
- "title" => "Invalid value"
- }
- ]
- }
+ resp_data = Jason.decode!(conn.resp_body)
+
+ assert resp_data ==
+ %{
+ "errors" => [
+ %{
+ "message" => "Invalid format. Expected ~r/[a-zA-Z][a-zA-Z0-9_]+/",
+ "source" => %{"pointer" => "/user/name"},
+ "title" => "Invalid value"
+ }
+ ]
+ }
end
end
end
diff --git a/test/plug/cast_test.exs b/test/plug/cast_test.exs
deleted file mode 100644
index b30f42d..0000000
--- a/test/plug/cast_test.exs
+++ /dev/null
@@ -1,139 +0,0 @@
-defmodule OpenApiSpex.Plug.CastTest do
- use ExUnit.Case
-
- describe "query params - basics" do
- test "Valid Param" do
- conn =
- :get
- |> Plug.Test.conn("/api/users?validParam=true")
- |> OpenApiSpexTest.Router.call([])
-
- assert conn.status == 200
- end
-
- test "Invalid value" do
- conn =
- :get
- |> Plug.Test.conn("/api/users?validParam=123")
- |> OpenApiSpexTest.Router.call([])
-
- assert conn.status == 422
- end
-
- test "Invalid Param" do
- conn =
- :get
- |> Plug.Test.conn("/api/users?validParam=123&inValidParam=123&inValid2=hi")
- |> OpenApiSpexTest.Router.call([])
-
- assert conn.status == 422
- assert conn.resp_body == "Undefined query parameter: \"inValid2\""
- end
-
- test "with requestBody" do
- body =
- Jason.encode!(%{
- phone_number: "123-456-789",
- postal_address: "123 Lane St"
- })
-
- conn =
- :post
- |> Plug.Test.conn("/api/users/123/contact_info", body)
- |> Plug.Conn.put_req_header("content-type", "application/json")
- |> OpenApiSpexTest.Router.call([])
-
- assert conn.status == 200
- end
- end
-
- describe "query params - param with custom error handling" do
- test "Valid Param" do
- conn =
- :get
- |> Plug.Test.conn("/api/custom_error_users?validParam=true")
- |> OpenApiSpexTest.Router.call([])
-
- assert conn.status == 200
- end
-
- test "Invalid value" do
- conn =
- :get
- |> Plug.Test.conn("/api/custom_error_users?validParam=123")
- |> OpenApiSpexTest.Router.call([])
-
- assert conn.status == 400
- end
-
- test "Invalid Param" do
- conn =
- :get
- |> Plug.Test.conn("/api/custom_error_users?validParam=123&inValidParam=123&inValid2=hi")
- |> OpenApiSpexTest.Router.call([])
-
- assert conn.status == 400
- assert conn.resp_body == "Undefined query parameter: \"inValid2\""
- end
- end
-
- describe "body params" do
- test "Valid Request" do
- request_body = %{
- "user" => %{
- "id" => 123,
- "name" => "asdf",
- "email" => "foo@bar.com",
- "updated_at" => "2017-09-12T14:44:55Z"
- }
- }
-
- conn =
- :post
- |> Plug.Test.conn("/api/users", Jason.encode!(request_body))
- |> Plug.Conn.put_req_header("content-type", "application/json; charset=UTF-8")
- |> OpenApiSpexTest.Router.call([])
-
- assert conn.body_params == %OpenApiSpexTest.Schemas.UserRequest{
- user: %OpenApiSpexTest.Schemas.User{
- id: 123,
- name: "asdf",
- email: "foo@bar.com",
- updated_at: ~N[2017-09-12T14:44:55Z] |> DateTime.from_naive!("Etc/UTC")
- }
- }
-
- assert Jason.decode!(conn.resp_body) == %{
- "data" => %{
- "email" => "foo@bar.com",
- "id" => 1234,
- "inserted_at" => nil,
- "name" => "asdf",
- "updated_at" => "2017-09-12T14:44:55Z"
- }
- }
- end
-
- test "Invalid Request" do
- request_body = %{
- "user" => %{
- "id" => 123,
- "name" => "*1234",
- "email" => "foo@bar.com",
- "updated_at" => "2017-09-12T14:44:55Z"
- }
- }
-
- conn =
- :post
- |> Plug.Test.conn("/api/users", Jason.encode!(request_body))
- |> Plug.Conn.put_req_header("content-type", "application/json")
-
- conn = OpenApiSpexTest.Router.call(conn, [])
- assert conn.status == 422
-
- assert conn.resp_body ==
- "#/user/name: Value \"*1234\" does not match pattern: [a-zA-Z][a-zA-Z0-9_]+"
- end
- end
-end
diff --git a/test/support/cast_and_validate_user_controller.ex b/test/support/cast_and_validate_user_controller.ex
deleted file mode 100644
index 142bddd..0000000
--- a/test/support/cast_and_validate_user_controller.ex
+++ /dev/null
@@ -1,177 +0,0 @@
-defmodule OpenApiSpexTest.CastAndValidateUserController do
- use Phoenix.Controller
- alias OpenApiSpex.Operation
- alias OpenApiSpexTest.Schemas
-
- plug OpenApiSpex.Plug.CastAndValidate
-
- def open_api_operation(action) do
- apply(__MODULE__, :"#{action}_operation", [])
- end
-
- @doc """
- API Spec for :show action
- """
- def show_operation() do
- import Operation
-
- %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)
- ],
- responses: %{
- 200 => response("User", "application/json", Schemas.UserResponse)
- }
- }
- end
-
- def show(conn, %{id: id}) do
- json(conn, %Schemas.UserResponse{
- data: %Schemas.User{
- id: id,
- name: "joe user",
- email: "joe@gmail.com"
- }
- })
- end
-
- def index_operation() do
- import Operation
-
- %Operation{
- tags: ["users"],
- summary: "List users",
- description: "List all useres",
- operationId: "UserController.index",
- parameters: [
- parameter(:validParam, :query, :boolean, "Valid Param", example: true)
- ],
- responses: %{
- 200 => response("User List Response", "application/json", Schemas.UsersResponse)
- }
- }
- end
-
- def index(conn, _params) do
- json(conn, %Schemas.UsersResponse{
- data: [
- %Schemas.User{
- id: 123,
- name: "joe user",
- email: "joe@gmail.com"
- }
- ]
- })
- end
-
- def create_operation() do
- import Operation
-
- %Operation{
- tags: ["users"],
- summary: "Create user",
- description: "Create a user",
- operationId: "UserController.create",
- parameters: [],
- requestBody: request_body("The user attributes", "application/json", Schemas.UserRequest),
- responses: %{
- 201 => response("User", "application/json", Schemas.UserResponse)
- }
- }
- end
-
- def create(conn = %{body_params: %Schemas.UserRequest{user: user = %Schemas.User{}}}, _) do
- json(conn, %Schemas.UserResponse{
- data: %{user | id: 1234}
- })
- end
-
- def contact_info_operation() do
- import Operation
-
- %Operation{
- tags: ["users"],
- summary: "Update contact info",
- description: "Update contact info",
- operationId: "UserController.contact_info",
- parameters: [
- parameter(:id, :path, :integer, "user ID")
- ],
- requestBody: request_body("Contact info", "application/json", Schemas.ContactInfo),
- responses: %{
- 200 => %OpenApiSpex.Response{
- description: "OK"
- }
- }
- }
- end
-
- # POST /contact_info
- def contact_info(conn = %{body_params: %Schemas.ContactInfo{}}, %{id: id}) do
- conn
- |> put_status(200)
- |> json(%{id: id})
- end
-
- def payment_details_operation() do
- import Operation
-
- %Operation{
- tags: ["users"],
- summary: "Show user payment details",
- description: "Shows a users payment details",
- operationId: "UserController.payment_details",
- parameters: [
- parameter(:id, :path, :integer, "User ID", example: 123, minimum: 1)
- ],
- responses: %{
- 200 => response("Payment Details", "application/json", Schemas.PaymentDetails)
- }
- }
- end
-
- def payment_details(conn, %{"id" => id}) do
- response =
- case rem(id, 2) do
- 0 ->
- %Schemas.CreditCardPaymentDetails{
- credit_card_number: "1234-5678-0987-6543",
- name_on_card: "Joe User",
- expiry: "0522"
- }
-
- 1 ->
- %Schemas.DirectDebitPaymentDetails{
- account_number: "98776543",
- account_name: "Joes Savings",
- bsb: "123-567"
- }
- end
-
- json(conn, response)
- end
-
- def create_entity_operation() do
- import Operation
-
- %Operation{
- tags: ["EntityWithDict"],
- summary: "Create an EntityWithDict",
- description: "Create an EntityWithDict",
- operationId: "UserController.create_entity",
- parameters: [],
- requestBody: request_body("Entity attributes", "application/json", Schemas.EntityWithDict),
- responses: %{
- 201 => response("EntityWithDict", "application/json", Schemas.EntityWithDict)
- }
- }
- end
-
- def create_entity(conn, %Schemas.EntityWithDict{} = entity) do
- json(conn, Map.put(entity, :id, 123))
- end
-end
diff --git a/test/support/custom_error_user_controller.ex b/test/support/custom_error_user_controller.ex
index 2acd746..c7df1da 100644
--- a/test/support/custom_error_user_controller.ex
+++ b/test/support/custom_error_user_controller.ex
@@ -1,56 +1,56 @@
defmodule OpenApiSpexTest.CustomErrorUserController do
use Phoenix.Controller
alias OpenApiSpex.Operation
alias OpenApiSpexTest.Schemas
alias Plug.Conn
defmodule CustomRenderErrorPlug do
-
@behaviour Plug
alias Plug.Conn
@impl Plug
def init(opts), do: opts
@impl Plug
- def call(conn, reason) do
- conn |> Conn.send_resp(400, "#{reason}")
+ def call(conn, errors) do
+ errors = errors |> Enum.map(&OpenApiSpex.error_message/1) |> Enum.join(",")
+ conn |> Conn.send_resp(400, "#{errors}")
end
end
- plug OpenApiSpex.Plug.Cast, render_error: CustomRenderErrorPlug
- plug OpenApiSpex.Plug.Validate, render_error: CustomRenderErrorPlug
+ plug OpenApiSpex.Plug.CastAndValidate, render_error: CustomRenderErrorPlug
def open_api_operation(action) do
apply(__MODULE__, :"#{action}_operation", [])
end
def index_operation() do
import Operation
+
%Operation{
tags: ["users"],
summary: "List users",
description: "List all useres",
operationId: "UserController.index",
parameters: [
parameter(:validParam, :query, :boolean, "Valid Param", example: true)
],
responses: %{
200 => response("User List Response", "application/json", Schemas.UsersResponse)
}
}
end
+
def index(conn, _params) do
json(conn, %Schemas.UsersResponse{
data: [
%Schemas.User{
id: 123,
name: "joe user",
email: "joe@gmail.com"
}
]
})
end
-
end
diff --git a/test/support/router.ex b/test/support/router.ex
index 98ac480..97b6a21 100644
--- a/test/support/router.ex
+++ b/test/support/router.ex
@@ -1,34 +1,24 @@
defmodule OpenApiSpexTest.Router do
use Phoenix.Router
alias Plug.Parsers
alias OpenApiSpex.Plug.{PutApiSpec, RenderSpec}
pipeline :api do
plug :accepts, ["json"]
plug PutApiSpec, module: OpenApiSpexTest.ApiSpec
plug Parsers, parsers: [:json], pass: ["text/*"], json_decoder: Jason
end
scope "/api", OpenApiSpexTest do
pipe_through :api
resources "/users", UserController, only: [:create, :index, :show]
# Used by ParamsTest
resources "/custom_error_users", CustomErrorUserController, only: [:index]
- # Used by CastAndValidateTest
- scope "/cast_and_validate_test" do
- get "/users", CastAndValidateUserController, :index
- get "/users/:id", CastAndValidateUserController, :show
- post "/users", CastAndValidateUserController, :create
- post "/users/:id/contact_info", CastAndValidateUserController, :contact_info
- post "/users/:id/payment_details", CastAndValidateUserController, :payment_details
- resources "/custom_error_users", CastAndValidate.CustomErrorUserController, only: [:index]
- end
-
get "/users/:id/payment_details", UserController, :payment_details
post "/users/:id/contact_info", UserController, :contact_info
post "/users/create_entity", UserController, :create_entity
get "/openapi", RenderSpec, []
end
end
diff --git a/test/support/user_controller.ex b/test/support/user_controller.ex
index 1c48db5..561daa3 100644
--- a/test/support/user_controller.ex
+++ b/test/support/user_controller.ex
@@ -1,164 +1,176 @@
defmodule OpenApiSpexTest.UserController do
use Phoenix.Controller
alias OpenApiSpex.Operation
alias OpenApiSpexTest.Schemas
- plug OpenApiSpex.Plug.Cast
- plug OpenApiSpex.Plug.Validate
+ plug OpenApiSpex.Plug.CastAndValidate
def open_api_operation(action) do
apply(__MODULE__, :"#{action}_operation", [])
end
@doc """
API Spec for :show action
"""
def show_operation() do
import Operation
+
%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)
],
responses: %{
200 => response("User", "application/json", Schemas.UserResponse)
}
}
end
+
def show(conn, %{id: id}) do
json(conn, %Schemas.UserResponse{
data: %Schemas.User{
id: id,
name: "joe user",
email: "joe@gmail.com"
}
})
end
def index_operation() do
import Operation
+
%Operation{
tags: ["users"],
summary: "List users",
description: "List all useres",
operationId: "UserController.index",
parameters: [
parameter(:validParam, :query, :boolean, "Valid Param", example: true)
],
responses: %{
200 => response("User List Response", "application/json", Schemas.UsersResponse)
}
}
end
+
def index(conn, _params) do
json(conn, %Schemas.UsersResponse{
data: [
%Schemas.User{
id: 123,
name: "joe user",
email: "joe@gmail.com"
}
]
})
end
def create_operation() do
import Operation
+
%Operation{
tags: ["users"],
summary: "Create user",
description: "Create a user",
operationId: "UserController.create",
parameters: [],
requestBody: request_body("The user attributes", "application/json", Schemas.UserRequest),
responses: %{
201 => response("User", "application/json", Schemas.UserResponse)
}
}
end
+
def create(conn = %{body_params: %Schemas.UserRequest{user: user = %Schemas.User{}}}, _) do
json(conn, %Schemas.UserResponse{
data: %{user | id: 1234}
})
end
def contact_info_operation() do
import Operation
+
%Operation{
tags: ["users"],
summary: "Update contact info",
description: "Update contact info",
operationId: "UserController.contact_info",
parameters: [
parameter(:id, :path, :integer, "user ID")
],
requestBody: request_body("Contact info", "application/json", Schemas.ContactInfo),
responses: %{
200 => %OpenApiSpex.Response{
description: "OK"
}
}
}
end
+
def contact_info(conn = %{body_params: %Schemas.ContactInfo{}}, %{id: id}) do
conn
|> put_status(200)
|> json(%{id: id})
end
def payment_details_operation() do
import Operation
+
%Operation{
tags: ["users"],
summary: "Show user payment details",
description: "Shows a users payment details",
operationId: "UserController.payment_details",
parameters: [
parameter(:id, :path, :integer, "User ID", example: 123, minimum: 1)
],
responses: %{
200 => response("Payment Details", "application/json", Schemas.PaymentDetails)
}
}
end
+
def payment_details(conn, %{"id" => id}) do
response =
case rem(id, 2) do
0 ->
%Schemas.CreditCardPaymentDetails{
credit_card_number: "1234-5678-0987-6543",
name_on_card: "Joe User",
expiry: "0522"
}
+
1 ->
%Schemas.DirectDebitPaymentDetails{
account_number: "98776543",
account_name: "Joes Savings",
bsb: "123-567"
}
end
- json(conn, response)
+ json(conn, response)
end
def create_entity_operation() do
import Operation
+
%Operation{
tags: ["EntityWithDict"],
summary: "Create an EntityWithDict",
description: "Create an EntityWithDict",
operationId: "UserController.create_entity",
parameters: [],
requestBody: request_body("Entity attributes", "application/json", Schemas.EntityWithDict),
responses: %{
201 => response("EntityWithDict", "application/json", Schemas.EntityWithDict)
}
}
end
+
def create_entity(conn, %Schemas.EntityWithDict{} = entity) do
json(conn, Map.put(entity, :id, 123))
end
end

File Metadata

Mime Type
text/x-diff
Expires
Wed, Nov 27, 7:46 PM (1 d, 16 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
40726
Default Alt Text
(33 KB)

Event Timeline