Current section
Files
Jump to
Current section
Files
lib/api_blueprint/formatter.ex
defmodule Xcribe.ApiBlueprint.Formatter do
@moduledoc false
alias Xcribe.{ContentDecoder, JsonSchema, Request}
import Xcribe.Helpers.Formatter
def merge_request(requests, new_request) do
group_key = object_key(new_request)
Map.update(
requests,
group_key,
new_request[group_key],
&merge_group(&1, new_request[group_key])
)
end
def full_request_object(%Request{resource_group: group} = request) do
%{
capitalize_all_words(to_string(group)) => %{
summary: "",
description: "",
resources: resource_object(request)
}
}
end
def resource_object(%Request{} = request) do
%{
resource_key(request) => %{
name: resource_name(request),
summary: "",
description: "",
parameters: resource_parameters(request),
actions: action_object(request)
}
}
end
def action_object(%Request{} = request) do
%{
action_key(request) => %{
name: action_name(request),
summary: "",
description: "",
parameters: action_parameters(request),
requests: request_object(request)
}
}
end
def request_object(%Request{description: desc, header_params: headers} = request) do
%{
desc => %{
content_type: content_type(headers),
headers: headers(headers),
body: request.request_body,
schema: request_schema(request),
response: response_object(request)
}
}
end
def response_object(%Request{resp_headers: headers, status_code: code} = request) do
%{
status: code,
content_type: content_type(headers),
headers: headers(headers),
body: response_body(request),
schema: response_schema(request)
}
end
def action_parameters(%Request{path_params: path_params}) do
Enum.reduce(path_params, %{}, &reduce_path_params/2)
end
def resource_parameters(%Request{path: path, path_params: path_params}) do
path_params
|> Map.take(url_params(path))
|> Enum.reduce(%{}, &reduce_path_params/2)
end
def response_schema(%Request{resp_body: body, resp_headers: headers}) do
headers
|> content_type()
|> json_schema_for(body)
end
def response_body(%Request{resp_body: body, resp_headers: headers}) do
case content_type(headers) do
nil -> %{}
content_type -> ContentDecoder.decode!(body, content_type)
end
end
def request_schema(%Request{request_body: body}) when body == %{}, do: %{}
def request_schema(%Request{request_body: body, header_params: headers}) do
headers
|> content_type()
|> json_schema_for(body)
end
def action_name(%Request{action: action, resource: resource}),
do: "#{capitalize_all_words(resource)}#{action}"
def action_key(%Request{path: path, verb: verb}) do
Enum.reduce(
url_params(path),
"#{String.upcase(verb)} #{path}",
&String.replace(&2, &1, format_path_parameter(&1))
)
end
def resource_name(%Request{resource: resource}),
do: resource |> capitalize_all_words() |> String.trim()
def resource_key(%Request{path: path}) do
Enum.reduce(
url_params(path),
resource_path(path),
&String.replace(&2, &1, format_path_parameter(&1))
)
end
defp merge_group(group, new_request),
do: %{group | resources: merge_group_resources(group.resources, new_request.resources)}
defp merge_group_resources(resources, new_request) do
resource_key = object_key(new_request)
Map.update(
resources,
resource_key,
new_request[resource_key],
&merge_resource(&1, new_request[resource_key])
)
end
defp merge_resource(resource, new_request) do
%{resource | actions: merge_resource_actions(resource.actions, new_request.actions)}
end
defp merge_resource_actions(actions, new_request) do
action_key = object_key(new_request)
Map.update(
actions,
action_key,
new_request[action_key],
&merge_action(&1, new_request[action_key])
)
end
defp merge_action(action, new_request) do
%{action | requests: Map.merge(action.requests, new_request.requests)}
end
defp object_key(%{} = request) do
request
|> Map.keys()
|> List.first()
end
defp resource_path(path) do
~r/(.*)(?=\/{.*}$)|(.*)/
|> Regex.run(path, capture: :all_but_first)
|> List.last()
end
defp capitalize_all_words(string),
do: Enum.reduce(String.split(string, "_"), "", &"#{&2}#{String.capitalize(&1)} ")
defp json_schema_for("application/json" = type, body) when is_binary(body),
do: json_schema_for(type, ContentDecoder.decode!(body, type))
defp json_schema_for("application/json", body) when is_map(body) or is_list(body),
do: JsonSchema.schema_for(body)
defp json_schema_for(_content_type, _body), do: %{}
defp reduce_path_params({param, value}, parameters) do
Map.put(
parameters,
format_path_parameter(param),
schema_for(value, true)
)
end
defp schema_for(value, required) do
{nil, value}
|> JsonSchema.schema_for()
|> add_required(required)
end
defp headers(headers), do: headers |> Enum.into(%{}) |> Map.delete("content-type")
defp add_required(map, true), do: Map.put(map, :required, true)
defp url_params(path) do
case Regex.run(~r/\{(.*?)\}.+/, path, capture: :all_but_first) do
nil -> []
result -> result
end
end
end