Current section

Files

Jump to
oapi_generator lib open_api spec schema.ex
Raw

lib/open_api/spec/schema.ex

defmodule OpenAPI.Spec.Schema do
@moduledoc false
import OpenAPI.Spec.Helper
alias OpenAPI.Spec
alias OpenAPI.Spec.ExternalDocumentation
alias OpenAPI.Spec.Schema.Discriminator
alias OpenAPI.Spec.Schema.XML
alias OpenAPI.State
#
# Definition
#
@type t :: %__MODULE__{
"$oag_base_file_path": [State.path_segment()],
"$oag_last_ref_file": String.t(),
"$oag_last_ref_path": [State.path_segment()],
title: String.t() | nil,
multiple_of: pos_integer | nil,
maximum: integer | nil,
exclusive_maximum: boolean,
minimum: integer | nil,
exclusive_minimum: boolean,
max_length: non_neg_integer | nil,
min_length: non_neg_integer | nil,
pattern: String.t() | nil,
max_items: non_neg_integer | nil,
min_items: non_neg_integer | nil,
unique_items: boolean,
max_properties: non_neg_integer | nil,
min_properties: non_neg_integer | nil,
required: [String.t()] | nil,
enum: [any] | nil,
type: String.t() | nil,
all_of: [t] | nil,
one_of: [t] | nil,
any_of: [t] | nil,
not: t | nil,
items: t | nil,
properties: %{optional(String.t()) => t},
additional_properties: boolean | t,
description: String.t() | nil,
format: String.t() | nil,
default: any,
nullable: boolean,
discriminator: Spec.Schema.Discriminator.t(),
read_only: boolean,
write_only: boolean,
xml: Spec.Schema.XML.t(),
external_docs: Spec.ExternalDocumentation.t() | nil,
example: any,
deprecated: boolean
}
defstruct [
:"$oag_base_file_path",
:"$oag_last_ref_file",
:"$oag_last_ref_path",
:title,
:multiple_of,
:maximum,
:exclusive_maximum,
:minimum,
:exclusive_minimum,
:max_length,
:min_length,
:pattern,
:max_items,
:min_items,
:unique_items,
:max_properties,
:min_properties,
:required,
:enum,
:type,
:all_of,
:one_of,
:any_of,
:not,
:items,
:properties,
:additional_properties,
:description,
:format,
:default,
:nullable,
:discriminator,
:read_only,
:write_only,
:xml,
:external_docs,
:example,
:deprecated
]
#
# Decoder
#
@spec decode(map, map) :: {map, t}
def decode(state, yaml) do
{state, all_of} = decode_all_of(state, yaml)
{state, one_of} = decode_one_of(state, yaml)
{state, any_of} = decode_any_of(state, yaml)
{state, discriminator} = decode_discriminator(state, yaml)
{state, docs} = decode_external_docs(state, yaml)
{state, xml} = decode_xml(state, yaml)
{state, not_schema} = decode_not(state, yaml)
{state, items} = decode_items(state, yaml)
{state, properties} = decode_properties(state, yaml)
{state, additional_properties} = decode_additional_properties(state, yaml)
schema = %__MODULE__{
"$oag_base_file_path": Map.fetch!(state, :base_file_path) |> Enum.reverse(),
"$oag_last_ref_file": Map.fetch!(state, :last_ref_file),
"$oag_last_ref_path": Map.fetch!(state, :last_ref_path) |> Enum.reverse(),
title: Map.get(yaml, "title"),
multiple_of: Map.get(yaml, "multipleOf"),
maximum: Map.get(yaml, "maximum"),
exclusive_maximum: Map.get(yaml, "exclusiveMaximum", false),
minimum: Map.get(yaml, "minimum"),
exclusive_minimum: Map.get(yaml, "exclusiveMinimum", false),
max_length: Map.get(yaml, "maxLength"),
min_length: Map.get(yaml, "minLength"),
pattern: Map.get(yaml, "pattern"),
max_items: Map.get(yaml, "maxItems"),
min_items: Map.get(yaml, "minItems"),
unique_items: Map.get(yaml, "uniqueItems", false),
max_properties: Map.get(yaml, "maxProperties"),
min_properties: Map.get(yaml, "minProperties"),
required: Map.get(yaml, "required"),
enum: Map.get(yaml, "enum"),
type: Map.get(yaml, "type"),
all_of: all_of,
one_of: one_of,
any_of: any_of,
not: not_schema,
items: items,
properties: properties,
additional_properties: additional_properties,
description: Map.get(yaml, "description"),
format: Map.get(yaml, "format"),
default: Map.get(yaml, "default"),
nullable: Map.get(yaml, "nullable", false),
discriminator: discriminator,
read_only: Map.get(yaml, "readOnly", false),
write_only: Map.get(yaml, "writeOnly", false),
xml: xml,
external_docs: docs,
example: Map.get(yaml, "example"),
deprecated: Map.get(yaml, "deprecated", false)
}
{state, schema}
end
@spec decode_discriminator(map, map) :: {map, Discriminator.t() | nil}
defp decode_discriminator(state, %{"discriminator" => discriminator}),
do: with_path(state, discriminator, "discriminator", &Discriminator.decode/2)
defp decode_discriminator(state, _discriminator), do: {state, nil}
@spec decode_external_docs(map, map) :: {map, ExternalDocumentation.t() | nil}
defp decode_external_docs(state, %{"externalDocs" => docs}),
do: with_path(state, docs, "externalDocs", &ExternalDocumentation.decode/2)
defp decode_external_docs(state, _docs), do: {state, nil}
@spec decode_xml(map, map) :: {map, XML.t() | nil}
defp decode_xml(state, %{"xml" => xml}),
do: with_path(state, xml, "xml", &XML.decode/2)
defp decode_xml(state, _xml), do: {state, nil}
@spec decode_all_of(map, map) :: {map, t | nil}
defp decode_all_of(state, %{"allOf" => all_of}) do
with_path(state, all_of, "allOf", fn state, all_of ->
all_of
|> Enum.with_index()
|> Enum.reverse()
|> Enum.reduce({state, []}, fn {schema_or_ref, index}, {state, list} ->
{state, element} =
with_path(state, schema_or_ref, index, fn state, schema_or_ref ->
with_ref(state, schema_or_ref, &decode/2)
end)
{state, [element | list]}
end)
end)
end
defp decode_all_of(state, _schema), do: {state, nil}
@spec decode_one_of(map, map) :: {map, t | nil}
defp decode_one_of(state, %{"oneOf" => one_of}) do
with_path(state, one_of, "oneOf", fn state, one_of ->
one_of
|> Enum.with_index()
|> Enum.reverse()
|> Enum.reduce({state, []}, fn {schema_or_ref, index}, {state, list} ->
{state, element} =
with_path(state, schema_or_ref, index, fn state, schema_or_ref ->
with_ref(state, schema_or_ref, &decode/2)
end)
{state, [element | list]}
end)
end)
end
defp decode_one_of(state, _schema), do: {state, nil}
@spec decode_any_of(map, map) :: {map, t | nil}
defp decode_any_of(state, %{"anyOf" => any_of}) do
with_path(state, any_of, "anyOf", fn state, any_of ->
any_of
|> Enum.with_index()
|> Enum.reverse()
|> Enum.reduce({state, []}, fn {schema_or_ref, index}, {state, list} ->
{state, element} =
with_path(state, schema_or_ref, index, fn state, schema_or_ref ->
with_ref(state, schema_or_ref, &decode/2)
end)
{state, [element | list]}
end)
end)
end
defp decode_any_of(state, _schema), do: {state, nil}
@spec decode_not(map, map) :: {map, t | nil}
defp decode_not(state, %{"not" => schema}) do
with_path(state, schema, "not", fn state, schema ->
with_ref(state, schema, &decode/2)
end)
end
defp decode_not(state, _schema), do: {state, nil}
@spec decode_items(map, map) :: {map, t | nil}
defp decode_items(state, %{"items" => schema}) do
with_path(state, schema, "items", fn state, schema ->
with_ref(state, schema, &decode/2)
end)
end
defp decode_items(state, _schema), do: {state, nil}
@spec decode_properties(map, map) :: {map, %{optional(String.t()) => t}}
defp decode_properties(state, %{"properties" => properties}) do
with_path(state, properties, "properties", fn state, properties ->
Enum.reduce(properties, {state, %{}}, fn
{key, schema}, {state, properties} ->
{state, property} =
with_path(state, schema, key, fn state, schema ->
with_ref(state, schema, &decode/2)
end)
{state, Map.put(properties, key, property)}
end)
end)
end
defp decode_properties(state, _schema), do: {state, %{}}
@spec decode_additional_properties(map, map) :: {map, t | boolean}
defp decode_additional_properties(state, %{"additionalProperties" => value})
when is_boolean(value),
do: {state, value}
defp decode_additional_properties(state, %{"additionalProperties" => schema}) do
with_path(state, schema, "additionalProperties", fn state, schema ->
with_ref(state, schema, &decode/2)
end)
end
defp decode_additional_properties(state, _schema), do: {state, true}
end