OpenAPITools / openapi-generator

OpenAPI Generator allows generation of API client libraries (SDK generation), server stubs, documentation and configuration automatically given an OpenAPI Spec (v2, v3)
https://openapi-generator.tech
Apache License 2.0
20.73k stars 6.32k forks source link

[BUG] [typescript-rxjs] Collision for generated model and interface in API that have same name #18226

Open jdespatis opened 3 months ago

jdespatis commented 3 months ago

Bug Report Checklist

Description

I want to generate models and API interfaces from an OpenAPI 3.0.3 schema using the typescript-rxjs generator.

openapi-generator version

7.5.0-SNAPSHOT

OpenAPI declaration file content or url
{
  "servers": [
    {
      "url": "https://tst-example.fr"
    }
  ],
  "paths": {
    "/v1/order/retail_orders/parameters/{id}": {
      "post": {
        "tags": [
          "RetailOrders"
        ],
        "summary": "Update a retail order by id",
        "security": [
          {
            "BearerToken": []
          }
        ],
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "creation_date",
                    "creation_date_index",
                    "expiration_date",
                    "id",
                    "label",
                    "lines",
                    "order_type",
                    "requesting_location",
                    "reservation_duration",
                    "seller",
                    "sold_to",
                    "status",
                    "total_qty"
                  ],
                  "properties": {
                    "urgent": {
                      "type": "boolean"
                    },
                    "total_qty": {
                      "type": "integer"
                    },
                    "status": {
                      "type": "object",
                      "required": [
                        "last_action",
                        "last_modification",
                        "last_modifier",
                        "last_status_code",
                        "status_code"
                      ],
                      "properties": {
                        "status_description": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "required": [
                              "language",
                              "value"
                            ],
                            "properties": {
                              "value": {
                                "type": "string"
                              },
                              "language": {
                                "type": "string"
                              }
                            }
                          },
                          "example": [
                            {
                              "value": "Description du statut ( en anglais )",
                              "language": "en-GB"
                            }
                          ]
                        },
                        "status_code": {
                          "type": "string",
                          "enum": [
                            "Active",
                            "Canceled",
                            "Sold"
                          ]
                        },
                        "last_status_code": {
                          "type": "string",
                          "enum": [
                            "Active",
                            "Canceled",
                            "Sold"
                          ]
                        },
                        "last_modifier": {
                          "type": "object",
                          "required": [
                            "id",
                            "name"
                          ],
                          "properties": {
                            "name": {
                              "type": "object",
                              "required": [
                                "last"
                              ],
                              "properties": {
                                "last": {
                                  "type": "string"
                                },
                                "first": {
                                  "type": "string"
                                }
                              }
                            },
                            "id": {
                              "type": "string",
                              "minLength": 1
                            }
                          }
                        },
                        "last_modification": {
                          "type": "string",
                          "pattern": "^(?:[1-9]\\d{3}-(?:(?:0[1-9]|1[0-2])-(?:0[1-9]|1\\d|2[0-8])|(?:0[13-9]|1[0-2])-(?:29|30)|(?:0[13578]|1[02])-31)|(?:[1-9]\\d(?:0[48]|[2468][048]|[13579][26])|(?:[2468][048]|[13579][26])00)-02-29)T(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d{1,9})?(?:Z|[+-][01]\\d:[0-5]\\d)$"
                        },
                        "last_action": {
                          "type": "string"
                        }
                      }
                    },
                    "sold_to": {
                      "type": "object",
                      "required": [
                        "contact",
                        "customer_id",
                        "name",
                        "qualification"
                      ],
                      "properties": {
                        "tier": {
                          "type": "string"
                        },
                        "qualification": {
                          "type": "string"
                        },
                        "name": {
                          "type": "object",
                          "required": [
                            "last"
                          ],
                          "properties": {
                            "last": {
                              "type": "string"
                            },
                            "first": {
                              "type": "string"
                            }
                          }
                        },
                        "customer_id": {
                          "type": "string",
                          "minLength": 1
                        },
                        "contact": {
                          "type": "object",
                          "properties": {
                            "preferred_way_of_contact": {
                              "type": "string"
                            },
                            "preferred_contact": {
                              "type": "string",
                              "enum": [
                                "sss",
                                "seller",
                                "none"
                              ],
                              "default": "sss"
                            },
                            "phone2": {
                              "type": "string"
                            },
                            "phone1": {
                              "type": "string"
                            },
                            "email": {
                              "type": "string",
                              "pattern": "^[\\w-\\.]+@([\\w-]+\\.)+[\\w-]{2,4}$"
                            }
                          }
                        }
                      }
                    },
                    "seller": {
                      "type": "object",
                      "required": [
                        "id",
                        "name"
                      ],
                      "properties": {
                        "name": {
                          "type": "object",
                          "required": [
                            "last"
                          ],
                          "properties": {
                            "last": {
                              "type": "string"
                            },
                            "first": {
                              "type": "string"
                            }
                          }
                        },
                        "id": {
                          "type": "string",
                          "minLength": 1
                        }
                      }
                    },
                    "reservation_duration": {
                      "type": "integer"
                    },
                    "requesting_location": {
                      "type": "object",
                      "required": [
                        "id",
                        "name"
                      ],
                      "properties": {
                        "name": {
                          "type": "string"
                        },
                        "id": {
                          "type": "string"
                        }
                      }
                    },
                    "order_type": {
                      "type": "string",
                      "enum": [
                        "reservation",
                        "customer_request",
                        "kbc_wish",
                        "crown_sac_a_la_carte",
                        "moto_sales"
                      ]
                    },
                    "order_total_amount_incl_taxes": {
                      "type": "number"
                    },
                    "order_total_amount_excl_taxes": {
                      "type": "number"
                    },
                    "lines": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "required": [
                          "UoM",
                          "cites",
                          "color",
                          "color_description_id",
                          "finition",
                          "id_line",
                          "main_color_id",
                          "qty",
                          "size",
                          "status"
                        ],
                        "properties": {
                          "storage_location": {
                            "type": "string"
                          },
                          "status": {
                            "type": "string"
                          },
                          "sourcing_type": {
                            "type": "string",
                            "enum": [
                              "remote",
                              "main"
                            ]
                          },
                          "sourcing_location": {
                            "type": "string"
                          },
                          "sku_without_prefix": {
                            "type": "string"
                          },
                          "sku_with_prefix": {
                            "type": "string"
                          },
                          "size": {
                            "type": "string"
                          },
                          "qty": {
                            "type": "integer"
                          },
                          "product_name": {
                            "type": "object"
                          },
                          "product_label": {
                            "type": "object"
                          },
                          "product_description": {
                            "type": "object"
                          },
                          "main_color_id": {
                            "type": "string"
                          },
                          "id_line": {
                            "type": "string",
                            "pattern": "^(HRO_[0-9]{6}_HW_([A-Z]|[0-9]){5}_[0-9]{3})$",
                            "example": "HRO_240110_HW_A0A20_000"
                          },
                          "finition": {
                            "type": "string"
                          },
                          "department_id": {
                            "type": "object"
                          },
                          "color_description_id": {
                            "type": "string"
                          },
                          "color": {
                            "type": "object",
                            "required": [
                              "business_color",
                              "coloris",
                              "main_color_id"
                            ],
                            "properties": {
                              "main_color_id": {
                                "type": "string"
                              },
                              "coloris": {
                                "type": "string"
                              },
                              "business_color": {
                                "type": "string"
                              }
                            }
                          },
                          "cites": {
                            "type": "boolean"
                          },
                          "UoM": {
                            "type": "string",
                            "enum": [
                              "RLX",
                              "PAN",
                              "CM",
                              "PAI",
                              "PCE"
                            ]
                          }
                        }
                      }
                    },
                    "label": {
                      "type": "object",
                      "required": [
                        "id",
                        "value"
                      ],
                      "properties": {
                        "value": {
                          "type": "object",
                          "example": {
                            "en-GB": "Description du statut ( en anglais )"
                          }
                        },
                        "id": {
                          "type": "string"
                        }
                      }
                    },
                    "internal_reference": {
                      "type": "string"
                    },
                    "id": {
                      "type": "string"
                    },
                    "expiration_date": {
                      "type": "string",
                      "pattern": "^(?:[1-9]\\d{3}-(?:(?:0[1-9]|1[0-2])-(?:0[1-9]|1\\d|2[0-8])|(?:0[13-9]|1[0-2])-(?:29|30)|(?:0[13578]|1[02])-31)|(?:[1-9]\\d(?:0[48]|[2468][048]|[13579][26])|(?:[2468][048]|[13579][26])00)-02-29)T(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d{1,9})?(?:Z|[+-][01]\\d:[0-5]\\d)$",
                      "format": "TZ"
                    },
                    "currency": {
                      "type": "string"
                    },
                    "creation_date_index": {
                      "type": "integer"
                    },
                    "creation_date": {
                      "type": "string",
                      "pattern": "^(?:[1-9]\\d{3}-(?:(?:0[1-9]|1[0-2])-(?:0[1-9]|1\\d|2[0-8])|(?:0[13-9]|1[0-2])-(?:29|30)|(?:0[13578]|1[02])-31)|(?:[1-9]\\d(?:0[48]|[2468][048]|[13579][26])|(?:[2468][048]|[13579][26])00)-02-29)T(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d{1,9})?(?:Z|[+-][01]\\d:[0-5]\\d)$",
                      "format": "TZ"
                    },
                    "comment": {
                      "type": "string"
                    },
                    "business_id": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "5XX": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "error"
                  ],
                  "properties": {
                    "error": {
                      "type": "object",
                      "required": [
                        "uuid",
                        "value"
                      ],
                      "properties": {
                        "value": {
                          "type": "object",
                          "example": {
                            "en": "Error 5XX. An unexpected issue has occurred. Please try again later or contact support for assistance."
                          }
                        },
                        "uuid": {
                          "type": "string"
                        },
                        "details": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "required": [
                              "detail",
                              "error_type"
                            ],
                            "properties": {
                              "error_type": {
                                "type": "string"
                              },
                              "detail": {
                                "type": "string"
                              }
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "4XX": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "error"
                  ],
                  "properties": {
                    "error": {
                      "type": "object",
                      "required": [
                        "uuid",
                        "value"
                      ],
                      "properties": {
                        "value": {
                          "type": "object",
                          "example": {
                            "en": "Error 4XX. An unexpected issue has occurred. Please try again later or contact support for assistance."
                          }
                        },
                        "uuid": {
                          "type": "string"
                        },
                        "details": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "required": [
                              "detail",
                              "error_type"
                            ],
                            "properties": {
                              "error_type": {
                                "type": "string"
                              },
                              "detail": {
                                "type": "string"
                              }
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "updated_by"
                ],
                "properties": {
                  "updated_by": {
                    "type": "object",
                    "required": [
                      "store"
                    ],
                    "properties": {
                      "store": {
                        "type": "object",
                        "required": [
                          "id"
                        ],
                        "properties": {
                          "label": {
                            "type": "string"
                          },
                          "id": {
                            "type": "string"
                          }
                        }
                      },
                      "seller": {
                        "type": "object",
                        "required": [
                          "id",
                          "name"
                        ],
                        "properties": {
                          "name": {
                            "type": "object",
                            "required": [
                              "last"
                            ],
                            "properties": {
                              "last": {
                                "type": "string"
                              },
                              "first": {
                                "type": "string"
                              }
                            }
                          },
                          "id": {
                            "type": "string",
                            "minLength": 1
                          }
                        }
                      }
                    }
                  },
                  "sold_to": {
                    "type": "object",
                    "required": [
                      "contact",
                      "customer_id",
                      "name",
                      "qualification"
                    ],
                    "properties": {
                      "tier": {
                        "type": "string"
                      },
                      "qualification": {
                        "type": "string"
                      },
                      "name": {
                        "type": "object",
                        "required": [
                          "last"
                        ],
                        "properties": {
                          "last": {
                            "type": "string"
                          },
                          "first": {
                            "type": "string"
                          }
                        }
                      },
                      "customer_id": {
                        "type": "string",
                        "minLength": 1
                      },
                      "contact": {
                        "type": "object",
                        "properties": {
                          "preferred_way_of_contact": {
                            "type": "string"
                          },
                          "preferred_contact": {
                            "type": "string",
                            "enum": [
                              "sss",
                              "seller",
                              "none"
                            ],
                            "default": "sss"
                          },
                          "phone2": {
                            "type": "string"
                          },
                          "phone1": {
                            "type": "string"
                          },
                          "email": {
                            "type": "string",
                            "pattern": "^[\\w-\\.]+@([\\w-]+\\.)+[\\w-]{2,4}$"
                          }
                        }
                      }
                    }
                  },
                  "expiration_date": {
                    "type": "string",
                    "pattern": "^(?:[1-9]\\d{3}-(?:(?:0[1-9]|1[0-2])-(?:0[1-9]|1\\d|2[0-8])|(?:0[13-9]|1[0-2])-(?:29|30)|(?:0[13578]|1[02])-31)|(?:[1-9]\\d(?:0[48]|[2468][048]|[13579][26])|(?:[2468][048]|[13579][26])00)-02-29)T(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d{1,9})?(?:Z|[+-][01]\\d:[0-5]\\d)$",
                    "format": "TZ"
                  },
                  "comment": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "parameters": [
          {
            "schema": {
              "type": "string",
              "default": "EN"
            },
            "required": false,
            "name": "lang",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "pattern": "^(HRO_[0-9]{6}_HW_([A-Z]|[0-9]){5})$"
            },
            "required": true,
            "name": "id",
            "in": "path",
            "example": "HRO_240110_HW_A0A20"
          }
        ],
        "description": "/v1/order/retail_orders/parameters/{id}"
      }
    }
  },
  "openapi": "3.0.3",
  "info": {
    "version": "0.7.9",
    "title": "API documentation gateway",
    "description": "Gateway documentation"
  },
  "components": {
    "securitySchemes": {
      "BearerToken": {
        "type": "http",
        "scheme": "bearer"
      },
      "BasicAuth": {
        "type": "http",
        "scheme": "basic"
      }
    }
  }
}
Steps to reproduce

You can reproduce those steps to reproduce: (./generated/openapi.json contains the json provided above)

docker run --rm \
 -v $PWD/generated:/generated openapitools/openapi-generator-cli generate \
 -i ./generated/openapi.json \
 -g typescript-rxjs \
 -o /generated/Result

It generates (among other files) :

And the problem is that apis/RetailOrdersApi.ts generated contains this piece of typescript code, which is invalid:

import type {
    V1OrderRetailOrdersParametersIdPost200Response,
    V1OrderRetailOrdersParametersIdPost4XXResponse,
    V1OrderRetailOrdersParametersIdPost5XXResponse,
    V1OrderRetailOrdersParametersIdPostRequest,
} from '../models';

export interface V1OrderRetailOrdersParametersIdPostRequest {
    id: string;
    v1OrderRetailOrdersParametersIdPostRequest: V1OrderRetailOrdersParametersIdPostRequest;
    lang?: string;
}

The code is invalid because the interface uses the name V1OrderRetailOrdersParametersIdPostRequest which is also the same name of a model imported just above. => I get a compiler typescript error

TS2440: Import declaration conflicts with local declaration of 'V1OrderRetailOrdersParametersIdPostRequest'.

Related issues/PRs

None

Suggest a fix

I don't get this problem when I'm using typescript-fetch generator, because:

I don't have any suggestion, but I can work on a PR if you help me understand where should I apply the fix.

jdespatis commented 3 months ago

To complete, I've found a workaround for now, but it's definitely not the good strategy...

I export all templates of the typescript-rxjs generator with the command:

docker run --rm \
    -v $PWD/generated:/generated openapitools/openapi-generator-cli author template \
    -g typescript-rxjs \
    --output ./generated/templates-rxjs

I remove (from ./generated/templates-rxjs) all templates except the apis.mustache template

And then apply this patch to it: (basically replace Request by OperationRequest to avoid any conflict)

--- generated/out-rxjs/apis.mustache    2024-03-26 14:25:29.000000000 +0100
+++ generated/out/apis.mustache 2024-03-26 11:58:34.000000000 +0100
@@ -15,7 +15,7 @@
 {{#operations}}
 {{#operation}}
 {{#allParams.0}}
-export interface {{operationIdCamelCase}}Request {
+export interface {{operationIdCamelCase}}OperationRequest {
     {{#allParams}}
     {{paramName}}{{^required}}?{{/required}}: {{#isEnum}}{{{datatypeWithEnum}}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{#isNullable}} | null{{/isNullable}}{{/isEnum}};
     {{/allParams}}
@@ -39,12 +39,12 @@
      * {{&summary}}
      {{/summary}}
      */
-    {{nickname}}({{#allParams.0}}{ {{#allParams}}{{paramName}}{{#vendorExtensions.x-param-name-alternative}}: {{vendorExtensions.x-param-name-alternative}}{{/vendorExtensions.x-param-name-alternative}}{{^-last}}, {{/-last}}{{/allParams}} }: {{operationIdCamelCase}}Request{{/allParams.0}}): Observable<{{{returnType}}}{{^returnType}}void{{/returnType}}>
+    {{nickname}}({{#allParams.0}}{ {{#allParams}}{{paramName}}{{#vendorExtensions.x-param-name-alternative}}: {{vendorExtensions.x-param-name-alternative}}{{/vendorExtensions.x-param-name-alternative}}{{^-last}}, {{/-last}}{{/allParams}} }: {{operationIdCamelCase}}OperationRequest{{/allParams.0}}): Observable<{{{returnType}}}{{^returnType}}void{{/returnType}}>
 {{#withProgressSubscriber}}
-    {{nickname}}({{#allParams.0}}{ {{#allParams}}{{paramName}}{{#vendorExtensions.x-param-name-alternative}}: {{vendorExtensions.x-param-name-alternative}}{{/vendorExtensions.x-param-name-alternative}}{{^-last}}, {{/-last}}{{/allParams}} }: {{operationIdCamelCase}}Request, {{/allParams.0}}opts?: Pick<OperationOpts, 'progressSubscriber'>): Observable<{{{returnType}}}{{^returnType}}void{{/returnType}}>
+    {{nickname}}({{#allParams.0}}{ {{#allParams}}{{paramName}}{{#vendorExtensions.x-param-name-alternative}}: {{vendorExtensions.x-param-name-alternative}}{{/vendorExtensions.x-param-name-alternative}}{{^-last}}, {{/-last}}{{/allParams}} }: {{operationIdCamelCase}}OperationRequest, {{/allParams.0}}opts?: Pick<OperationOpts, 'progressSubscriber'>): Observable<{{{returnType}}}{{^returnType}}void{{/returnType}}>
 {{/withProgressSubscriber}}
-    {{nickname}}({{#allParams.0}}{ {{#allParams}}{{paramName}}{{#vendorExtensions.x-param-name-alternative}}: {{vendorExtensions.x-param-name-alternative}}{{/vendorExtensions.x-param-name-alternative}}{{^-last}}, {{/-last}}{{/allParams}} }: {{operationIdCamelCase}}Request, {{/allParams.0}}opts?: OperationOpts): Observable<{{#returnType}}AjaxResponse<{{{.}}}>{{/returnType}}{{^returnType}}void | AjaxResponse<void>{{/returnType}}>
-    {{nickname}}({{#allParams.0}}{ {{#allParams}}{{paramName}}{{#vendorExtensions.x-param-name-alternative}}: {{vendorExtensions.x-param-name-alternative}}{{/vendorExtensions.x-param-name-alternative}}{{^-last}}, {{/-last}}{{/allParams}} }: {{operationIdCamelCase}}Request, {{/allParams.0}}opts?: OperationOpts): Observable<{{#returnType}}{{{returnType}}} | AjaxResponse<{{{returnType}}}>{{/returnType}}{{^returnType}}void | AjaxResponse<void>{{/returnType}}> {
+    {{nickname}}({{#allParams.0}}{ {{#allParams}}{{paramName}}{{#vendorExtensions.x-param-name-alternative}}: {{vendorExtensions.x-param-name-alternative}}{{/vendorExtensions.x-param-name-alternative}}{{^-last}}, {{/-last}}{{/allParams}} }: {{operationIdCamelCase}}OperationRequest, {{/allParams.0}}opts?: OperationOpts): Observable<{{#returnType}}AjaxResponse<{{{.}}}>{{/returnType}}{{^returnType}}void | AjaxResponse<void>{{/returnType}}>
+    {{nickname}}({{#allParams.0}}{ {{#allParams}}{{paramName}}{{#vendorExtensions.x-param-name-alternative}}: {{vendorExtensions.x-param-name-alternative}}{{/vendorExtensions.x-param-name-alternative}}{{^-last}}, {{/-last}}{{/allParams}} }: {{operationIdCamelCase}}OperationRequest, {{/allParams.0}}opts?: OperationOpts): Observable<{{#returnType}}{{{returnType}}} | AjaxResponse<{{{returnType}}}>{{/returnType}}{{^returnType}}void | AjaxResponse<void>{{/returnType}}> {
         {{#hasParams}}
         {{#allParams}}
         {{#required}}

I can then use the generator typescript-rxjs safely without any compiler problem, with the command:

docker run --rm \
    -v $PWD/generated:/generated openapitools/openapi-generator-cli generate \
    -t ./generated/templates-rxjs \
    -i ./generated/openapi.json \
    -g typescript-rxjs \
    -o /generated/Result

As said before, it's not the good strategy, as this template is roughtly the same as the one of the generator typescript-fetch (nearly same structure of files generated), and it perfectly works with the latter

=> it seems that the {{operationIdCamelCase}} in the typescript-rxjs template doesn't return something as strong as the one provided by typescript-fetch

Any idea of a better fix ?

Thanks for your feedback!

iliastsa commented 3 months ago

This seems related to issue #17909 which created some time ago