Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F113964
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Award Token
Flag For Later
Size
5 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/lib/open_api_spex/controller.ex b/lib/open_api_spex/controller.ex
index ac79793..bf9389f 100644
--- a/lib/open_api_spex/controller.ex
+++ b/lib/open_api_spex/controller.ex
@@ -1,188 +1,188 @@
defmodule OpenApiSpex.Controller do
@moduledoc ~S'''
Generation of OpenAPI documentation via ExDoc documentation and tags.
## Supported OpenAPI fields
### `description` and `summary`
Description of endpoint will be filled with documentation string in the same
manner as ExDocs, so first line will be used as a `summary` and whole
documentation will be used as `description` field.
### `operation_id`
The action's `operation_id` can be set explicitly using a `@doc` tag.
If no `operation_id` is specified, it will default to the action's module path: `Module.Name.function_name`
### `parameters`
Parameters of the endpoint are defined by `:parameters` tag which should be
map or keyword list that is formed as:
```
[
param_name: definition
]
```
Where `definition` is `OpenApiSpex.Parameter.t()` structure or map or keyword
list that accepts the same arguments.
### `responses`
Responses are controlled by `:responses` tag. Responses must be defined as
a map or keyword list in form of:
```
%{
200 => {"Response name", "application/json", schema},
:not_found => {"Response name", "application/json", schema}
}
```
Where atoms are the same as `Plug.Conn.Status.code/1` values.
### `requestBody`
Controlled by `:request_body` parameter and is defined as a tuple in form
`{description, mime, schema}` or `{description, mime, schema, opts}` that
matches the arguments of `OpenApiSpex.Operation.request_body/3` or
`OpenApiSpex.Operation.request_body/4`, respectively.
```
@doc request_body: {
"CartUpdateRequest",
"application/vnd.api+json",
CartUpdateRequest,
required: true
}
```
### `tags`
Tags are controlled by `:tags` attribute. In contrast to other attributes, this
one will also inherit all tags defined as a module documentation attributes.
## Example
```
defmodule FooController do
use MyAppWeb, :controller
use #{inspect(__MODULE__)}
@moduledoc tags: ["Foos"]
@doc """
Endpoint summary
Endpoint description...
"""
@doc parameters: [
id: [in: :path, type: :string, required: true]
],
request_body: {"Request body to update Foo", "application/json", FooUpdateBody, required: true},
responses: [
ok: {"Foo document", "application/json", FooSchema}
]
def update(conn, %{id: id}) do
foo_params = conn.body_params
# …
end
end
```
'''
alias OpenApiSpex.{Operation, Response}
defmacro __using__(_opts) do
quote do
@doc false
@spec open_api_operation(atom()) :: OpenApiSpex.Operation.t()
def open_api_operation(name),
do: unquote(__MODULE__).__api_operation__(__MODULE__, name)
defoverridable open_api_operation: 1
end
end
@doc false
@spec __api_operation__(module(), atom()) :: Operation.t() | nil
def __api_operation__(mod, name) do
with {:ok, {mod_meta, summary, docs, meta}} <- get_docs(mod, name) do
%Operation{
description: docs,
operationId: build_operation_id(meta, mod, name),
parameters: build_parameters(meta),
requestBody: build_request_body(meta),
responses: build_responses(meta),
summary: summary,
tags: Map.get(mod_meta, :tags, []) ++ Map.get(meta, :tags, [])
}
else
_ -> nil
end
end
defp get_docs(module, name) do
{:docs_v1, _anno, _lang, _format, _module_doc, mod_meta, mod_docs} = Code.fetch_docs(module)
{_, _, _, docs, meta} =
Enum.find(mod_docs, fn
{{:function, ^name, _}, _, _, _, _} -> true
_ -> false
end)
if docs == :none do
:error
else
docs = Map.get(docs, "en", "")
[summary | _] = String.split(docs, ~r/\n\s*\n/, parts: 2)
{:ok, {mod_meta, summary, docs, meta}}
end
end
defp build_operation_id(meta, mod, name) do
Map.get(meta, :operation_id, "#{inspect(mod)}.#{name}")
end
defp build_parameters(%{parameters: params}) do
for {name, options} <- params do
{location, options} = Keyword.pop(options, :in, :query)
{type, options} = Keyword.pop(options, :type, :string)
- {description, options} = Keyword.pop(options, :description, :string)
+ {description, options} = Keyword.pop(options, :description, "")
Operation.parameter(name, location, type, description, options)
end
end
defp build_parameters(_), do: []
defp build_responses(%{responses: responses}) do
Map.new(responses, fn
{status, {description, mime, schema}} ->
{Plug.Conn.Status.code(status), Operation.response(description, mime, schema)}
{status, %Response{} = response} ->
{Plug.Conn.Status.code(status), response}
end)
end
defp build_responses(_), do: []
defp build_request_body(%{body: {name, mime, schema}}) do
IO.warn("Using :body key for requestBody is deprecated. Please use :request_body instead.")
Operation.request_body(name, mime, schema)
end
defp build_request_body(%{request_body: {name, mime, schema}}) do
Operation.request_body(name, mime, schema)
end
defp build_request_body(%{request_body: {name, mime, schema, opts}}) do
Operation.request_body(name, mime, schema, opts)
end
defp build_request_body(_), do: nil
end
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Mon, Nov 25, 5:54 PM (1 d, 11 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
39982
Default Alt Text
(5 KB)
Attached To
Mode
R22 open_api_spex
Attached
Detach File
Event Timeline
Log In to Comment