Packages
phoenix_swagger_ro
0.8.3
PhoenixSwagger is the library that provides swagger integration to the phoenix web framework adding the readonly property.
Current section
Files
Jump to
Current section
Files
lib/phoenix_swagger.ex
defmodule PhoenixSwagger do
use Application
alias PhoenixSwagger.Path
alias PhoenixSwagger.Path.PathObject
@moduledoc """
The PhoenixSwagger module provides macros for defining swagger operations and schemas.
Example:
use PhoenixSwagger
swagger_path :create do
post "/api/v1/{team}/users"
summary "Create a new user"
consumes "application/json"
produces "application/json"
parameters do
user :body, Schema.ref(:User), "user attributes"
team :path, :string, "Users team ID"
end
response 200, "OK", Schema.ref(:User)
end
def swagger_definitions do
%{
User: swagger_schema do
title "User"
description "A user of the application"
properties do
name :string, "Users name", required: true
id :string, "Unique identifier", required: true
address :string, "Home adress"
end
end
}
end
"""
@table :validator_table
def start(_type, _args) do
import Supervisor.Spec, warn: false
children = [
# Define workers and child supervisors to be supervised
# worker(Test.Worker, [arg1, arg2, arg3]),
]
:ets.new(@table, [:public, :named_table])
# See http://elixir-lang.org/docs/stable/elixir/Supervisor.html
# for other strategies and supported options
opts = [strategy: :one_for_one, name: Test.Supervisor]
Supervisor.start_link(children, opts)
end
defmacro __using__(_) do
quote do
import PhoenixSwagger
require PhoenixSwagger.Schema, as: Schema
require PhoenixSwagger.JsonApi, as: JsonApi
end
end
@doc """
Builds a swagger schema map using a DSL from the functions defined in `PhoenixSwagger.Schema`.
## Example
iex> use PhoenixSwagger
...> swagger_schema do
...> title "Pet"
...> description "A pet in the pet store"
...> properties do
...> id :integer, "Unique identifier", required: true, format: :int64
...> name :string, "Pets name", required: true
...> tags array(:string), "Tag categories for this pet"
...> end
...> additional_properties false
...> end
%{
"title" => "Pet",
"type" => "object",
"description" => "A pet in the pet store",
"properties" => %{
"id" => %{
"description" => "Unique identifier",
"format" => "int64",
"type" => "integer"
},
"name" => %{
"description" => "Pets name",
"type" => "string"
},
"tags" => %{
"description" => "Tag categories for this pet",
"items" => %{
"type" => "string"
},
"type" => "array"
}
},
"required" => ["name", "id"],
"additionalProperties" => false
}
iex> use PhoenixSwagger
...> swagger_schema do
...> title "Phone"
...> description "An 8 digit phone number with optional 2 digit area code"
...> type :string
...> max_length 11
...> pattern ~S"^(\([0-9]{2}\))?[0-9]{4}-[0-9]{4}$"
...> end
%{
"description" => "An 8 digit phone number with optional 2 digit area code",
"maxLength" => 11,
"pattern" => "^(\\([0-9]{2}\\))?[0-9]{4}-[0-9]{4}$",
"title" => "Phone",
"type" => "string"
}
"""
defmacro swagger_schema(block) do
exprs =
case block do
[do: {:__block__, _, exprs}] -> exprs
[do: expr] -> [expr]
end
acc =
quote do
%Schema{type: :object}
end
body =
Enum.reduce(exprs, acc, fn expr, acc ->
quote do
unquote(acc) |> unquote(expr)
end
end)
# Immediately invoked anonymous function for locally scoped import
quote do
(fn ->
import PhoenixSwagger.Schema
alias PhoenixSwagger.Schema
unquote(body)
|> PhoenixSwagger.to_json()
end).()
end
end
@doc """
Swagger operations (aka "paths") are defined inside a `swagger_path` block.
Within the do-end block, the DSL provided by the `PhoenixSwagger.Path` module can be used.
The DSL is any chain of functions with first argument being a `PhoenixSwagger.Path.PathObject` struct.
The verb and path can be set explicitly using the `get`, `put`, `post`, `patch`, `delete` functions, or
inferred from the phoenix router automatically.
Swagger `tags` will default to match the module name with trailing `Controller` removed.
Eg operations defined in module MyApp.UserController will have `tags: ["User"]`.
Swagger `operationId` will default to the fully qualified action function name.
Eg `index` action in `MyApp.UserController` will have `operationId: "MyApp.UserController.index"`.
## Example
defmodule ExampleController do
use ExampleApp.Web, :controller
use PhoenixSwagger
swagger_path :index do
get "/users"
summary "Get users"
description "Get users, filtering by account ID"
parameter :query, :id, :integer, "account id", required: true
response 200, "Description", :Users
tag "users"
end
def index(conn, _params) do
posts = Repo.all(Post)
render(conn, "index.json", posts: posts)
end
end
"""
defmacro swagger_path(action, do: {:__block__, _, [first_expr | exprs]}) do
fun_name = "swagger_path_#{action}" |> String.to_atom()
body =
Enum.reduce(exprs, first_expr, fn expr, acc ->
quote do
unquote(acc) |> unquote(expr)
end
end)
quote do
def unquote(fun_name)(route) do
import PhoenixSwagger.Path
%PhoenixSwagger.Path.PathObject{}
|> unquote(body)
|> PhoenixSwagger.ensure_operation_id(__MODULE__, unquote(action))
|> PhoenixSwagger.ensure_tag(__MODULE__)
|> PhoenixSwagger.ensure_verb_and_path(route)
|> PhoenixSwagger.Path.nest()
|> PhoenixSwagger.to_json()
end
end
end
@doc false
# Add a default operationId based on model name and action if required
def ensure_operation_id(path = %PathObject{operation: %{operationId: ""}}, module, action) do
Path.operation_id(path, String.replace_prefix("#{module}.#{action}", "Elixir.", ""))
end
def ensure_operation_id(path, _module, _action), do: path
@doc false
# Add a default tag based on controller module name if none present
def ensure_tag(path = %PathObject{operation: %{tags: []}}, module) do
tags =
module
|> Module.split()
|> Enum.reverse()
|> hd()
|> String.split("Controller")
|> Enum.filter(&(String.length(&1) > 0))
put_in(path.operation.tags, tags)
end
def ensure_tag(path, _module), do: path
@doc false
# Allow the verb and path to be inferred from the phoenix route
def ensure_verb_and_path(path = %PathObject{verb: nil, path: nil}, route) do
%{path | verb: route.verb, path: route.path}
end
def ensure_verb_and_path(path, _route), do: path
@doc """
Use JSON library from phoenix configuration
"""
def json_library do
Application.get_env(:phoenix_swagger, :json_library, Poison)
end
@doc false
# Converts a Schema struct to regular map, removing nils
def to_json(value = %{__struct__: _}) do
value
|> Map.from_struct()
|> to_json()
end
def to_json(value) when is_map(value) do
value
|> Enum.map(fn {k, v} -> {to_string(k), to_json(v)} end)
|> Enum.filter(fn
{_, :null} -> false
_ -> true
end)
|> Enum.into(%{})
end
def to_json(value) when is_list(value) do
Enum.map(value, &to_json/1)
end
def to_json(nil) do
:null
end
def to_json(:null) do
:null
end
def to_json(true) do
true
end
def to_json(false) do
false
end
def to_json(value) when is_atom(value) do
to_string(value)
end
def to_json(value) do
value
end
end