Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[BUG] typescript-fetch casts enum scalar values to Blob #11613

Closed
fernard opened this issue Feb 15, 2022 · 7 comments · Fixed by #19016
Closed

[BUG] typescript-fetch casts enum scalar values to Blob #11613

fernard opened this issue Feb 15, 2022 · 7 comments · Fixed by #19016

Comments

@fernard
Copy link

fernard commented Feb 15, 2022

Description

Currently, when the OpenAPI declaration file is being generated, the open-api-generator treats enum values (which are scalars, such as string and numbers) as complex, object-like structures in insists of casting them to Blob as a part of the multipart/form-data request payload.

openapi-generator version

5.3.1

OpenAPI declaration file content or url

This is the output from the FASTApi application, an endpoint expecting a multipart/form-data request with both file and some "metadata" describing this file.

(...)

  "components": {
    "schemas": {
      "Body_create_upload_form_data_request": {
        "title": "Body_create_upload_form_data_request",
        "required": [
          "file_type",
          "some_prop_1",
        ],
        "type": "object",
        "properties": {
          "source_file": {
            "title": "Source File",
            "type": "string",
            "format": "binary"
          },
          "file_type": {
            "$ref": "#/components/schemas/FileType"
          },
          "some_prop_1": {
            "title": "Some Prop",
            "type": "string"
          },
        }
      },

(...)
      "FileType": {
        "title": "FileType",
        "enum": [
          "MY_FILE_TYPE_1",
          "MY_FILE_TYPE_2",
          "MY_FILE_TYPE_3",

        ],
        "type": "string",
        "description": "An enumeration."
      },
(...)
Generation Details

The command-line script to run the docker container with open-api generator

docker run --add-host=host.docker.internal:host-gateway --rm \
    --user $(id -u):$(id -g) \
    -v "${PWD}:/local" openapitools/openapi-generator-cli:v5.3.1 generate \
    -i http://host.docker.internal/api/v1/openapi.json \ # all containers are run in the same docker-compose network, hence the address 
    -g typescript-fetch \
    --additional-properties=typescriptThreePlus=true \
    -o /som/dir 

The output of the generated code:

export enum FileType {
    MyFileType1 = 'MY_FILE_TYPE_1',
    MyFileType2 = 'MY_FILE_TYPE_1',
    MyFileType3 = 'MY_FILE_TYPE_1',
}

if (requestParameters.fileType !== undefined) {
   formParams.append(
       'file_type', new Blob(
       [JSON.stringify(FileTypeToJSON(requestParameters.fileType))],
       { type: "application/json" }
      )
   );
}
Steps to reproduce

Create an appropriate data structure in your code so that it leads to the OpenAPI declaration output as shown above

Suggest a fix

Treat enum value as a primitive and allow it to be directly taken as an argument for formData in multipart/form-data requests.

 if (requestParameters.fileType !== undefined) {
    formParams.append('file_type', requestParameters.fileType as any);
}
@adrianwix
Copy link

The same problem happens when I want to send an object as JSON to the backend. Without the SDK I would just
formData.append('files'. JSON.stringify(data)). I don't see the point of having new Blob() there

@adrianwix
Copy link

new Blob() makes "multerjs" fails also. Because it thinks the the json is a file and I didn't specified the field with the json to be a file inside the multer configuration

@zmrl010
Copy link

zmrl010 commented Apr 12, 2022

Im having issues with this also. I was able to get it working for my needs with a workaround, but I would much rather this handle it properly and generate the client code cleanly. If anyone is curious, here is the workaround I used. It essentially unravels the Blob wrapper around the enum to get the string value in a middleware.

async function ResolveAssetTypeEnumMiddleware(context: RequestContext) {
  const { body } = context.init;

  if (body && body instanceof FormData) {
    const type = body.get("type");
    if (type instanceof File) {
      const jsonType = await type.text();
      const resolvedType = JSON.parse(jsonType);
      body.set("type", resolvedType);
    }
  }

  return context;
}

const assetsApi = new AssetsApi(tokenApiConfig).withPreMiddleware(
  ResolveAssetTypeEnumMiddleware
);

@kigawas
Copy link

kigawas commented Sep 20, 2022

@zmrl010 Thanks!

BTW, if you have multiple types to handle:

async function resolveEnumTypes(context: RequestContext) {
  const { body } = context.init;

  if (body && body instanceof FormData) {
    for (const key of ["amazing_type1", "amazing_type2"]) {
      const type = body.get(key);

      if (type instanceof File) {
        const jsonType = await type.text();
        const resolvedType = JSON.parse(jsonType);
        body.set(key, resolvedType);
      }
    }
  }

  return context;
}

@fincha
Copy link

fincha commented Dec 1, 2022

Confirmed this with 6.2.1 have the same issue

Request Interface

export interface EmployeesPatchRequestParams {
  /** A unique integer value identifying this User. */
  id: number;
  /** Company ID for which employees should be filtered. */
  company_id?: number;
  position?: string | null;
  lang?: PatchedUserPatchRequestLang | null;
}

implementation

if (lang !== undefined) {
      localVarFormParams =
        (localVarFormParams.append(
          'lang',
          localVarUseForm ? new Blob([JSON.stringify(lang)], { type: 'application/json' }) : <any>lang
        ) as any) || localVarFormParams;
    }

swagger

"PatchedUserRequest": {
                "type": "object",
                "properties": {
                    "position": {
                        "type": "string",
                        "nullable": true,
                        "maxLength": 256
                    },
                    "lang": {
                        "nullable": true,
                        "oneOf": [
                            {
                                "$ref": "#/components/schemas/LangEnum"
                            },
                            {
                                "$ref": "#/components/schemas/BlankEnum"
                            },
                            {
                                "$ref": "#/components/schemas/NullEnum"
                            }
                        ]
                    },

@Davidos533
Copy link

Davidos533 commented Apr 30, 2023

Same problem with multipart/form-data
When i try to send files and json string as form-data key-value
openapi-generator-cli add new Blob

openapi-generator-cli version 6.5.0

swagger.json

"requestBody": {
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "properties": {
                  "request": {
                    "$ref": "#/components/schemas/MySchema"
                  },
                  "files": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "format": "binary"
                    }
                  }
                }
              },

generated code:

if (request !== undefined) { 
                localVarFormParams.append('request', new Blob([JSON.stringify(request)], { type: "application/json", }));
            }
                if (files) {
                files.forEach((element) => {
                    localVarFormParams.append('files', element as any);
                })
            }

Fix it please

@R3DST0RM
Copy link

R3DST0RM commented Oct 5, 2023

Also hoping in. How it is currently done, requests fail on mobile devices using React-Native with fetch. Treating enum values as scalar values fixes it.

 if (requestParameters.type !== undefined) {
    formParams.append('type', requestParameters.type as any);
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
7 participants