Current section

Files

Jump to
api_doc lib format open_api_3.ex
Raw

lib/format/open_api_3.ex

defmodule APIDoc.Format.OpenAPI3 do
@moduledoc ~S"""
"""
def format(document) do
paths = document.endpoints |> Enum.map(&paths/1) |> Enum.join("\n")
schemas = document.schemas |> Enum.map(&format_schema/1) |> Enum.join("\n")
securitySchemes = document.security |> Enum.map(&format_security/1) |> Enum.join("\n")
"""
openapi: 3.0.0
info:
#{info(document.info)}
servers:
#{servers(document.servers)}
paths:
#{paths}
components:
schemas:
#{schemas}
securitySchemes:
#{securitySchemes}
"""
|> String.replace(~r/\n{2,}/, "\n")
end
defp info(info) do
contact =
if info.contact do
"""
contact:
name: #{info.contact.name}
email: #{info.contact.email}
"""
else
""
end
"""
title: #{info.name}
description: |
#{info.description |> String.split("\n") |> Enum.join(" \n")}
version: #{info.version}
#{contact}
"""
end
defp servers(servers) do
servers
|> Enum.map(
&"""
- url: #{&1.url}
description: #{&1.description}
"""
)
|> Enum.join("\n")
end
defp paths(data, pre \\ "", security \\ [])
defp paths(%{path: path, endpoints: endpoints, security: secure}, pre, security) do
path = pre <> format_path(path)
security = secure ++ security
endpoints |> Enum.sort_by(&Enum.count(&1.path)) |> Enum.map(&paths(&1, path, security))
|> Enum.join("\n")
end
defp paths(data = %{id: api, security: secure}, pre, security) do
path = pre <> format_path(data.path)
security = secure ++ security
"""
#{path}:
#{data.method}:
operationId: #{api}
#{format_summary(data.summary)}
#{format_description(data.description)}
#{format_tags(data.tags)}
#{format_params(data.parameters)}
responses:
#{data.responses |> Enum.map(&format_response/1) |> Enum.join("")}
security:
#{security |> Enum.map(&format_security_use/1) |> Enum.join("\n")}
"""
end
def format_security_use([secure | security]) do
[
" - #{Macro.to_string(secure)}: []"
| security |> Enum.map(&" #{Macro.to_string(&1)}: []")
]
|> Enum.join("\n")
end
def format_security_use(secure) do
" - #{Macro.to_string(secure)}: []"
end
def format_description(nil), do: ""
def format_description(description) do
" description: |\n " <>
(description |> String.split("\n") |> Enum.join("\n "))
end
defp format_summary(nil), do: ""
defp format_summary(summary), do: " summary: " <> summary
defp format_tags([]), do: ""
defp format_tags(tags),
do: " tags:\n" <> (tags |> Enum.map(&(" - " <> &1)) |> Enum.join("\n"))
defp format_path([]), do: ""
defp format_path(path) do
"/" <>
(path
|> Enum.map(&if(is_atom(&1), do: "{#{&1}}", else: &1))
|> Enum.join("/"))
end
defp format_params([]), do: ""
defp format_params(params),
do: " parameters:\n" <> (params |> Enum.map(&create_param/1) |> Enum.join(""))
defp create_param(%{
name: name,
type: type,
description: description,
required: required,
schema: schema
}) do
schema =
schema
|> Kernel.||(%{type: "string"})
|> Enum.map(fn {k, v} -> " #{k}: #{v}" end)
|> Enum.join("\n")
"""
- name: #{name}
in: #{type}
description: #{description}
required: #{if required, do: "true", else: "false"}
schema:
#{schema}
"""
end
defp format_response(%{
status: status,
description: description,
content: content
}) do
status = if status == :default, do: "default", else: "'#{status}'"
responses =
content
|> Enum.map(fn {response, type} ->
"""
#{response}:
schema:
$ref: "#/components/schemas/#{type}"
"""
end)
|> Enum.join("\n")
"""
#{status}:
description: #{description}
content:
#{responses}
"""
end
defp format_schema(schema = %{type: :object}) do
name = schema.name |> to_string() |> String.trim_leading("Elixir.")
"""
#{name}:
required:
#{schema.required |> Enum.map(&" - #{&1}") |> Enum.join("\n")}
properties:
#{
schema.properties |> Enum.map(&format_property(&1, schema.example || %{}))
|> Enum.join("\n")
}
"""
end
defp format_schema(schema) do
name = schema.name |> to_string() |> String.trim_leading("Elixir.")
example = if schema.example, do: " example: #{inspect(schema.example)}", else: ""
items = if schema.items, do: " items:\n type: #{schema.items.type}", else: ""
"""
#{name}:
type: #{schema.type}
#{example}
#{items}
"""
end
defp format_property({property, data}, example) do
example =
if Map.get(example, property),
do: " example: #{inspect(Map.get(example, property))}",
else: ""
"""
#{property}:
type: #{data.type}
#{example}
"""
end
defp format_security({id, schema}) do
name = id |> to_string() |> String.trim_leading("Elixir.")
description =
if schema.description, do: " description: #{inspect(schema.description)}", else: ""
"""
#{name}:
type: #{schema.type}
in: #{schema.in}
name: #{schema.name}
#{description}
"""
end
end