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
21.36k stars 6.46k forks source link

Enum value names with spaces are legal in JSON/YAML but illegal in C #12263

Open dsyer opened 2 years ago

dsyer commented 2 years ago

Spec:

openapi: "3.0.0"
info:
  description: "test"
  version: "1.0.0"
  title: "myTest"
  contact:
    email: "my@mail.com"
servers:
- url: "http://localhost:9998/v1"
tags:
- name: "users"
paths: {}
components:
  schemas:
    UserDetail:
      type: "object"
      description: "Detail of a User"
      properties:
        name:
          type: "string"
          description: "the name of the user"
        type:
          description: "the type of the user"
          type: "string"
          enum:
          - zero
          - 'one potato'
          - 'two potato'

What's the actual output vs expected output?

Expected output is code that compiles in the target language (e.g. C). Actual output has syntax errors.

$ java -jar ../modules/openapi-generator-cli/target/openapi-generator-cli.jar generate -g c -i enum_with_spaces.yaml
$ cat model/user_detail.h
...
// Enum TYPE for user_detail

// Enum TYPE for user_detail

typedef enum  { mytest_user_detail_TYPE_NULL = 0, mytest_user_detail_TYPE_zero, mytest_user_detail_TYPE_one potato, mytest_user_detail_TYPE_two potato } mytest_user_detail_TYPE_e;
...

The whistespace in the YAML enum value is transferred directly to the C code which then doesn't compile. The problem is this method in DefaultCodegen.java:

    @Override
    @SuppressWarnings("static-method")
    public String escapeText(String input) {
        if (input == null) {
            return input;
        }

        // remove \t, \n, \r
        // replace \ with \\
        // replace " with \"
        // outer unescape to retain the original multi-byte characters
        // finally escalate characters avoiding code injection
        return escapeUnsafeCharacters(
                StringEscapeUtils.unescapeJava(
                                StringEscapeUtils.escapeJava(input)
                                        .replace("\\/", "/"))
                        .replaceAll("[\\t\\n\\r]", " ")
                        .replace("\\", "\\\\")
                        .replace("\"", "\\\""));
    }

Seems like you could fix it by simply replacing spaces with underscores (for instance), but that might depend on something I don't know about. You would want the JSON sent to the server by a client to be the same as before.

dsyer commented 2 years ago

I think this is just a bug in the templates for the C code. Java enums come out legal because they use the "name" not the "value" as the identifier. You could fix the example above like this:

--- a/modules/openapi-generator/src/main/resources/C-libcurl/model-header.mustache
+++ b/modules/openapi-generator/src/main/resources/C-libcurl/model-header.mustache
@@ -23,7 +23,7 @@ typedef struct {{classname}}_t {{classname}}_t;
 {{#allowableValues}}
 // Enum {{enumName}} for {{classVarName}}

-typedef enum { {{projectName}}_{{classVarName}}_{{enumName}}_NULL = 0{{#enumVars}}, {{projectName}}_{{classVarName}}_{{enumName}}_{{{value}}}{{/enumVars}} } {{projectName}}_{{classVarName}}_{{enumName}}_e;
+typedef enum { {{projectName}}_{{classVarName}}_{{enumName}}_NULL = 0{{#enumVars}}, {{projectName}}_{{classVarName}}_{{enumName}}_{{{name}}}{{/enumVars}} } {{projectName}}_{{classVarName}}_{{enumName}}_e;
 {{/allowableValues}}

 char* {{classFilename}}_{{classname}}_ToString({{projectName}}_{{classVarName}}_{{enumName}}_e {{classname}});
@@ -43,7 +43,7 @@ char* {{classFilename}}_{{classname}}_ToString({{projectName}}_{{classVarName}}_
 // Enum {{enumName}} for {{classVarName}}

         {{#allowableValues}}
-typedef enum  { {{projectName}}_{{classVarName}}_{{enumName}}_NULL = 0{{#enumVars}}, {{projectName}}_{{classVarName}}_{{enumName}}_{{{value}}}{{/enumVars}} } {{projectName}}_{{classVarName}}_{{enumName}}_e;
+typedef enum  { {{projectName}}_{{classVarName}}_{{enumName}}_NULL = 0{{#enumVars}}, {{projectName}}_{{classVarName}}_{{enumName}}_{{{name}}}{{/enumVars}} } {{projectName}}_{{classVarName}}_{{enumName}}_e;
         {{/allowableValues}}

 char* {{classFilename}}_{{name}}_ToString({{projectName}}_{{classVarName}}_{{enumName}}_e {{name}});
@@ -60,7 +60,7 @@ char* {{classFilename}}_{{name}}_ToString({{projectName}}_{{classVarName}}_{{enu
 // Enum {{enumName}} for {{classVarName}}

             {{#allowableValues}}
-typedef enum  { {{projectName}}_{{classVarName}}_{{enumName}}_NULL = 0{{#enumVars}}, {{projectName}}_{{classVarName}}_{{enumName}}_{{{value}}}{{/enumVars}} } {{projectName}}_{{classVarName}}
_{{enumName}}_e;
+typedef enum  { {{projectName}}_{{classVarName}}_{{enumName}}_NULL = 0{{#enumVars}}, {{projectName}}_{{classVarName}}_{{enumName}}_{{{name}}}{{/enumVars}} } {{projectName}}_{{classVarName}}_
{{enumName}}_e;
             {{/allowableValues}}

 char* {{classFilename}}_{{name}}_ToString({{projectName}}_{{classVarName}}_{{enumName}}_e {{name}});

I don't know what to suggest for the best though because this will break existing code that happens not to use spaces in enum values. On the other hand it's clearly broken when there are spaces. So what would be the right thing to do?

AmeliaCrowther commented 2 years ago

I don't think this is limited to the C generator - here's a screenshot of python code being generated with spaces in the middle of enum value names

Screenshot 2022-05-11 at 17 17 09