openapi-typescript-codegen icon indicating copy to clipboard operation
openapi-typescript-codegen copied to clipboard

oneof enum generates incorrect code

Open v-morlock opened this issue 11 months ago • 11 comments

Hi!

this model

      "VendingItemState": {
        "oneOf": [
          {
            "type": "object",
            "required": [
              "type"
            ],
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "Pending"
                ]
              }
            }
          },
          {
            "type": "object",
            "required": [
              "type"
            ],
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "Picked"
                ]
              }
            }
          },
          {
            "type": "object",
            "required": [
              "type"
            ],
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "Placed"
                ]
              }
            }
          },
          {
            "type": "object",
            "required": [
              "content",
              "type"
            ],
            "properties": {
              "content": {
                "type": "string",
                "format": "date-time"
              },
              "type": {
                "type": "string",
                "enum": [
                  "Dispensed"
                ]
              }
            }
          },
          {
            "type": "object",
            "required": [
              "content",
              "type"
            ],
            "properties": {
              "content": {
                "type": "array",
                "items": [
                  {
                    "type": "string",
                    "format": "date-time"
                  },
                  {
                    "type": "string"
                  }
                ],
                "maxItems": 2,
                "minItems": 2
              },
              "type": {
                "type": "string",
                "enum": [
                  "Error"
                ]
              }
            }
          }
        ]
      },

generates the following code:

export type VendingItemState = ({
    type: VendingItemState.type;
} | {
    content: string;
    type: VendingItemState.type;
} | {
    content: Array<any>;
    type: VendingItemState.type;
});
export namespace VendingItemState {
    export enum type {
        PENDING = 'Pending',
    }
}


which is missing most of the options for "type"...

v-morlock avatar Mar 12 '24 16:03 v-morlock

@v-morlock hey, are you able to replicate the same issue in @nicolas-chaulet/openapi-typescript-codegen?

mrlubos avatar Mar 12 '24 17:03 mrlubos

Hi! Unfortunately yes

v-morlock avatar Mar 12 '24 17:03 v-morlock

@v-morlock uh, I see what's happening. What's the output you'd expect? Roll all those options into a single enum, or multiple enums per each option?

mrlubos avatar Mar 12 '24 19:03 mrlubos

Both would be fine to me - the value is representing a rust enum that looks like this:

#[derive(Serialize, Deserialize, Clone, schemars::JsonSchema, Eq, PartialEq, Debug)]
#[serde(tag = "type", content = "content")]
pub enum VendingItemState {
    Pending,
    Picked,
    Placed,
    Dispensed(chrono::DateTime<Utc>),
    Error(chrono::DateTime<Utc>, String),
}

so i think the direct mapping would be sth like below - one could make an argument that a separate enum isn't really necessary for enums with only a single value. But in practice, anything that gives the same end-result as the structure below would be fine to me. I don't think I'd ever really use the generated enum anyway. I think a single enum wouldn't work though because then it wouldn't be clear which options contain additional values and which don't.

export type VendingItemState = ({
    type: "Pending";
} | {
    type: "Picked";
} | {
    type: "Placed";
} | {
    type: "Dispensed;
    content: string;
} | {
    type: "Error";
    content: Array<any>;
});

v-morlock avatar Mar 12 '24 20:03 v-morlock

Btw, do you have an idea why the "content" array is typed as "any"?

v-morlock avatar Mar 12 '24 20:03 v-morlock

@v-morlock Got it, thanks! This is an interesting case, will have a look at it, but it will be available in the package I shared above as this one isn't really maintained

mrlubos avatar Mar 12 '24 20:03 mrlubos

@v-morlock please try with the latest version of @nicolas-chaulet/openapi-typescript-codegen 🎉

That should give you exactly what you need

export type ModelWithOneOfEnum = {
    foo: 'Bar';
} | {
    foo: 'Baz';
} | {
    foo: 'Qux';
} | {
    content: string;
    foo: 'Quux';
} | {
    content: [string | string];
    foo: 'Corge';
};

mrlubos avatar Mar 13 '24 07:03 mrlubos

@v-morlock I updated @nicolas-chaulet/openapi-typescript-codegen so it should now work out of the box. It is a huge change set, so I'd love if you could upgrade and let me know if anything breaks!

mrlubos avatar Mar 15 '24 05:03 mrlubos

@v-morlock how did you generate your OpenAPI specification?

mrlubos avatar Apr 11 '24 04:04 mrlubos

Hi! Thanks for developing that fix and sorry that I haven't found the time to test it yet. The spec is generated by the library "aide" for rust / axum

v-morlock avatar Apr 11 '24 05:04 v-morlock

@v-morlock for the minItems/maxItems array, would you be okay with output [Date | string, Date | string]?

mrlubos avatar Apr 11 '24 05:04 mrlubos