Skip to content

Commit

Permalink
Cast discriminator when no title present (#574)
Browse files Browse the repository at this point in the history
Co-authored-by: Alberto Sartori <[email protected]>
  • Loading branch information
albertored and Alberto Sartori authored Nov 22, 2023
1 parent 7c05fa0 commit 531607c
Show file tree
Hide file tree
Showing 3 changed files with 43 additions and 16 deletions.
34 changes: 19 additions & 15 deletions lib/open_api_spex/cast/discriminator.ex
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ defmodule OpenApiSpex.Cast.Discriminator do
end
end

defp cast_discriminator(%_{value: %{} = value, schema: schema} = ctx) do
defp cast_discriminator(%_{value: %{} = value, schema: schema, schemas: schemas} = ctx) do
{discriminator_property, mappings} = discriminator_details(schema)

case value["#{discriminator_property}"] || value[discriminator_property] do
Expand All @@ -49,7 +49,7 @@ defmodule OpenApiSpex.Cast.Discriminator do
# or return an error according to the Open API Spec.
composite_ctx = %{ctx | schema: %{schema | discriminator: nil}}

cast_composition(composite_ctx, ctx, discriminator_value, mappings)
cast_composition(composite_ctx, ctx, discriminator_value, mappings, schemas)
end
end

Expand All @@ -62,7 +62,8 @@ defmodule OpenApiSpex.Cast.Discriminator do
%_{schema: %{allOf: [_ | _]}} = composite_ctx,
ctx,
discriminator_value,
mappings
mappings,
schemas
) do
with {composite_schemas, cast_composition_result} <-
{locate_composition_schemas(composite_ctx), Cast.cast(composite_ctx)},
Expand All @@ -71,7 +72,8 @@ defmodule OpenApiSpex.Cast.Discriminator do
find_discriminator_schema(
discriminator_value,
mappings,
composite_schemas
composite_schemas,
schemas
) do
Cast.cast(%{composite_ctx | schema: schema})
else
Expand All @@ -80,37 +82,39 @@ defmodule OpenApiSpex.Cast.Discriminator do
end
end

defp cast_composition(composite_ctx, ctx, discriminator_value, mappings) do
defp cast_composition(composite_ctx, ctx, discriminator_value, mappings, schemas) do
with composite_schemas <- locate_composition_schemas(composite_ctx),
%{} = schema <- find_discriminator_schema(discriminator_value, mappings, composite_schemas) do
%{} = schema <-
find_discriminator_schema(discriminator_value, mappings, composite_schemas, schemas) do
Cast.cast(%{composite_ctx | schema: schema})
else
nil -> error(:invalid_discriminator_value, ctx)
{:error, _} = error -> error
end
end

defp find_discriminator_schema(discriminator, mappings = %{}, schemas) do
defp find_discriminator_schema(discriminator, mappings = %{}, composite_schemas, schemas) do
case Map.fetch(mappings, discriminator) do
{:ok, "#/components/schemas/" <> name} ->
find_discriminator_schema(name, nil, schemas)
find_discriminator_schema(name, nil, composite_schemas, schemas) || Map.get(schemas, name)

{:ok, name} ->
find_discriminator_schema(name, nil, schemas)
find_discriminator_schema(name, nil, composite_schemas, schemas)

:error ->
find_discriminator_schema(discriminator, nil, schemas)
find_discriminator_schema(discriminator, nil, composite_schemas, schemas)
end
end

defp find_discriminator_schema(discriminator, _mappings, schemas)
defp find_discriminator_schema(discriminator, nil, composite_schemas, _schemas)
when is_atom(discriminator) and not is_nil(discriminator),
do: Enum.find(schemas, &Kernel.==(&1."x-struct", discriminator))
do: Enum.find(composite_schemas, &Kernel.==(&1."x-struct", discriminator))

defp find_discriminator_schema(discriminator, _mappings, schemas) when is_binary(discriminator),
do: Enum.find(schemas, &Kernel.==(&1.title, discriminator))
defp find_discriminator_schema(discriminator, nil, composite_schemas, _schemas)
when is_binary(discriminator),
do: Enum.find(composite_schemas, &Kernel.==(&1.title, discriminator))

defp find_discriminator_schema(_discriminator, _mappings, _schemas), do: nil
defp find_discriminator_schema(_discriminator, _mappings, _composite_schemas, _schemas), do: nil

defp discriminator_details(%{discriminator: %{propertyName: property_name, mapping: mappings}}),
do: {String.to_existing_atom(property_name), mappings}
Expand Down
23 changes: 23 additions & 0 deletions test/cast/discriminator_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,29 @@ defmodule OpenApiSpex.CastDiscriminatorTest do
assert cast(value: input_value, schema: discriminator_schema) == expected
end

test "without title", %{schemas: %{dog: dog, cat: cat}} do
dog = Map.put(dog, :title, nil)
cat = Map.put(cat, :title, nil)

schemas = %{"Dog" => dog, "Cat" => cat}

discriminator_schema = %OpenApiSpex.Schema{
anyOf: [
%OpenApiSpex.Reference{"$ref": "#/components/schemas/Dog"},
%OpenApiSpex.Reference{"$ref": "#/components/schemas/Cat"}
],
discriminator: %{
mapping: %{"dog" => "#/components/schemas/Dog", "cat" => "#/components/schemas/Cat"},
propertyName: "animal_type"
},
type: :object
}

input_value = %{@discriminator => "dog", "breed" => "Corgi", "age" => 1}
expected = {:ok, %{age: 1, breed: "Corgi", animal_type: "dog"}}
assert cast(value: input_value, schema: discriminator_schema, schemas: schemas) == expected
end

test "valid discriminator mapping but schema does not match", %{
schemas: %{dog: dog, wolf: wolf, cat: cat}
} do
Expand Down
2 changes: 1 addition & 1 deletion test/mix/tasks/openapi.spec.json_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ defmodule Mix.Tasks.Openapi.Spec.JsonTest do
Mix.Tasks.Openapi.Spec.Json.run(~w(
--quiet=true
--spec OpenApiSpexTest.Tasks.SpecModule
"tmp/openapi.json"
tmp/openapi.json
))

refute_received {:mix_shell, :info, ["* creating tmp"]}
Expand Down

0 comments on commit 531607c

Please sign in to comment.