OpenAPITools / OpenAPITools/openapi-generator

Incorrect generation of Retrofit interfaces for file uploads

Open
#19,693 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Issue: Bug
Dominant language
Java
Stars
26.8k
Forks
7.7k
PR merge metrics
PR metrics pending

Description

Bug Report Checklist
  • Have you provided a full/minimal spec to reproduce the issue?
  • Have you validated the input using an OpenAPI validator (example)?
  • Have you tested with the latest master to confirm the issue still exists?
  • Have you searched for related issues/PRs?
  • What's the actual output vs expected output?
  • [Optional] Sponsorship to speed up the bug fix or feature request (example)
Description

I have a project using the Kotlin generator and jvm-retrofit2 as the library. For file uploads, my OpenAPI schema has the following request body definition:

      requestBody:
        content:
          application/octet-stream:
            schema:
              format: binary
              type: string
        required: true

The resulting interface uses java.io.File as the body for the Retrofit interface. However, Retrofit doesn't support File for file uploads out of the box, resulting in an exception at runtime.

openapi-generator version

7.8.0

OpenAPI declaration file content or url

https://github.com/fyreplace/fyreplace-android/blob/develop/app/src/main/assets/openapi.yaml

Generation Details

The code is generated using the official Gradle plugin.

Steps to reproduce
  • Clone https://github.com/fyreplace/fyreplace-android
  • Run ./gradlew openapiGenerate inside the repository
  • Check the resulting app/build/openapi/src/main/kotlin/app/fyreplace/api/UsersEndpointApi.kt, and look for setCurrentUserAvatar()
Related issues/PRs
Suggest a fix

A custom Converter for files can be created to handle files in request bodies. I have managed to make my uploads work with the following code:

import okhttp3.RequestBody
import okhttp3.RequestBody.Companion.asRequestBody
import retrofit2.Converter
import retrofit2.Retrofit
import java.io.File
import java.lang.reflect.Type

class FileConverterFactory : Converter.Factory() {
    override fun requestBodyConverter(
        type: Type,
        parameterAnnotations: Array<out Annotation>,
        methodAnnotations: Array<out Annotation>,
        retrofit: Retrofit
    ) = when (type) {
        File::class.java -> Converter<File, RequestBody> { it.asRequestBody(null) }
        else -> null
    }

    companion object {
        fun create() = FileConverterFactory()
    }
}

The equivalent Java implementation would look something like this:

import androidx.annotation.Nullable;
import java.io.File;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import okhttp3.RequestBody;
import retrofit2.Converter;
import retrofit2.Retrofit;

public class FileConverterFactory extends Converter.Factory {
    @Nullable
    @Override
    public Converter<?, RequestBody> requestBodyConverter(Type type, Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
        return type == File.class
                ? (File file) -> RequestBody.create(file, null)
                : null;
    }

    public static FileConverterFactory create() {
        return new FileConverterFactory();
    }
}

Adding an instance of this FileConverterFactory to the list of converter factories when creating the API client makes Retrofit perfectly happy to send File objects in its requests.

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

Reproduce the report by cloning fyreplace-android, running ./gradlew openapiGenerate, and inspecting app/build/openapi/src/main/kotlin/app/fyreplace/api/UsersEndpointApi.kt at setCurrentUserAvatar(). Compare the generated request body with the application/octet-stream schema in app/src/main/assets/openapi.yaml. Done means the generated Retrofit interface handles this file upload without the reported runtime failure.

Written by the indexing model from the issue text.

Assessment

Tech stack
android, kotlin
Domain
api, mobile-dev
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.