[core] Resolve oneOf discriminator property type from child schemas

Closed
IG
4mo ago
+569-4

What

getDiscriminatorPropertyType resolved the discriminator property's type only from the oneOf/anyOf schema's own properties, defaulting to "string" otherwise. For a pure oneOf/anyOf interface whose discriminator property lives on a shared base the children inherit via allOf, this produced a String getter that clashed with the enum the subtypes actually expose.

This adds a fallback (in DiscriminatorUtils) that resolves the type from the mapped child schemas, chasing the property through each child's allOf members. It only fires when the previous code would have returned "string", so existing specs are unaffected — regenerating the full sample suite produces no diffs.

Scenarios covered

Discriminator property declared…Resolves toTest
As a $ref on the schema's own properties (unchanged path)the referenced type (e.g. PetType)DiscriminatorUtilsTest, DefaultCodegenTest
Only on a shared base the oneOf children inherit via allOfPetType (from children)DiscriminatorUtilsTest, DefaultCodegenTest
Only on a shared base the anyOf children inherit via allOfPetType (from children)DiscriminatorUtilsTest
On a grandparent reached through multi-level allOfPetType (recursive descent)DiscriminatorUtilsTest
As a plain inline string (no $ref, no enum)"string" (fallback, unchanged)DiscriminatorUtilsTest, DefaultCodegenTest

Simples example where the discriminator is in the base object that is separate from the schema that contains oneOf

PetRequest:
      type: object
      oneOf:
        - $ref: '#/components/schemas/CatRequest'
        - $ref: '#/components/schemas/DogRequest'
      discriminator:
        propertyName: petType
        mapping:
          CAT: '#/components/schemas/CatRequest'
          DOG: '#/components/schemas/DogRequest'

    # Shared base WITHOUT a discriminator, so children flatten it via allOf
    # instead of extending it (which would create a cyclical reference).
    PetBase:
      type: object
      required:
        - petType
        - name
      properties:
        petType:
          $ref: '#/components/schemas/PetType'  # ---> This would be duplicated into PetRequest without this PR refactor
        name:
          type: string

    CatRequest:
      type: object
      allOf:
        - $ref: '#/components/schemas/PetBase'
        - type: object
          required:
            - indoor
          properties:
            indoor:
              type: boolean

    DogRequest:
      type: object
      allOf:
        - $ref: '#/components/schemas/PetBase'
        - type: object
          required:
            - trained
          properties:
            trained:
              type: boolean

PR checklist

  • Read the contribution guidelines.
  • 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
    
    (For Windows users, please run the script in WSL)
    Commit all changed files.
    This is important, as CI jobs will verify all generator outputs of your HEAD commit as it would merge with master.
    These must match the expectations made by your contribution.
    You may regenerate an individual generator by passing the relevant config(s) as an argument to the script, for example ./bin/generate-samples.sh bin/configs/java*.
    IMPORTANT: Do NOT purge/delete any folders/files (e.g. tests) when regenerating the samples as manually written tests may be removed.
  • 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

Fixes discriminator type resolution for oneOf/anyOf interfaces by deriving the property type from child schemas (via allOf) with cycle-safe recursion. Getter types now use the correct enum (e.g., PetType) instead of "string", avoiding interface vs subtype mismatches.

  • Bug Fixes
    • Updated DiscriminatorUtils.getDiscriminatorPropertyType(OpenAPI, ...) to first check the schema’s own $ref, then fall back to oneOf/anyOf children via getDiscriminatorPropertyTypeFromChildren and recursive getDiscriminatorSchemaDeep (supports multi-level allOf with a visited guard for cycles).
    • Updated DefaultCodegen.getDiscriminatorPropertyType to pass openAPI, map to model names, and keep the "string" fallback.
    • Added DiscriminatorUtilsTest, expanded DefaultCodegenTest, and new YAMLs (oneOf/anyOf, nested base, cyclic, string fallback). Regenerating samples shows no diffs.