openapi-generator icon indicating copy to clipboard operation
openapi-generator copied to clipboard

[BUG] [DART] Dart Generator does not generate Multipart properly. Sends an empty object instead of file

Open Yegorisa opened this issue 2 years ago • 12 comments

Bug Report Checklist

  • [x] Have you provided a full/minimal spec to reproduce the issue? Started from 6.0.0 (5.4.0 worked perfectly)
  • [x] Have you validated the input using an OpenAPI validator (example)?
  • [ ] Have you tested with the latest master to confirm the issue still exists?
  • [x] Have you searched for related issues/PRs?
  • [x] What's the actual output vs expected output?
Description

The Dart generator does not generate multipart properly. It sends an empty object instead of a multipart file provided. It appears only with such configuration: dart-dio, json_serializable and timemachine.

If you use just a dart generator instead of dart-dio it generates it properly.

Here is an example of faulty outputs

Future<Response<UploadProfessionalImage201Response>> uploadProfessionalImage({ 
    required String professionalId,
    MultipartFile? image,
    CancelToken? cancelToken,
    Map<String, dynamic>? headers,
    Map<String, dynamic>? extra,
    ValidateStatus? validateStatus,
    ProgressCallback? onSendProgress,
    ProgressCallback? onReceiveProgress,
  }) async {
    final _path = r'/images/professionals/{professionalId}/'.replaceAll('{' r'professionalId' '}', professionalId.toString());
    final _options = Options(
      method: r'POST',
      headers: <String, dynamic>{
        ...?headers,
      },
      extra: <String, dynamic>{
        'secure': <Map<String, String>>[
          {
            'type': 'apiKey',
            'name': 'BearerToken',
            'keyName': 'Authorization',
            'where': 'header',
          },
        ],
        ...?extra,
      },
      contentType: 'multipart/form-data',
      validateStatus: validateStatus,
    );

    dynamic _bodyData;

    try {

    } catch(error, stackTrace) {
      throw DioError(
         requestOptions: _options.compose(
          _dio.options,
          _path,
        ),
        type: DioErrorType.other,
        error: error,
      )..stackTrace = stackTrace;
    }

    final _response = await _dio.request<Object>(
      _path,
      data: _bodyData,
      options: _options,
      cancelToken: cancelToken,
      onSendProgress: onSendProgress,
      onReceiveProgress: onReceiveProgress,
    );

    UploadProfessionalImage201Response _responseData;

    try {
_responseData = deserialize<UploadProfessionalImage201Response, UploadProfessionalImage201Response>(_response.data!, 'UploadProfessionalImage201Response', growable: true);
    } catch (error, stackTrace) {
      throw DioError(
        requestOptions: _response.requestOptions,
        response: _response,
        type: DioErrorType.other,
        error: error,
      )..stackTrace = stackTrace;
    }

    return Response<UploadProfessionalImage201Response>(
      data: _responseData,
      headers: _response.headers,
      isRedirect: _response.isRedirect,
      requestOptions: _response.requestOptions,
      redirects: _response.redirects,
      statusCode: _response.statusCode,
      statusMessage: _response.statusMessage,
      extra: _response.extra,
    );
  }

As you can see _bodyData is completely empty.

This is a problem because it makes this API call completely useless and swapping to standard dart generator would mean a lot of refactoring and losing features for us.

openapi-generator version

From 6.0.0 and onward

5.4.0 works fine

OpenAPI declaration file content or url
 "/images/professionals/{professionalId}/": {
            "post": {
                "security": [
                    {
                        "BearerToken": []
                    }
                ],
                "description": "Upload image for professional (profile image)",
                "consumes": [
                    "multipart/form-data"
                ],
                "tags": [
                    "Image"
                ],
                "operationId": "UploadProfessionalImage",
                "parameters": [
                    {
                        "type": "file",
                        "description": "Image file",
                        "name": "image",
                        "in": "formData",
                        "required": false
                    },
                    {
                        "type": "string",
                        "description": "Professional ID",
                        "name": "professionalId",
                        "in": "path",
                        "required": true
                    }
                ],
                "responses": {
                    "201": {
                        "description": "Created",
                        "schema": {
                            "allOf": [
                                {
                                    "type": "object"
                                },
                                {
                                    "type": "object",
                                    "properties": {
                                        "data": {
                                            "$ref": "#/definitions/dto.Image"
                                        }
                                    }
                                }
                            ]
                        }
                    },
                    "default": {
                        "description": "",
                        "schema": {
                            "allOf": [
                                {
                                    "type": "object"
                                },
                                {
                                    "type": "object",
                                    "properties": {
                                        "error": {
                                            "$ref": "#/definitions/errs.E"
                                        }
                                    }
                                }
                            ]
                        }
                    }
                }
            }
        },
Generation Details

openapi-generator-cli generate -i swagger.json -g dart-dio -o ./ -p dateLibrary=timemachine,pubLibrary=eliva.api,pubName=project_api,serializationLibrary=json_serializable

Steps to reproduce

Just generate using the command and the faulty code appears

Suggest a fix

No idea unfortunatelly

Yegorisa avatar Jan 17 '23 10:01 Yegorisa

We are also facing this issue! Very annoying!

adar2378 avatar Jan 26 '23 13:01 adar2378

We are facing this issue with version 6.5.0

Here is an sample of swagger.json "requestBody": { "content": { "multipart/form-data": { "schema": { "required": [ "files" ], "type": "object", "properties": { "files": { "type": "array", "items": { "type": "string", "format": "binary" } } } }, "encoding": { "files": { "style": "form" } } } } },

And the workaround we implemented on generated file FormData _bodyData = FormData.fromMap( <String, List<MultipartFile>>{"files": files}); // MANUALLY ADDED !

francois-robert avatar May 11 '23 08:05 francois-robert

@jaumard (2018/09) @josh-burton (2019/12) @amondnet (2019/12) @sbu-WBT (2020/12) @kuhnroyal (2020/12) @agilob (2020/12) @ahmednfwela (2021/08)

ahmednfwela avatar May 28 '23 18:05 ahmednfwela

@Yegorisa You seem to be using OpenAPI 2.0 - please provide a full spec that reproduces the issue.

@francois-robert I generated your snippet on master and got the following result, which look correct on the first glance:

  /// fooPost
  /// 
  ///
  /// Parameters:
  /// * [files] 
  /// * [cancelToken] - A [CancelToken] that can be used to cancel the operation
  /// * [headers] - Can be used to add additional headers to the request
  /// * [extras] - Can be used to add flags to the request
  /// * [validateStatus] - A [ValidateStatus] callback that can be used to determine request success based on the HTTP status of the response
  /// * [onSendProgress] - A [ProgressCallback] that can be used to get the send progress
  /// * [onReceiveProgress] - A [ProgressCallback] that can be used to get the receive progress
  ///
  /// Returns a [Future]
  /// Throws [DioException] if API call or serialization fails
  Future<Response<void>> fooPost({ 
    required BuiltList<MultipartFile> files,
    CancelToken? cancelToken,
    Map<String, dynamic>? headers,
    Map<String, dynamic>? extra,
    ValidateStatus? validateStatus,
    ProgressCallback? onSendProgress,
    ProgressCallback? onReceiveProgress,
  }) async {
    final _path = r'/foo';
    final _options = Options(
      method: r'POST',
      headers: <String, dynamic>{
        ...?headers,
      },
      extra: <String, dynamic>{
        'secure': <Map<String, String>>[],
        ...?extra,
      },
      contentType: 'multipart/form-data',
      validateStatus: validateStatus,
    );

    dynamic _bodyData;

    try {
      _bodyData = FormData.fromMap(<String, dynamic>{
        r'files': files.toList(),
      });

    } catch(error, stackTrace) {
      throw DioException(
         requestOptions: _options.compose(
          _dio.options,
          _path,
        ),
        type: DioExceptionType.unknown,
        error: error,
        stackTrace: stackTrace,
      );
    }

    final _response = await _dio.request<Object>(
      _path,
      data: _bodyData,
      options: _options,
      cancelToken: cancelToken,
      onSendProgress: onSendProgress,
      onReceiveProgress: onReceiveProgress,
    );

    return _response;
  }

kuhnroyal avatar Jul 17 '23 11:07 kuhnroyal

@kuhnroyal Thanks for your answer. It seems that it is working with default parameters but when using "--additional-properties=serializationLibrary=json_serializable" it does generate _bodyData at all.

francois-robert avatar Jul 17 '23 11:07 francois-robert

Ah I see, thanks - will take a look.

kuhnroyal avatar Jul 17 '23 11:07 kuhnroyal

note that this is fixed in DartNext generator

ahmednfwela avatar Jul 17 '23 12:07 ahmednfwela

Nice! Still might fix it for 7.0.0 in dart-dio. Will see if I find the time.

kuhnroyal avatar Jul 17 '23 12:07 kuhnroyal

Any status on this? Or is this being dropped in favour of DartNext (Can't seem to find what 'DartNext' is though 😅)?

Manatho avatar Aug 29 '23 08:08 Manatho

Can't seem to find what 'DartNext' is though 😅

It's this PR https://github.com/OpenAPITools/openapi-generator/pull/15485

ahmednfwela avatar Aug 29 '23 09:08 ahmednfwela

It's this PR https://github.com/OpenAPITools/openapi-generator/pull/15485

Ah thanks! searched for DartNext, rather than dart-next... 🙃

Also thanks for the quick response 😄

Manatho avatar Aug 29 '23 17:08 Manatho

@kuhnroyal Thanks for your answer. It seems that it is working with default parameters but when using "--additional-properties=serializationLibrary=json_serializable" it does generate _bodyData at all.

Hi, is this solved? Still happened to me, using docker image. Working fine if remove the additional-properties.

ariefsn avatar May 08 '24 23:05 ariefsn