Current section

Files

Jump to
oapi_generator lib open_api spec.ex
Raw

lib/open_api/spec.ex

defmodule OpenAPI.Spec do
@moduledoc false
import OpenAPI.Spec.Helper
alias OpenAPI.Spec
alias OpenAPI.Spec.Components
alias OpenAPI.Spec.ExternalDocumentation
alias OpenAPI.Spec.Info
alias OpenAPI.Spec.Path.Item
alias OpenAPI.Spec.Server
alias OpenAPI.Spec.Tag
alias OpenAPI.State
#
# Definition
#
@typedoc "Open API specification"
@type t :: %__MODULE__{
openapi: String.t(),
info: Info.t(),
servers: [Server.t()],
paths: %{optional(:string) => Item.t()},
components: Components.t(),
security: [term],
tags: [term],
external_docs: ExternalDocumentation.t() | nil
}
defstruct [
:openapi,
:info,
:servers,
:paths,
:components,
:security,
:tags,
:external_docs
]
#
# Decoder
#
@spec decode(State.t()) :: State.t()
def decode(state) do
yaml = state.files[state.base_file]
{state, info} = decode_info(state, yaml)
{state, servers} = decode_servers(state, yaml)
{state, components} = decode_components(state, yaml)
{state, paths} = decode_paths(state, yaml)
{state, tags} = decode_tags(state, yaml)
{state, external_docs} = decode_external_docs(state, yaml)
spec = %__MODULE__{
openapi: Map.fetch!(yaml, "openapi"),
info: info,
servers: servers,
paths: paths,
components: components,
security: [],
tags: tags,
external_docs: external_docs
}
%State{state | spec: spec}
end
@spec decode_info(State.t(), State.yaml()) :: {State.t(), Info.t()}
defp decode_info(state, %{"info" => info}), do: with_path(state, info, "info", &Info.decode/2)
@spec decode_servers(State.t(), State.yaml()) :: {State.t(), [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, [%Spec.Server{url: "/"}]}
@spec decode_components(State.t(), State.yaml()) :: {State.t(), Components.t()}
defp decode_components(state, %{"components" => components}) do
with_path(state, components, "components", &Components.decode/2)
end
@spec decode_tags(State.t(), State.yaml()) :: {State.t(), [Tag.t()]}
defp decode_tags(state, %{"tags" => tags}) do
with_path(state, tags, "tags", fn state, tags ->
{state, tags} =
tags
|> Enum.with_index()
|> Enum.reduce({state, []}, fn {tag, index}, {state, tags} ->
{state, tag} = with_path(state, tag, index, &Tag.decode/2)
{state, [tag | tags]}
end)
{state, Enum.reverse(tags)}
end)
end
@spec decode_external_docs(State.t(), State.yaml()) :: {State.t(), ExternalDocumentation.t()}
defp decode_external_docs(state, %{"externalDocs" => docs}) do
with_path(state, docs, "externalDocs", &ExternalDocumentation.decode/2)
end
defp decode_external_docs(state, _docs), do: {state, nil}
@spec decode_paths(State.t(), State.yaml()) :: {State.t(), %{optional(String.t()) => Item.t()}}
defp decode_paths(state, %{"paths" => paths}) do
with_path(state, paths, "paths", fn state, paths ->
Enum.reduce(paths, {state, %{}}, fn {key, path}, {state, paths} ->
{state, path} = with_path(state, path, key, &Item.decode/2)
{state, Map.put(paths, key, path)}
end)
end)
end
end