Packages
open_api_spex
3.6.0
3.22.3
3.22.2
3.22.1
3.22.0
3.21.5
3.21.4
3.21.3
3.21.2
3.21.1
3.21.0
3.20.1
3.20.0
3.19.1
3.19.0
3.18.3
3.18.2
3.18.1
3.18.0
3.17.3
3.17.2
3.17.1
3.17.0
3.16.4
3.16.3
3.16.2
3.16.1
3.16.0
3.15.0
3.14.0
3.13.0
3.12.0
3.11.0
3.10.0
3.9.0
3.8.0
3.7.0
3.6.0
3.5.2
3.5.1
3.5.0
3.4.0
3.3.0
3.2.1
3.2.0
3.1.0
3.0.0
2.3.1
2.3.0
2.2.0
2.1.1
2.1.0
2.0.0
1.1.4
1.1.3
1.1.2
1.1.1
1.1.0
1.0.1
1.0.0
Leverage Open Api Specification 3 (swagger) to document, test, validate and explore your Plug and Phoenix APIs.
Current section
Files
Jump to
Current section
Files
lib/open_api_spex/cast/all_of.ex
defmodule OpenApiSpex.Cast.AllOf do
@moduledoc false
alias OpenApiSpex.Cast
alias OpenApiSpex.Schema
def cast(ctx), do: cast_all_of(ctx, nil)
defp cast_all_of(%{schema: %{allOf: [%Schema{type: :array} = schema | remaining]}} = ctx, acc)
when is_list(acc) or acc == nil do
# Since we parse a multi-type array, acc has to be a list or nil
acc = acc || []
case Cast.cast(%{ctx | schema: schema}) do
{:ok, value} when is_list(value) ->
# Since the cast for the list didn't result in a cast error,
# we do not proceed the values through the remaining schemas
{:ok, Enum.concat(acc, value)}
{:error, errors} ->
with {:ok, cleaned_ctx} <- reject_error_values(ctx, errors) do
case Cast.cast(cleaned_ctx) do
{:ok, cleaned_values} ->
new_ctx = put_in(ctx.schema.allOf, remaining)
new_ctx = update_in(new_ctx.value, fn values -> values -- cleaned_ctx.value end)
cast_all_of(new_ctx, Enum.concat(acc, cleaned_values))
end
else
_ -> Cast.error(ctx, {:all_of, to_string(schema.title || schema.type)})
end
end
end
defp cast_all_of(%{schema: %{allOf: [%Schema{} = schema | remaining]}} = ctx, acc) do
relaxed_schema = %{schema | additionalProperties: true, "x-struct": nil}
new_ctx = put_in(ctx.schema.allOf, remaining)
case Cast.cast(%{ctx | schema: relaxed_schema}) do
{:ok, value} when is_map(value) ->
# Complex allOf Schema
# reject all "additionalProperties"
acc =
value
|> Enum.reject(fn {k, _} -> is_binary(k) end)
|> Enum.concat(acc || %{})
|> Map.new()
cast_all_of(new_ctx, acc)
{:ok, value} ->
# allOf definitions with primitives are a little bit strange..., we just return the cast for the first Schema,
# but validate the values against every other schema as well, since the value must be compatible with all Schemas
cast_all_of(new_ctx, acc || value)
{:error, _} ->
# Since in a allOf Schema, every
Cast.error(ctx, {:all_of, to_string(relaxed_schema.title || relaxed_schema.type)})
end
end
defp cast_all_of(%{schema: %{allOf: [schema | remaining]}} = ctx, result) do
schema = OpenApiSpex.resolve_schema(schema, ctx.schemas)
cast_all_of(%{ctx | schema: %{allOf: [schema | remaining]}}, result)
end
defp cast_all_of(%{schema: schema} = ctx, nil) do
Cast.error(ctx, {:all_of, to_string(schema.title || schema.type)})
end
defp cast_all_of(%{schema: %{allOf: [], "x-struct": module}}, acc) when not is_nil(module),
do: {:ok, struct(module, acc)}
defp cast_all_of(%{schema: %{allOf: []}}, acc) do
# All values have been casted against the allOf schemas - return accumulator
{:ok, acc}
end
defp reject_error_values(%{value: values} = ctx, [%{reason: :invalid_type} = error | tail]) do
new_values = List.delete(values, error.value)
reject_error_values(%{ctx | value: new_values}, tail)
end
defp reject_error_values(ctx, []) do
# All errors should now be resolved for the current schema
{:ok, ctx}
end
defp reject_error_values(_ctx, errors) do
# Some errors couldn't be resolved, we break and return the remaining errors
errors
end
end