Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F115298
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Award Token
Flag For Later
Size
33 KB
Referenced Files
None
Subscribers
None
View Options
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
Details
Attached
Mime Type
text/x-diff
Expires
Wed, Nov 27, 7:46 PM (1 d, 18 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
40726
Default Alt Text
(33 KB)
Attached To
Mode
R22 open_api_spex
Attached
Detach File
Event Timeline
Log In to Comment