AnotiaWang / 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
0 stars 0 forks source link

Sweep: TypeScript + fetch generated code with `oneOf` does not properly check the dto type #1

Open AnotiaWang opened 1 month ago

AnotiaWang commented 1 month ago

I'm using the TypeScript + fetch template. When I am using oneOf to define models, the generated code does not check the enum property to judge which model instance it is.

For example, for the schema file below:

{
  "openapi": "3.0.0",
  "paths": {
    "/endpoint": {
      "get": {
        "operationId": "endpoint",
        "summary": "endpoint",
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Dto"
                }
              }
            }
          }
        },
        "security": [
          {
            "access-token": []
          }
        ]
      }
    }
  },
  "info": {
    "title": "API",
    "description": "API",
    "version": "1.0",
    "contact": {},
    "license": {
      "name": "UNLICENSED",
      "url": "UNLICENSED"
    }
  },
  "tags": [],
  "servers": [
  ],
  "components": {
    "securitySchemes": {
      "access-token": {
        "scheme": "bearer",
        "bearerFormat": "JWT",
        "type": "http",
        "description": "JWT Authorization header using the Bearer scheme."
      }
    },
    "schemas": {
      "Payload3": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "AGE_VERIFICATION"
            ]
          },
          "barcode": {
            "type": "string"
          },
          "requiredAge": {
            "type": "number"
          }
        },
        "required": [
          "type",
          "barcode"
        ]
      },
      "Payload2": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "ASSISTANCE_REQUESTED"
            ]
          }
        },
        "required": [
          "type"
        ]
      },
      "Payload1": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "PARTIAL_RESCAN"
            ]
          },
          "barcode": {
            "type": "string"
          }
        },
        "required": [
          "type",
          "barcode"
        ]
      },
      "Dto": {
        "type": "object",
        "properties": {
          "payload": {
            "description": "Payload for the assistance request",
            "oneOf": [
              {
                "$ref": "#/components/schemas/Payload1"
              },
              {
                "$ref": "#/components/schemas/Payload2"
              },
              {
                "$ref": "#/components/schemas/Payload3"
              }
            ]
          }
        },
        "required": [
          "payload"
        ]
      }
    }
  }
}

Exmaple of generated code:

// models/Payload1.ts
export function instanceOfPayload1(value: object): boolean {
    if (!('type' in value)) return false; // Does not check `type !== 'payload1'`
    if (!('barcode' in value)) return false;
    return true;
}

So in this case, Payload2 and Payload3 is never matched, which causes bugs in production.

Expected behavior

The instanceOfXXX() function should check if property type is exactly the enum value.

// models/Payload1.ts
export function instanceOfPayload1(value: object): boolean {
    if (!('type' in value) || value['type'] !== Object.values(Payload1TypeEnum)[0]) return false;
    if (!('barcode' in value)) return false;
    return true;
}

Additional Information

An example of fixing this is:

modules/openapi-generator/src/main/resources/typescript-fetch/modelGeneric.mustache:

-     if (!('{{name}}' in value) || value['{{name}}'] === undefined) return false;
+     if (!('{{name}}' in value){{#isEnum}} || value['{{name}}'] !== Object.values({{datatypeWithEnum}})[0]{{/isEnum}}) return false;
sweep-ai[bot] commented 1 month ago

🚀 Here's the PR! #4

💎 Sweep Pro: You have unlimited Sweep issues

Actions

Relevant files (click to expand). Mentioned files will always appear here. https://github.com/AnotiaWang/openapi-generator/blob/ed2aad6756db1dfc780f709f0a6ee0feeb00f31a/modules/openapi-generator/src/main/resources/typescript-fetch/modelGeneric.mustache#L1-L159 https://github.com/AnotiaWang/openapi-generator/blob/ed2aad6756db1dfc780f709f0a6ee0feeb00f31a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptClientCodegen.java#L1-L1108 https://github.com/AnotiaWang/openapi-generator/blob/ed2aad6756db1dfc780f709f0a6ee0feeb00f31a/samples/client/petstore/typescript-fetch/builds/enum/models/InlineObject.ts#L1-L101 https://github.com/AnotiaWang/openapi-generator/blob/ed2aad6756db1dfc780f709f0a6ee0feeb00f31a/samples/client/petstore/typescript-fetch/builds/enum/models/EnumPatternObject.ts#L1-L96

Step 2: ⌨️ Coding

modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptClientCodegen.java

In the `postProcessModels` method, set the `enumType` property on enum CodegenProperties to make the enum type available in the template.
--- 
+++ 
@@ -9,6 +9,7 @@
             for (CodegenProperty var : cm.vars) {
                 if (Boolean.TRUE.equals(var.isEnum)) {
                     var.datatypeWithEnum = var.datatypeWithEnum.replace(var.enumName, cm.classname + var.enumName);
+                    var.enumType = cm.classname + var.enumName;
                 }
             }
             if (cm.parent != null) {
@@ -16,6 +17,7 @@
                     if (Boolean.TRUE.equals(var.isEnum)) {
                         var.datatypeWithEnum = var.datatypeWithEnum
                                 .replace(var.enumName, cm.classname + var.enumName);
+                        var.enumType = cm.classname + var.enumName;
                     }
                 }
             }

modules/openapi-generator/src/main/resources/typescript-fetch/modelGeneric.mustache

Use the `enumType` property passed from TypeScriptClientCodegen.java instead of `Object.values({{datatypeWithEnum}})`.
--- 
+++ 
@@ -1,5 +1,5 @@
     {{#vars}}
     {{#required}}
-    if (!('{{name}}' in value) || value['{{name}}'] === undefined) return false;
+    if (!('{{name}}' in value){{#isEnum}} || value['{{name}}'] !== {{enumType}}[0]{{/isEnum}}) return false;
     {{/required}}
     {{/vars}}

Step 3: 🔄️ Validating

Your changes have been successfully made to the branch sweep/typescript_fetch_generated_code_with_on. I have validated these changes using a syntax checker and a linter.


[!TIP] To recreate the pull request, edit the issue title or description.

This is an automated message generated by Sweep AI.