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.85k stars 6.59k forks source link

[BUG][aspnetcore] FileParameters should be refactor to multimap and support content-type #11132

Open joaocmendes opened 2 years ago

joaocmendes commented 2 years ago

Bug Report Checklist

Description

Build fails for a request with multipart-form-data when there is multiple files for the same field. The client receives a List but this can't be handled by RequestOptions because it only accepts a single file for the same key.

The API consumed requires the content-type for the file sent (e.g. application/pdf, image/png). There is no way to achieve this with the actual generator.

openapi-generator version

openapi-generator-cli-5.3.0.jar

OpenAPI declaration file content or url
{
  "openapi": "3.0.1",
  "info": {
    "title": "Test API",
    "version": "1.0"
  },
  "servers": [
    {
      "url": "/ocrapis"
    }
  ],
  "paths": {
    "/api/v1/Documents/{entityName}": {
      "post": {
        "tags": [
          "Documents"
        ],
        "summary": "Returns Extraction Result of Your Request",
        "parameters": [
          {
            "name": "entityName",
            "in": "path",
            "description": "Tenant Name",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "properties": {
                  "UploadedFiles": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "format": "binary"
                    },
                    "description": "Files to be Extracted"
                  },
                  "UploadedFileTypes": {
                    "type": "string",
                    "description": "File Type"
                  },
                  "ProjectName": {
                    "type": "string",
                    "description": "Project Name of Tenant"
                  }
                }
              },
              "encoding": {
                "UploadedFiles": {
                  "style": "form",
                          "contentType": "application/pdf"
                },
                "UploadedFileTypes": {
                  "style": "form"
                },
                "ProjectName": {
                  "style": "form"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Successfull Request",
            "content": {
              "application/json": {
                "schema": {
                "type": "object"    
                }
              }
            }
          }
        }
      }
    },
  }
}
Generation Details

java -jar openapi-generator-cli-5.3.0.jar generate -i swagger.json -g csharp-netcore -o testNew

Steps to reproduce

generate the code build the code it fails with Error CS1503 Argument 2: cannot convert from 'System.Collections.Generic.List<System.IO.Stream>' to 'System.IO.Stream'

Suggest a fix

Change on RequestOptions class the field FileParameters from Dictionary<string, Stream> to Multimap<string, Stream> would fix the issue as follows:

// Actual 
public Dictionary<string, Stream> FileParameters { get; set; }

// New 
public Multimap<string, Stream> FileParameters { get; set; }

This fix also requires a change at ApiClient to iterate the multiples files under the same key. Code block: image

FileParameters should support the defintion of a ContentType or the code could infer from the file extension (but it can be tricky not for the common types like pdf but for some ambiguous types). It's already supported by aspnetcore httpClient and RestSharp when creating file as follows:

public static FileParameter Create(string name, byte[] data, string filename, string contentType);
saarivirtajCGI commented 2 years ago

I tried out the fix, in addition to the above Multimap<string, Stream> RequestOptions class fix I applied the following fix to ApiClient, which seemed to do the trick:

if (options.FileParameters != null)
{
    foreach (var fileParam in options.FileParameters)
    {
        foreach(var item in fileParam.Value)
        {
            var bytes = ClientUtils.ReadAsBytes(item);
            var fileStream = item as FileStream;
            if (fileStream != null)
                request.Files.Add(FileParameter.Create(fileParam.Key, bytes, System.IO.Path.GetFileName(fileStream.Name)));
            else
                request.Files.Add(FileParameter.Create(fileParam.Key, bytes, "no_file_name_provided"));
        }
    }
}
wing328 commented 2 years ago

Please submit a PR with the suggested fix and we'll review accordingly.

joaocmendes commented 2 years ago

@saarivirtajCGI In my opinion thats it, is the same code I had on on my side to make the workaround. I haven't the time to go to the pre-generated code and check what we had to do in order to generate with this.

One not so simple is about the contentType. I've created an util method to get the content-type of the file based on the name but its not safe at all. It's possible to create a class that extends FileStream and has an extra property to achieve this?

My code is the following:

    if (options.FileParameters != null)
    {
        foreach (var fileParam in options.FileParameters)
        {
            foreach (var file in fileParam.Value)
            {
                var bytes = ClientUtils.ReadAsBytes(file);
                var fileStream = file as FileStream;
                if (fileStream != null)
                    request.Files.Add(FileParameter.Create(fileParam.Key, bytes, System.IO.Path.GetFileName(fileStream.Name), this.GetContentTypeForFile(fileStream.Name)));
                else
                    request.Files.Add(FileParameter.Create(fileParam.Key, bytes, "no_file_name_provided"));
            }
        }
    }
joaocmendes commented 2 years ago

@wing328 PR submited