swagger-api / swagger-api/swagger-codegen

[JAVASCRIPT] unable to upload file using `contentType == 'multipart/form-data'`

Open
#11,000 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Mustache
Stars
17.8k
Forks
6k
PR merge metrics
No merged PRs in 30d

Description

Description

When processing (uploading) a file that makes use of the content type of “multipart/form-data” the current logic normalizes the parameter values to a string object, where it should be a file object.

Swagger-codegen version

Swagger Codegen 3.0.25

Swagger declaration file content or url

The issue occurs in line 428 (see attached file) when the contentType is evaluated to be of type multipart/form-data, it calls the method normalizeParams (213) where the file parameter is formatted as a string because it fails to identify as params as a file parameter.

ApiClient.zip
Partial code from the callAPI method:

        if (contentType === 'application/x-www-form-urlencoded') {
            request.send(querystring.stringify(this.normalizeParams(formParams)));
        } else if (contentType == 'multipart/form-data') {
            var _formParams = this.normalizeParams(formParams);
            for (var key in _formParams) {
                if (_formParams.hasOwnProperty(key)) {
                    if (this.isFileParam(_formParams[key])) {
                        // file field
                        request.attach(key, _formParams[key]);
                    } else {
                        request.field(key, _formParams[key]);
                    }
                }
            }
        } else if (bodyParam) {
            request.send(bodyParam);
        }

The normalizeParams method:

    /**
    * Normalizes parameter values:
    * <ul>
    * <li>remove nils</li>
    * <li>keep files and arrays</li>
    * <li>format to string with `paramToString` for other cases</li>
    * </ul>
    * @param {Object.<String, Object>} params The parameters as object properties.
    * @returns {Object.<String, Object>} normalized parameters.
    */
    normalizeParams(params) {
        var newParams = {};
        for (var key in params) {
            if (params.hasOwnProperty(key) && params[key] != undefined && params[key] != null) {
                var value = params[key];
                if (this.isFileParam(value) || Array.isArray(value)) {
                    newParams[key] = value;
                } else {
                    newParams[key] = this.paramToString(value);
                }
            }
        }

        return newParams;
    }
Command line used for generation

JAVA - openjdk 11.0.10 2021-01-19
OpenJDK Runtime Environment AdoptOpenJDK (build 11.0.10+9)
OpenJDK 64-Bit Server VM AdoptOpenJDK (build 11.0.10+9, mixed mode)

java generate -i %YAML_PATH% -l javascript -t javascript_template -o %OUT_PATH% --additional-properties useES6=true

Steps to reproduce

Generate a javascript client SDK from and API that makes use of contentType == 'multipart/form-data'
Create an application that makes use of the SDK and attempt to upload a file to a hosting services (URL).

Related issues/PRs

Searched issues, did not find any related reported issues.

Suggest a fix/enhancement

Course of Action: 1
Add a check in the normalizeParams method to check if the value is a stream buffer (line 216)

/**
    * Normalizes parameter values:
    * <ul>
    * <li>remove nils</li>
    * <li>keep files and arrays</li>
    * <li>format to string with `paramToString` for other cases</li>
    * </ul>
    * @param {Object.<String, Object>} params The parameters as object properties.
    * @returns {Object.<String, Object>} normalized parameters.
    */
    normalizeParams(params) {
        var newParams = {};
        for (var key in params) {
            if (params.hasOwnProperty(key) && params[key] != undefined && params[key] != null || isStream.readable(value)
) {
                var value = params[key];
                if (this.isFileParam(value) || Array.isArray(value)) {
                    newParams[key] = value;
                } else {
                    newParams[key] = this.paramToString(value);
                }
            }
        }

        return newParams;
    }

Course of Action 2
Add a is stream buffer check in the isFileParam method

    /**
    * Checks whether the given parameter value represents file-like content.
    * @param param The parameter to check.
    * @returns {Boolean} <code>true</code> if <code>param</code> represents a file.
    */
    isFileParam(param) {
        // fs.ReadStream in Node.js and Electron (but not in runtime like browserify)
        if (typeof require === 'function') {
            let fs;
            try {
                fs = require('fs');
            } catch (err) {}
            if (fs && fs.ReadStream && param instanceof fs.ReadStream) {
                return true;
            }
        }

        // HACK (needs import isStream from "is-stream";)
        // Readstream - fs.createReadStream or fs.ReadStream
        if (isStream.readable(param)) {
            return true;
        }
		
        // Buffer in Node.js
        if (typeof Buffer === 'function' && param instanceof Buffer) {
            return true;
        }

        // Blob in browser
        if (typeof Blob === 'function' && param instanceof Blob) {
            return true;
        }

        // File in browser (it seems File object is also instance of Blob, but keep this for safe)
        if (typeof File === 'function' && param instanceof File) {
            return true;
        }

        return false;
    }

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the generated JavaScript ApiClient's normalizeParams and isFileParam methods described around lines 213 and 428, then reproduce a multipart/form-data upload using the provided generation command and ApiClient example. Done means stream, Buffer, Blob, or File values remain file-like so multipart requests use request.attach rather than stringifying them; no test file is named in the issue.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript
Domain
api
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
42/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.