question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Add support for `oneOf` in openapi3 documents

See original GitHub issue

It fails while trying to access getAllOf which is null (tested with version 0.44.0)

        m.getAllOf.asScala.toList.get(1).flatMap {

OpenApi doc:

  "openapi": "3.0.0",
  "info": {
    "title": "---",
    "version": "0.1.0"
  },
  "components": {
    "schemas": {
      "TypeB": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string"
          }
        },
        "description": "TypeB",
        "required": [
          "type"
        ]
      },
      "TypeA": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string"
          }
        },
        "description": "TypeA",
        "required": [
          "type"
        ]
      },
      "Foo": {
        "type": "object",
        "description": "Foo",
        "oneOf": [
          {
            "$ref": "#/components/schemas/TypeA"
          },
          {
            "$ref": "#/components/schemas/TypeB"
          }
        ]
      }
    }
  },
  "paths": {
    "/foobar": {
      "get": {
        "operationId": "getFoo",
        "responses": {
          "200": {
            "description": "Response 200: FOO",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Foo"
                }
              }
            }
          }
        }
      }
    }
  }
}

However, it does not fail if the oneOf is surrounded by a allOf, like so:

      "Foo": {
        "type": "object",
        "description": "Foo",
        "allOf": [{
          "type": "object",
          "oneOf": [
            {
              "$ref": "#/components/schemas/TypeA"
            },
            {
              "$ref": "#/components/schemas/TypeB"
            }
          ]
        }]
      }

Issue Analytics

  • State:open
  • Created 5 years ago
  • Reactions:3
  • Comments:15 (5 by maintainers)

github_iconTop GitHub Comments

2reactions
blast-hardcheesecommented, Sep 26, 2022

Hi! Any progress on this issue? I can generate code for simple cases, but unfortunately I can’t generate code for production specs (we use oneOf a lot to describe tagged unions ) 😞

No progress on this so far, but if all the branches of your oneOf are defined as components you could do a workaround like:

x-scala-type: Either[Either[C, B], A]
oneOf:
  - $ref: .../A
  - $ref: .../B
  - $ref: .../C

which isn’t the best, but it could help get unblocked for the time being. You’ll need to be sure to order the types sensibly, as if one is able to be represented as a subset of another, you won’t be able to decode successfully without discriminator support.

A better solution would be to write something along the lines of:

sealed trait MyOneOf3[+A, +B, +C] {
  def fold[Z](fromA: A => Z, fromB: B => Z, fromC: C => Z): Z // implement this in the branches
}
final case class MyOneOf3A[A](value: A) extends MyOneOf3[A, Nothing, Nothing]
final case class MyOneOf3B[B](value: B) extends MyOneOf3[Nothing, B, Nothing]
final case class MyOneOf3C[C](value: C) extends MyOneOf3[Nothing, Nothing, C]

object MyOneOf3 {
  implicit def encoder[A, B, C](implicit A: Encoder[A], B: Encoder[B], C: Encoder[C]): Encoder[MyOneOf3[A, B, C]] =
    Encoder.instance[MyOneOf3[A, B, C]] {
      case MyOneOf3A(value) => A(value)
      // ... etc
    }
  implicit def decoder[A, B, C](implicit A: Decoder[A], B: Decoder[B], C: Decoder[C]): Decoder[MyOneOf3[A, B, C]] =
    A.map(MyOneOf3A.apply).or(B.map(MyOneOf3B.apply).or(C.map(MyOneOf3C.apply)))
}

… and stick that into a package somewhere, then use the imports configuration parameter in your sbt file like imports=List("com.example.foo.hacks._") which will add that import to all generated files. Then you can improve things like:

x-scala-type: MyOneOf3[A, B, C]
oneOf:
  - $ref: .../A
  - $ref: .../B
  - $ref: .../C

You’d still need to be aware of the ordering, but it would give you better ergonomics for use


To that end, what I have written above is very similar to how the response type mappers are managed, and generating this on-the-fly where we need oneOf support seems pretty reasonable.

One of the hard problems here (one of the reasons this is not currently implemented) is that object literals would need to be represented as nested case classes, with their own codecs and so on. Similarly, the oneOf itself would need to be defined as a child of wherever it is used.

A simplifying first step would be to just restrict oneOf to named components, both in which components it references and that it only occurs in top-level component definitions, which at least gives the required functionality for those that are able to accept this restriction.

1reaction
sledorzecommented, Mar 6, 2019

@blast-hardcheese thanks for the feedback, I thought it was supported.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Add support for oneOf in openapi3 documents #195 - GitHub
We would like to add support for oneOf in the best case via sealed types so the generated types can be used for...
Read more >
oneOf, anyOf, allOf, not - Swagger
oneOf – validates the value against exactly one of the subschemas ... Use the oneOf keyword to ensure the given data is valid...
Read more >
Support for oneOf, anyOf and allOf - Liferay Help Center
Support for oneOf, anyOf and allOf. OpenAPI 3.0 added several ways of using inheritance and composition to create complex schemas. Specifically, it added...
Read more >
Swashbuckle how to add OneOf declaration to OpenAPI 3
In C# .NET Core 5: In order to automatically resolve oneOf (polymorphism) at compile-time for your swagger.json , add the following line in ......
Read more >
Why You Need oneOf in Your OpenAPI Specifications - APIMatic
This blog is an insight on what oneOf means in an API definition and the ... To understand how oneOf can help your...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found