Current section
Files
Jump to
Current section
Files
lib/swagger/swagger.ex
defmodule Xcribe.Swagger do
@moduledoc """
Treats list of Requests and generates OpenAPI 3.0 JSON
"""
alias Xcribe.{Config, JSON}
alias Xcribe.Swagger.{Descriptor, Formatter}
@empty_data [nil, %{}, []]
@security_scheme %{
"securitySchemes" => %{
"api_key" => %{"name" => "Authorization", "type" => "apiKey", "in" => "header"}
}
}
def generate_doc(requests) do
requests
|> build_openapi_object()
|> include_security_scheme(requests)
|> json_encode!()
end
defp build_openapi_object(requests) do
%{
"openapi" => "3.0.0",
"info" => %{
"title" => Map.get(xcribe_info(), :name, ""),
"version" => Map.get(xcribe_info(), :version, "0.1.0"),
"description" => Map.get(xcribe_info(), :description, "")
},
"paths" => requests |> Enum.sort_by(& &1.status_code) |> paths_from_requests()
}
end
defp paths_from_requests(requests),
do: Enum.reduce(requests, %{}, &build_path_from_request/2)
defp build_path_from_request(%{path: path} = request, paths) do
Map.update(
paths,
path,
build_path_item_object(request),
&build_path_verb_from_request(&1, request)
)
end
defp build_path_verb_from_request(path, %{verb: verb} = request) do
Map.update(
path,
verb,
build_operation_object(request),
&build_responses_from_request(&1, request)
)
end
defp build_responses_from_request(operation_object, request) do
Map.update(operation_object, "responses", %{}, fn responses ->
Map.merge(responses, Formatter.format_responses(request))
end)
end
defp build_path_item_object(%{verb: verb} = request),
do: %{verb => build_operation_object(request)}
defp build_operation_object(request) do
request
|> base_operation_object
|> parameters_if_needed(request)
|> request_body_if_needed(request)
|> security_if_needed(request)
end
defp base_operation_object(request) do
%{
"summary" => Descriptor.get_action_description(request),
"description" => Descriptor.get_request_description(request),
"responses" => Formatter.format_responses(request)
}
end
defp include_security_scheme(openapi_object, requests) do
requests
|> Enum.any?(&has_authorization_header?/1)
|> put_security_scheme(openapi_object)
end
defp put_security_scheme(false, openapi_object), do: openapi_object
defp put_security_scheme(true, openapi_object),
do: Map.put(openapi_object, "components", @security_scheme)
defp has_authorization_header?(request) do
request
|> Map.fetch!(:header_params)
|> Enum.any?(fn {h, _} -> String.match?(h, ~r/^authorization$/i) end)
end
defp parameters_if_needed(openapi_object, request) do
request
|> Map.take([:path_params, :query_params, :header_params])
|> Enum.any?(&(elem(&1, 1) not in @empty_data))
|> include_parameters(openapi_object, request)
end
defp include_parameters(false, openapi_object, _request), do: openapi_object
defp include_parameters(true, openapi_object, request) do
Map.put(openapi_object, "parameters", Formatter.request_parameters(request))
end
defp request_body_if_needed(openapi_object, %{request_body: body}) when body in @empty_data,
do: openapi_object
defp request_body_if_needed(openapi_object, request) do
Map.put(openapi_object, "requestBody", Formatter.request_body(request))
end
defp security_if_needed(openapi_object, request) do
if has_authorization_header?(request) do
Map.put(openapi_object, "security", [%{"api_key" => []}])
else
openapi_object
end
end
defp json_encode!(map), do: JSON.encode!(map)
defp xcribe_info, do: apply(Config.xcribe_information_source(), :api_info, [])
end