Skip to content

Discriminator Mapping Support for oneOf #1572

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
1 task done
mzronek opened this issue Mar 1, 2024 · 5 comments
Closed
1 task done

Discriminator Mapping Support for oneOf #1572

mzronek opened this issue Mar 1, 2024 · 5 comments
Labels
enhancement New feature or request openapi-ts Relevant to the openapi-typescript library PRs welcome PRs are welcome to solve this issue!

Comments

@mzronek
Copy link
Contributor

mzronek commented Mar 1, 2024

Description

Supporting the OpenAPI discriminator mapping property would greatly enhance the support for OpenAPI inheritance and polymorphism. The currently proposed solution in the openapi-typescript docs with the discriminator property as an enum with a single value per object rarely reflects the implementation of backends in practice. The type is usually a full enum composed of all possible values and the mapping takes care of assigning the enum value to a type.

The mapping was briefly brought up in issues about the discriminator, but I have not seen any plans for implementation, so I am trying to get things rolling with this feature request.

Here is an openapi example file for discriminator mapping:

openapi: 3.0.0
paths:
  /endpoint:
    get:
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema: 
                oneOf:
                  - $ref: '#/components/schemas/simpleObject'
                  - $ref: '#/components/schemas/complexObject'
                discriminator:
                  propertyName: type
                  mapping:
                    simple: '#/components/schemas/simpleObject'
                    complex: '#/components/schemas/complexObject'
components:
  schemas:
    simpleObject:
      type: object
      required:
        - type
      properties:
        type:
          $ref: '#/components/schemas/type'
        simple:
          type: boolean
    complexObject:
      type: object
      required:
        - type
      properties:
        type:
          $ref: '#/components/schemas/type'
        complex:
          type: boolean
    type:
      type: string
      enum:
        - simple
        - complex

The generated output with 7.0.0-next.7 (unnecessary properties removed):

export interface paths {
    "/endpoint": {
        get: {
            responses: {
                /** @description OK */
                200: {
                    content: {
                        "application/json": components["schemas"]["simpleObject"] | components["schemas"]["complexObject"];
                    };
                };
            };
        };
    };
}
export interface components {
    schemas: {
        simpleObject: {
            type: components["schemas"]["type"];
            simple?: boolean;
        };
        complexObject: {
            type: components["schemas"]["type"];
            complex?: boolean;
        };
        /** @enum {string} */
        type: "simple" | "complex";
    };
}

The problem is solved with discriminators:

export type EndpointResponse = paths['/endpoint']['get']['responses']['200']['content']['application/json'];

const test: EndpointResponse = {
    type: 'simple',
    // complex should not be possible on a response with type 'simple'
    complex: true
}

Proposal

I propose to implement the mapping property for version 7.

Checklist

@mzronek mzronek added enhancement New feature or request PRs welcome PRs are welcome to solve this issue! openapi-ts Relevant to the openapi-typescript library labels Mar 1, 2024
@mzronek mzronek changed the title Discriminator Mapping Support Discriminator Mapping Support for oneOf Mar 2, 2024
@mzronek
Copy link
Contributor Author

mzronek commented Mar 2, 2024

I had a good look at the code now and read the discriminator unit tests. This case of oneOf seems to be not supported. There is a oneOf case that is tested, but it doesn't seem to even use a discriminator, the OpenAPI input is invalid and the output is simply a oneOf union: https://github.com/drwpow/openapi-typescript/blob/ba1aca51cf6adf7983157ac904b6ab19b9a894d8/packages/openapi-typescript/test/discriminators.test.ts#L198

The default usecase for oneOf discriminators seems to be not handled right now, so I changed the title of this feature request accordingly.

I also have a proposal now and might be able to implement this. The hard part is searching for all discriminator properties referenced by a discriminator. This should be done via the mapping itself, where we already get the path of the schema object. If the object consists of an allOf (for example a base object for the discriminator, where the discriminator property really is), the allOf needs to be resolved. Finally we need to overwrite the discriminator property with the single mapped value to mimick the Typescript union discimination.

We probably need to enhance the discriminator scan and add the discriminator property paths to the ctx information. That way we should be able to quickly find the discriminator properties inside transformSchemaObjectCore and transform { type: 'string', enum: [value] } instead of the full enum.

That doesn't take the resolved allOfs into account. Maybe we can append the discriminator property to the mapped schema objects to overwrite any inherited discriminator properties?

@drwpow Shall I try to implement the easy case from the OpenAPI above? It is the default case for oneOf from the OpenAPI documentation for discriminators.

@mzronek
Copy link
Contributor Author

mzronek commented Mar 3, 2024

I PR'd a proposal so we can discuss the new support: #1574

@mzronek
Copy link
Contributor Author

mzronek commented Mar 5, 2024

#1574 was merged into main now. Working on backporting to 6.x now.

@mzronek
Copy link
Contributor Author

mzronek commented Apr 27, 2024

Sadly, I had little time to do the backport. It is also taking much longer due to the big differences in the code base. So I gave this more thought and I feel like we probably should not do a backport to v6. The new discriminator code is a major change for the output, so this really belongs in v7 to avoid any surprises.
@drwpow How do you feel about this?

@mzronek
Copy link
Contributor Author

mzronek commented Apr 27, 2024

I will close this Issue to clean up a little.

@mzronek mzronek closed this as completed Apr 27, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request openapi-ts Relevant to the openapi-typescript library PRs welcome PRs are welcome to solve this issue!
Projects
None yet
Development

No branches or pull requests

1 participant