defmodule OpenAPI.Spec.Path.Item do
@moduledoc "Raw operation path from the OpenAPI spec"
import OpenAPI.Reader.State
alias OpenAPI.Spec.Path.Operation
alias OpenAPI.Spec.Path.Parameter
alias OpenAPI.Spec.Server
@type t :: %__MODULE__{
summary: String.t() | nil,
description: String.t() | nil,
get: Operation.t() | nil,
put: Operation.t() | nil,
post: Operation.t() | nil,
delete: Operation.t() | nil,
options: Operation.t() | nil,
head: Operation.t() | nil,
patch: Operation.t() | nil,
trace: Operation.t() | nil,
servers: [Server.t()],
parameters: [Parameter.t()]
}
defstruct [
:summary,
:description,
:get,
:put,
:post,
:delete,
:options,
:head,
:patch,
:trace,
:servers,
:parameters
]
@doc false
@spec decode(map, map) :: {map, t}
def decode(state, yaml) do
{state, parameters} = decode_parameters(state, yaml)
{state, servers} = decode_servers(state, yaml)
state = %{state | path_parameters: parameters}
{state, get} = decode_get(state, yaml)
{state, put} = decode_put(state, yaml)
{state, post} = decode_post(state, yaml)
{state, delete} = decode_delete(state, yaml)
{state, options} = decode_options(state, yaml)
{state, head} = decode_head(state, yaml)
{state, patch} = decode_patch(state, yaml)
{state, trace} = decode_trace(state, yaml)
state = %{state | path_parameters: []}
item = %__MODULE__{
summary: Map.get(yaml, "summary"),
description: Map.get(yaml, "description"),
get: get,
put: put,
post: post,
delete: delete,
options: options,
head: head,
patch: patch,
trace: trace,
servers: servers,
parameters: parameters
}
{state, item}
end
@spec decode_get(map, map) :: {map, Operation.t() | nil}
def decode_get(state, %{"get" => get}) do
with_path(state, get, "get", &Operation.decode/2)
end
def decode_get(state, _yaml), do: {state, nil}
@spec decode_put(map, map) :: {map, Operation.t() | nil}
def decode_put(state, %{"put" => put}) do
with_path(state, put, "put", &Operation.decode/2)
end
def decode_put(state, _yaml), do: {state, nil}
@spec decode_post(map, map) :: {map, Operation.t() | nil}
def decode_post(state, %{"post" => post}) do
with_path(state, post, "post", &Operation.decode/2)
end
def decode_post(state, _yaml), do: {state, nil}
@spec decode_delete(map, map) :: {map, Operation.t() | nil}
def decode_delete(state, %{"delete" => delete}) do
with_path(state, delete, "delete", &Operation.decode/2)
end
def decode_delete(state, _yaml), do: {state, nil}
@spec decode_options(map, map) :: {map, Operation.t() | nil}
def decode_options(state, %{"options" => options}) do
with_path(state, options, "options", &Operation.decode/2)
end
def decode_options(state, _yaml), do: {state, nil}
@spec decode_head(map, map) :: {map, Operation.t() | nil}
def decode_head(state, %{"head" => head}) do
with_path(state, head, "head", &Operation.decode/2)
end
def decode_head(state, _yaml), do: {state, nil}
@spec decode_patch(map, map) :: {map, Operation.t() | nil}
def decode_patch(state, %{"patch" => patch}) do
with_path(state, patch, "patch", &Operation.decode/2)
end
def decode_patch(state, _yaml), do: {state, nil}
@spec decode_trace(map, map) :: {map, Operation.t() | nil}
def decode_trace(state, %{"trace" => trace}) do
with_path(state, trace, "trace", &Operation.decode/2)
end
def decode_trace(state, _yaml), do: {state, nil}
@spec decode_parameters(map, map) :: {map, [Parameter.t()]}
def decode_parameters(state, %{"parameters" => parameters}) do
with_path(state, parameters, "parameters", fn state, parameters ->
parameters
|> Enum.with_index()
|> Enum.reverse()
|> Enum.reduce({state, []}, fn {parameter, index}, {state, list} ->
{state, element} =
with_path(state, parameter, index, fn state, parameter ->
with_ref(state, parameter, &Parameter.decode/2)
end)
{state, [element | list]}
end)
end)
end
def decode_parameters(state, _yaml), do: {state, []}
@spec decode_servers(map, map) :: {map, [Server.t()]}
defp decode_servers(state, %{"servers" => servers}) when is_list(servers) do
with_path(state, servers, "servers", fn state, servers ->
{state, servers} =
servers
|> Enum.with_index()
|> Enum.reduce({state, []}, fn {server, index}, {state, servers} ->
{state, server} = with_path(state, server, index, &Server.decode/2)
{state, [server | servers]}
end)
{state, Enum.reverse(servers)}
end)
end
defp decode_servers(state, _yaml), do: {state, [%Server{url: "/"}]}
def merge(item_one, item_two) do
%__MODULE__{
summary: item_two.summary || item_one.summary,
description: item_two.description || item_one.description,
get: item_two.get || item_one.get,
put: item_two.put || item_one.put,
post: item_two.post || item_one.post,
delete: item_two.delete || item_one.delete,
options: item_two.options || item_one.options,
head: item_two.head || item_one.head,
patch: item_two.patch || item_one.patch,
trace: item_two.trace || item_one.trace,
servers: Enum.uniq_by(item_two.servers ++ item_one.servers, & &1.url),
parameters: Enum.uniq_by(item_two.parameters ++ item_one.parameters, &{&1.name, &1.in})
}
end
end