OpenAPITools / OpenAPITools/openapi-generator

[BUG] typescript-fetch oneOf doesn't handle discrimination by property.

Open
#6,513 5 comments 11 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Client: TypeScript Issue: Bug
Dominant language
Java
Stars
26.8k
Forks
7.7k
PR merge metrics
PR metrics pending

Description

Description

OpenApi generator does not generate valid code for unions discriminated by property name. Unions (oneOf) work correctly when discriminated by a property value, however the generated code seems to try and combine the property lists from all members of the union. If each member of the union have disjoint sets of required properties, then the union is discriminated, and should be generated as such.

openapi-generator version

openapi-generator 4.3.1

OpenAPI declaration file content or url
{
    "openapi": "3.0",
    "info": {
        "title": "Test",
        "version": "0.1"
    },
    "servers": [{
        "url": "https://localhost"
    }],
    "paths": {
        "/": {
            "get": {
                "operationId": "dummyOperation",
                "responses": {
                    "200": {
                        "description": "Successful operaion",
                        "content": {
                            "application/json": {
                                "schema": {"$ref": "#/components/schemas/aOrB"}
                            }
                        }
                    }
                }
            }
        }
    },
    "components": {
        "schemas": {
            "a": {
                "type": "object",
                "required": ["a", "b"],
                "properties": {
                    "a": {
                        "type": "string"
                    },
                    "b": {
                        "type": "string"
                    }
                }
            },
            "b": {
                "type": "object",
                "required": ["b", "c"],
                "properties": {
                    "b": {
                        "type": "string"
                    },
                    "c": {
                        "type": "string"
                    }
                }
            },
            "aOrB": {
                "oneOf": [
                    {"$ref": "#/components/schemas/a"},
                    {"$ref": "#/components/schemas/b"}
                ]
            }
        }
    }
}
Command line used for generation

openapi-generator generate -i test.json -g typescript-fetch --additional-properties=typescriptThreePlus --generate-alias-as-model -o test

Steps to reproduce

After generation, the model file for aOrB is:

export function AOrBFromJSONTyped(json: any, ignoreDiscriminator: boolean): AOrB {
    if ((json === undefined) || (json === null)) {
        return json;
    }
    return { ...AFromJSONTyped(json, true), ...BFromJSONTyped(json, true) };
}

export function AOrBToJSON(value?: AOrB | null): any {
    if (value === undefined) {
        return undefined;
    }
    if (value === null) {
        return null;
    }
    return { ...AToJSON(value), ...BToJSON(value) };
}

This fails because A and B both contain 'required' parameters, so they cannot be combined by spreading as above.

Related issues/PRs

There are similar PR, but they don't seem to address exactly the same issue:
#6376 #5202 #4626 #1563 #927

Suggest a fix

The generated functions need to discriminate on properties as well as values, something like:

export function AOrBFromJSONTyped(json: any, ignoreDiscriminator: boolean): AOrB {
    if ((json === undefined) || (json === null)) {
        return json;
    }
    if (json['a'] != undefined && json['b'] != undefined) {
        return AFromJSONTyped(json, true);
    } else if (json['b'] != undefined && json['c'] != undefined) {
        return BFromJSONTyped(json, true);
    }
}

export function AOrBToJSON(value?: AOrB | null): any {
    if (value === undefined) {
        return undefined;
    }
    if (value === null) {
        return null;
    }
    if (value.a != undefined && value.b != undefined) {
        return AToJSON(value);
    } else if (value.b != undefined && value.c != undefined) {
        return BToJSON(value);
    }
}

In the 'if' statements it would test all the required parameters for that member object in the union.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Reproduce the issue with the supplied OpenAPI declaration and the typescript-fetch command, then trace how the generated aOrB model handles oneOf members. Compare property-based discrimination with the existing value-based behavior. Done means generated TypeScript checks each member's required properties during serialization and deserialization without merging incompatible models, with regression coverage for the example.

Written by the indexing model from the issue text.

Assessment

Tech stack
openapi, typescript
Domain
api, tooling
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.