Skip to content

Casting using oneOf not implemented? #26

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
maxmellen opened this issue Jun 27, 2018 · 3 comments
Closed

Casting using oneOf not implemented? #26

maxmellen opened this issue Jun 27, 2018 · 3 comments

Comments

@maxmellen
Copy link

Hi there 👋

This probably relates to #23

First off, thanks a lot for all the work to put this library together!
I've had a great time using it to document some Elixir APIs.

Recently I've been trying to invest some effort in building better schemas and let OpenApiSpex.Plug.Cast cast them into structs. However I ran into a few issues when trying to use polymorphic schemas.

For instance if I have the following schemas:

defmodule OpenApiSpexOneOfDemo.PetPlug.PetRequest do
  require OpenApiSpex

  alias OpenApiSpex.Discriminator
  alias OpenApiSpexOneOfDemo.Schemas.{Cat, Dog}

  OpenApiSpex.schema(%{
    title: "PetRequest",
    type: :object,
    oneOf: [Cat, Dog],
    discriminator: %Discriminator{
      propertyName: "pet_type"
    },
    example: %{"pet_type" => "Cat", "meow" => "meow"}
  })
end

defmodule OpenApiSpexOneOfDemo.Schemas do
  defmodule Cat do
    require OpenApiSpex

    alias OpenApiSpex.Schema

    OpenApiSpex.schema(%{
      title: "Cat",
      type: :object,
      properties: %{
        pet_type: %Schema{type: :string},
        meow: %Schema{type: :string}
      },
      required: [:pet_type, :meow]
    })
  end

  defmodule Dog do
    require OpenApiSpex

    alias OpenApiSpex.Schema

    OpenApiSpex.schema(%{
      title: "Dog",
      type: :object,
      properties: %{
        pet_type: %Schema{type: :string},
        bark: %Schema{type: :string}
      },
      required: [:pet_type, :bark]
    })
  end
end

I would expect posting the following JSON:

{
  "pet_type": "Cat",
  "meow": "meow"
}

To be cast into the following struct when using the OpenApiSpex.Plug.Cast plug:

%OpenApiSpexOneOfDemo.Schemas.Cat{meow: "meow", pet_type: "Cat"}

However, in my tests, it ends up being cast into:

%OpenApiSpexOneOfDemo.PetPlug.PetRequest{}

Having gone through the code a bit, it seems that casting into polymorphic schemas is simply not implemented... 😢

Is that the case or / and I doing something wrong?
If it hasn't been implemented yet, is there anything I can do to help?
Any pointers towards what would need to be done?
Do you have any idea how much effort you think it would take this implemented?
As a first step, only supporting polymorphic schemas that use discriminators might make this easier to implement. What do you think?

The full code for my test from which the above snippets are taken from can be found here: maxmellen/open_api_spex_one_of_demo

Running mix test will showcase the issue I am talking about here:

  1) test {"pet_type": "Cat", "meow": "meow"} is cast into %OpenApiSpexOneOfDemo.Schemas.Cat{} (OpenApiSpexOneOfDemo.PetPlugTest)
     test/open_api_spex_one_of_demo/pet_plug_test.exs:8
     Assertion with == failed
     code:  assert conn.resp_body() == "%OpenApiSpexOneOfDemo.Schemas.Cat{meow: \"meow\", pet_type: \"Cat\"}"
     left:  "%OpenApiSpexOneOfDemo.PetPlug.PetRequest{}"
     right: "%OpenApiSpexOneOfDemo.Schemas.Cat{meow: \"meow\", pet_type: \"Cat\"}"
     stacktrace:
       test/open_api_spex_one_of_demo/pet_plug_test.exs:19: (test)

I also have a version of the schemas using a Pet parent schema that can be found on the all-of branch:

defmodule OpenApiSpexOneOfDemo.Schemas do
  defmodule Pet do
    require OpenApiSpex

    alias OpenApiSpex.{Schema, Discriminator}

    OpenApiSpex.schema(%{
      title: "Pet",
      type: :object,
      properties: %{
        pet_type: %Schema{type: :string}
      },
      required: [:pet_type],
      discriminator: %Discriminator{
        propertyName: "pet_type"
      }
    })
  end

  defmodule Cat do
    require OpenApiSpex

    alias OpenApiSpex.Schema

    OpenApiSpex.schema(%{
      title: "Cat",
      type: :object,
      allOf: [
        Pet,
        %Schema{
          type: :object,
          properties: %{
            pet_type: %Schema{type: :string},
            meow: %Schema{type: :string}
          },
          required: [:meow]
        }
      ]
    })
  end

  defmodule Dog do
    require OpenApiSpex

    alias OpenApiSpex.Schema

    OpenApiSpex.schema(%{
      title: "Dog",
      type: :object,
      allOf: [
        Pet,
        %Schema{
          type: :object,
          properties: %{
            pet_type: %Schema{type: :string},
            bark: %Schema{type: :string}
          },
          required: [:bark]
        }
      ]
    })
  end
end

This schema results in the same behavior.


Thanks in advance for your help

Best ✨

@mbuhot
Copy link
Collaborator

mbuhot commented Jun 28, 2018

Hi @maxmellen 👋

I'm sketching out a potential solution in #28

There is some complexity in avoiding infinite recursion between parent and child schemas, but should be possible. It would be great if you could take a look.

Particularly if you can come up with a way to conveniently declare the struct fields for a schema that uses allOf with references to other schemas.

@maxmellen
Copy link
Author

Thanks a lot for the quick feedback!
I'd like to spend some time this week to help moving this forward 🚀
I'll make more comments in #28

@mbuhot
Copy link
Collaborator

mbuhot commented Aug 3, 2018

Closing this issue now that some support for casting polymorphic schemas is available.
We can create new issues as needed for further changes.

@mbuhot mbuhot closed this as completed Aug 3, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants