Packages

PhoenixSwagger is the library that provides swagger integration to the phoenix web framework adding the readonly property.

Current section

Files

Jump to
phoenix_swagger_ro lib phoenix_swagger path.ex
Raw

lib/phoenix_swagger/path.ex

defmodule PhoenixSwagger.Path do
@moduledoc """
Defines the swagger path DSL for specifying Controller actions.
This module should not be imported directly, it will be automatically imported
in the scope of a `swagger_path` macro body.
## Examples
use PhoenixSwagger
swagger_path :index do
get "/users"
produces "application/json"
paging
parameter :id, :query, :integer, "user id", required: true
tag "Users"
response 200 "User resource" :User
response 404 "User not found"
end
"""
alias PhoenixSwagger.Schema
defmodule Parameter do
@moduledoc """
A swagger parameter definition, similar to a `Schema`, but swagger defines
parameter name (and some other options) to be part of the parameter object itself.
See http://swagger.io/specification/#parameterObject
"""
defstruct(
name: "",
in: "",
description: "",
required: false,
schema: nil,
type: nil,
format: nil,
allowEmptyValue: nil,
items: nil,
collectionFormat: nil,
default: nil,
maximum: nil,
exclusiveMaximum: nil,
minimum: nil,
exclusiveMinimum: nil,
maxLength: nil,
minLength: nil,
pattern: nil,
maxItems: nil,
minItems: nil,
uniqueItems: nil,
enum: nil,
multipleOf: nil
)
end
defmodule ResponseObject do
@moduledoc """
A swagger response definition.
The response status (200, 404, etc.) is the key in the containing map.
See http://swagger.io/specification/#responseObject
"""
defstruct description: "", schema: nil, headers: nil, examples: nil
end
defmodule OperationObject do
@moduledoc """
A swagger operation object ties together parameters, responses, etc.
See http://swagger.io/specification/#operationObject
"""
defstruct(
tags: [],
summary: "",
description: "",
externalDocs: nil,
operationId: "",
consumes: nil,
produces: nil,
parameters: [],
responses: %{},
deprecated: nil,
security: nil
)
end
defmodule PathObject do
@moduledoc """
The DSL builds paths out of individual operations, so this is a flattened version
of a swagger Path.
The `nest` function will convert this to a nested map before final
conversion to a JSON map.
See http://swagger.io/specification/#pathsObject
"""
defstruct path: nil, verb: nil, operation: %OperationObject{}
end
@doc "Initializes a PathObject with verb \"get\" and given path"
def get(path_obj = %PathObject{}, path), do: %{path_obj | path: path, verb: "get"}
@doc "Initializes a PathObject with verb \"post\" and given path"
def post(path_obj = %PathObject{}, path), do: %{path_obj | path: path, verb: "post"}
@doc "Initializes a PathObject with verb \"put\" and given path"
def put(path_obj = %PathObject{}, path), do: %{path_obj | path: path, verb: "put"}
@doc "Initializes a PathObject with verb \"patch\" and given path"
def patch(path_obj = %PathObject{}, path), do: %{path_obj | path: path, verb: "patch"}
@doc "Initializes a PathObject with verb \"delete\" and given path"
def delete(path_obj = %PathObject{}, path), do: %{path_obj | path: path, verb: "delete"}
@doc "Initializes a PathObject with verb \"head\" and given path"
def head(path_obj = %PathObject{}, path), do: %{path_obj | path: path, verb: "head"}
@doc "Initializes a PathObject with verb \"options\" and given path"
def options(path_obj = %PathObject{}, path), do: %{path_obj | path: path, verb: "options"}
@doc """
Adds the summary section to the operation of a swagger `%PathObject{}`.
"""
def summary(path = %PathObject{}, summary) do
put_in(path.operation.summary, summary)
end
@doc """
Adds the description section to the operation of a swagger `%PathObject{}`.
"""
def description(path = %PathObject{}, description) do
put_in(path.operation.description, description)
end
@doc """
Adds a mime-type to the consumes list of the operation of a swagger `%PathObject{}`.
"""
def consumes(path = %PathObject{}, mimetype) do
put_in(path.operation.consumes, (path.operation.consumes || []) ++ [mimetype])
end
@doc """
Adds a mime-type to the produces list of the operation of a swagger `%PathObject{}`.
"""
def produces(path = %PathObject{}, mimetype) do
put_in(path.operation.produces, (path.operation.produces || []) ++ [mimetype])
end
@doc """
Adds a tag to the operation of a swagger `%PathObject{}`.
"""
def tag(path = %PathObject{}, tag) do
put_in(path.operation.tags, path.operation.tags ++ [tag])
end
@doc """
Adds the operationId section to the operation of a swagger `%PathObject{}`.
"""
def operation_id(path = %PathObject{}, id) do
put_in(path.operation.operationId, id)
end
@doc """
Adds the security section to the operation of a swagger `%PathObject{}`.
"""
def security(path = %PathObject{}, security) do
put_in(path.operation.security, security)
end
@doc """
Defines multiple parameters for an operation with a custom DSL syntax.
## Examples
swagger_path :create do
post "/api/v1/{team}/users"
summary "Create a new user"
parameters do
user :body, Schema.ref(:User), "user attributes"
team :path, :string, "Users team ID"
end
response 200, "OK", :User
end
"""
defmacro parameters(path, block) do
exprs =
case block do
[do: {:__block__, _, exprs}] -> exprs
[do: expr] -> [expr]
end
exprs
|> Enum.map(fn {name, line, args} -> {:parameter, line, [name | args]} end)
|> Enum.reduce(path, fn expr, acc ->
quote do
unquote(acc) |> unquote(expr)
end
end)
end
@doc """
Adds a parameter to the operation of a swagger `%PathObject{}`.
"""
def parameter(path = %PathObject{}, name, location, type, description, opts \\ []) do
param = %Parameter{
name: name,
in: location,
description: description
}
param =
case location do
:body -> %{param | schema: type}
:path -> %{param | type: type, required: true}
_ -> %{param | type: type}
end
param = Map.merge(param, opts |> Enum.into(%{}, &translate_parameter_opt/1))
params = path.operation.parameters
put_in(path.operation.parameters, params ++ [param])
end
@doc """
Adds the deprecation section to the operation of a swagger `%PathObject{}`.
"""
def deprecated(path = %PathObject{}, status) do
put_in(path.operation.deprecated, status)
end
defp translate_parameter_opt({:example, v}), do: {:"x-example", v}
defp translate_parameter_opt({:items, items_schema}) when is_list(items_schema) do
{:items, Enum.into(items_schema, %{})}
end
defp translate_parameter_opt({k, v}), do: {k, v}
@doc """
Adds page size, number and offset parameters to the operation of a swagger `%PathObject{}`.
The names default to "page_size" and "page" for ease of use with `scriviner_ecto`, but can be overridden.
## Examples
get "/api/pets/"
paging
response 200, "OK"
get "/api/pets/dogs"
paging size: "page_size", number: "count"
response 200, "OK"
get "/api/pets/cats"
paging size: "limit", offset: "offset"
response 200, "OK"
"""
def paging(path = %PathObject{}, opts \\ [size: "page_size", number: "page"]) do
Enum.reduce(opts, path, fn
{:size, size}, path ->
parameter(path, size, :query, :integer, "Number of elements per page", minimum: 1)
{:number, number}, path ->
parameter(path, number, :query, :integer, "Number of the page", minimum: 1)
{:offset, offset}, path ->
parameter(path, offset, :query, :integer, "Offset of first element in the page")
end)
end
@doc """
Adds a response to the operation of a swagger `%PathObject{}`, without a schema.
"""
def response(path = %PathObject{}, status, description) do
resp = %ResponseObject{description: description}
put_in(path.operation.responses[to_string(status)], resp)
end
@doc """
Adds a response to the operation of a swagger `%PathObject{}`, with a schema.
Optional keyword args can be provided for `headers` and `examples`.
If the mime-type is known from the `produces` list, then a single can be given as a shorthand.
## Examples
get "/users/{id}"
produces "application/json"
parameter :id, :path, :integer, "user id", required: true
response 200, "Success", :User, examples: %{"application/json": %{id: 1, name: "Joe"}}
get "/users/{id}"
produces "application/json"
parameter :id, :path, :integer, "user id", required: true
response 200, "Success", :User, example: %{id: 1, name: "Joe"}
"""
def response(path, status, description, schema, opts \\ [])
def response(path = %PathObject{}, status, description, schema, opts) when is_atom(schema) do
response(path, status, description, Schema.ref(schema), opts)
end
def response(path = %PathObject{}, status, description, schema = %{}, opts) do
opts = expand_response_example(path, opts)
resp = struct(ResponseObject, [description: description, schema: schema] ++ opts)
put_in(path.operation.responses[status |> to_string], resp)
end
def expand_response_example(%PathObject{operation: %{produces: [mimetype | _]}}, opts) do
Enum.map(opts, fn
{:example, e} -> {:examples, %{mimetype => e}}
opt -> opt
end)
end
def expand_response_example(%PathObject{}, opts), do: opts
@doc """
Converts the `%PathObject{}` struct into the nested JSON form expected by swagger.
"""
def nest(path = %PathObject{}) do
%{path.path => %{path.verb => path.operation}}
end
end