defmodule OpenApiSpex.Discriminator do
@moduledoc """
Defines the `OpenApiSpex.Discriminator.t` type.
"""
alias OpenApiSpex.Schema
@enforce_keys :propertyName
defstruct [
:propertyName,
:mapping,
:extensions
]
@typedoc """
[Discriminator Object](https://swagger.io/specification/#discriminatorObject)
When request bodies or response payloads may be one of a number of different schemas,
a discriminator object can be used to aid in serialization, deserialization, and validation.
The discriminator is a specific object in a schema which is used to inform the consumer of the
specification of an alternative schema based on the value associated with it.
"""
@type t :: %__MODULE__{
propertyName: String.t(),
mapping: %{String.t() => String.t()} | nil,
extensions: %{String.t() => any()} | nil
}
@doc """
Resolve the schema that should be used to cast/validate a value using a `Discriminator`.
"""
@spec resolve(t, map, %{String.t() => Schema.t()}) :: {:ok, Schema.t()} | {:error, String.t()}
def resolve(%{propertyName: name, mapping: mapping}, value = %{}, schemas = %{}) do
case get_property_value(value, name) do
{:ok, val} ->
mapped = map_property_value(mapping, val)
lookup_schema(schemas, mapped)
{:error, reason} ->
{:error, reason}
end
end
@spec get_property_value(map, String.t()) :: {:ok, any} | {:error, String.t()}
defp get_property_value(value = %{}, property_name) do
case Map.fetch(value, property_name) do
{:ok, val} -> {:ok, val}
:error -> {:error, "No value for required disciminator property: #{property_name}"}
end
end
@spec map_property_value(%{String.t() => String.t()} | nil, String.t()) :: String.t()
defp map_property_value(nil, val), do: val
defp map_property_value(mapping = %{}, val) do
Map.get(mapping, val, val)
end
@spec lookup_schema(%{String.t() => Schema.t()}, String.t()) ::
{:ok, Schema.t()} | {:error, String.t()}
defp lookup_schema(schemas, "#/components/schemas/" <> name) do
lookup_schema(schemas, name)
end
defp lookup_schema(schemas, name) do
case Map.fetch(schemas, name) do
{:ok, schema} -> {:ok, schema}
:error -> {:error, "Unknown schema: #{name}"}
end
end
end