Skip to content

[feat][scala-sttp] Add oneOf and allOf discriminator support with sealed traits for circe generator#23510

Open
nikhilsu wants to merge 6 commits intoOpenAPITools:masterfrom
nikhilsu:scala-sttp-circe/add-discriminator-support
Open

[feat][scala-sttp] Add oneOf and allOf discriminator support with sealed traits for circe generator#23510
nikhilsu wants to merge 6 commits intoOpenAPITools:masterfrom
nikhilsu:scala-sttp-circe/add-discriminator-support

Conversation

@nikhilsu
Copy link
Copy Markdown
Contributor

@nikhilsu nikhilsu commented Apr 10, 2026

@clasnake (2017/07), @shijinkui (2018/01), @ramzimaalej (2018/03), @chameleon82 (2020/03), @Bouillie (2020/04) @Fish86 (2023/06)

Adds discriminator-based polymorphism support to the scala-sttp generator when using circe as the JSON library. This enables correct deserialization of polymorphic types.

Implementation

Three discriminator patterns are supported:

  1. allOf + discriminator (no oneOf on parent) - e.g., AnimalCat/Dog with mapped values
  2. oneOf + discriminator (no explicit mapping) - dispatches using schema names
  3. oneOf + discriminator + mapping - dispatches using explicit mapped values

Falls back to regular (non-sealed) traits for edge cases like nested oneOf or shared members.

PR checklist

  • Read the contribution guidelines.
  • Pull Request title clearly describes the work in the pull request and Pull Request description provides details about how to validate the work. Missing information here may result in delayed response from the community.
  • Run the following to build the project
    and update samples:
  ./mvnw clean package || exit                                                                                                                                                                                 
  ./bin/generate-samples.sh ./bin/configs/*.yaml || exit                                                                                                                                                     
  ./bin/utils/export_docs_generators.sh || exit

Commit all changed files.

  • File the PR against the correct branch: master
  • If your PR solves a reported issue, reference it using GitHub's linking syntax
  • If your PR is targeting a particular programming language, @mention the technical committee
    members, so they are more likely to review the pull request.

Summary by cubic

Adds discriminator-based polymorphism to the scala-sttp generator with circe, supporting both oneOf and allOf. Generates sealed traits and discriminator-aware codecs for correct deserialization; updates docs, tests, and samples.

  • New Features

    • Advertises support for oneOf/allOf; synthesizes oneOf from allOf discriminator mappings for uniform handling.
    • Generates a sealed trait when all children can be inlined; otherwise a regular trait. Inlining only when a child is referenced once and not used as a field type or inside containers.
    • Circe codecs write discriminator fields on encode and dispatch on decode (using explicit mappings or schema names). Falls back to a union decoder when no discriminator.
    • Docs mark oneOf/allOf as supported; config now points to scala-sttp-circe/petstore.yaml. Samples show Animal (regular trait due to Seq[Dog]), Collar (mapped), and Treat (schema-name dispatch). Tests cover baseName field mapping, allOf/oneOf discriminator flows, and no-inlining when children are reused.
  • Bug Fixes

    • Unknown discriminator values now return clear DecodingFailure errors; added missing imports.
    • Prevented incorrect inlining when a child model is referenced in containers or as a property.

Written for commit 821db67. Summary will update on new commits.

@nikhilsu
Copy link
Copy Markdown
Contributor Author

cc: @wing328

Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

3 issues found across 21 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="modules/openapi-generator/src/main/resources/scala-sttp/model.mustache">

<violation number="1" location="modules/openapi-generator/src/main/resources/scala-sttp/model.mustache:67">
P2: Regular-trait encoders are non-exhaustive; unknown implementations will throw MatchError during encoding.</violation>
</file>

<file name="samples/client/petstore/scala-sttp-circe/src/main/scala/org/openapitools/client/model/Animal.scala">

<violation number="1" location="samples/client/petstore/scala-sttp-circe/src/main/scala/org/openapitools/client/model/Animal.scala:45">
P2: Subtype codecs use unconstrained `className` strings while the polymorphic codec expects canonical discriminator constants, causing inconsistent serialization and potential wrong subtype decoding.</violation>
</file>

<file name="modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaSttpClientCodegen.java">

<violation number="1" location="modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaSttpClientCodegen.java:363">
P2: Inlining decision only counts oneOf parents; a child referenced elsewhere can still be marked inlineable and removed, leading to missing model classes for non-oneOf references.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 1 file (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaSttpClientCodegen.java">

<violation number="1" location="modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaSttpClientCodegen.java:316">
P2: Model reference counting uses exact `dataType` matching, missing container-wrapped model references and causing incorrect inlining decisions.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 issues found across 6 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="modules/openapi-generator/src/test/java/org/openapitools/codegen/scala/ScalaSttpCirceCodegenTest.java">

<violation number="1" location="modules/openapi-generator/src/test/java/org/openapitools/codegen/scala/ScalaSttpCirceCodegenTest.java:158">
P2: Test assertion does not verify non-sealed trait and will pass for `sealed trait Animal` too.</violation>
</file>

<file name="modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaSttpClientCodegen.java">

<violation number="1" location="modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaSttpClientCodegen.java:367">
P1: Shared oneOf child models can be rebound to the last processed parent because parent vendor extensions are overwritten unconditionally.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

if (child == null) continue;

// All children extend the parent trait
child.getVendorExtensions().put("x-oneOfParent", parent.classname);
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: Shared oneOf child models can be rebound to the last processed parent because parent vendor extensions are overwritten unconditionally.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaSttpClientCodegen.java, line 367:

<comment>Shared oneOf child models can be rebound to the last processed parent because parent vendor extensions are overwritten unconditionally.</comment>

<file context>
@@ -345,16 +352,26 @@ private void markOneOfTraits(Map<String, ModelsMap> modelsMap,
+            if (child == null) continue;
+
+            // All children extend the parent trait
+            child.getVendorExtensions().put("x-oneOfParent", parent.classname);
+            if (parent.discriminator != null) {
+                child.getVendorExtensions().put("x-parentDiscriminatorName",
</file context>
Fix with Cubic

Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 5 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="samples/client/petstore/scala-sttp-circe/src/main/scala/org/openapitools/client/model/Animal.scala">

<violation number="1" location="samples/client/petstore/scala-sttp-circe/src/main/scala/org/openapitools/client/model/Animal.scala:18">
P2: `Animal` was made non-sealed, but `encoderAnimal` still matches only `Cat`/`Dog`, leaving a non-exhaustive runtime match for `Animal` values.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

@GerretS
Copy link
Copy Markdown

GerretS commented Apr 10, 2026

Note that both this PR and #22916 are for solving this issue: #19891

I didn't look into the code myself but mentioning it here to prevent duplicate work.

@nikhilsu
Copy link
Copy Markdown
Contributor Author

#22916 adds oneOf support for scala-sttp4-circe generator. This one is for sttp3

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

Successfully merging this pull request may close these issues.

2 participants