OpenAPITools / OpenAPITools/openapi-generator

[BUG][JAVA][OKHTTP] UnsupportedOperationException when using okhhtpclient with gzip feature enabled

Open
#3,432 5 comments 1 reaction 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

Description

When instantiating a java/okhttp based client (generated with 'useGzipFeature' enabled) with parameters for oauth authentication, the constructor throws a java.lang.UnsupportedOperationException because the generated ApiClient tries to add the GzipRequestInterceptor' to an Unmodifiable Collection of the 'OkHttpClient.

The error originates in the generated code of the ApiClient during initialization, because httpClient.interceptors().add(...) is called after httpClient = builder.build();:

private void init() {
        OkHttpClient.Builder builder = new OkHttpClient.Builder();
        builder.addNetworkInterceptor(getProgressInterceptor());
        builder.addInterceptor(new GzipRequestInterceptor());
        httpClient = builder.build();

        // Enable gzip request compression
        httpClient.interceptors().add(new GzipRequestInterceptor());

        verifyingSsl = true;

        json = new JSON();

        // Set default User-Agent.
        setUserAgent("OpenAPI-Generator/0.2.1-SNAPSHOT/java");

        authentications = new HashMap<String, Authentication>();
}
openapi-generator version

4.0.0

OpenAPI declaration file content or url
openapi: 3.0.2
info:
  version: 0.2.1
  title: My Api
servers:
  - url: 'https://my.domain.org'
security:
  - myAuth:
      - 'api:access'
paths:
  '/api/v1/test':
    post:
      summary: Create a new object
      description: >-
        Creates a new object
      operationId: createObject
      tags:
        - command
        - object
      requestBody:
        $ref: '#/components/requestBodies/Object.JsonApi.v1'
      responses:
        '202':
          description: 'Object is being created'
        default:
          $ref: '#/components/responses/Error.JsonApi.v1'

components:
  # ------------------------------------------------------------------------- SCHEMAS
  schemas:
    # ------------------------------------------------------------------------- BASE SCHEMAS

    Object:
      description: 'An object'
      type: object
      required:
        - type
      properties:
        id:
          type: string
          description: 'The domain object identifier'
          example: 'c24c0bfb-7a15-4f17-94e2-9b112491b882'
        type:
          type: string
          readOnly: true
          description: 'The type of the domain object'
          default: 'my.api.model.Object'

    Object.JsonApi.v1:
      description: 'Object details, JSON:API v1.0'
      type: object
      required:
        - data
      properties:
        data:
          $ref: '#/components/schemas/Object'

    ErrorMetadata:
      # only used in responses for now, no need to declare required properties
      description: 'Basic error object, JSON:API v1.0'
      type: object
      properties:
        code:
          type: string
          description: 'Application specific error code'
          readOnly: true
          example: 'ERR_0011'
        status:
          type: string
          description: 'The HTTP status code applicable to this problem'
          readOnly: true
          example: '400'
        detail:
          type: string
          description: 'A verbose, human readable description of the error, may be localized'
          readOnly: true
          example: 'The validation has failed due to a missing parameter.'

    # ------------------------------------------------------------------------- RESPONSES
  responses:
    Error.JsonApi.v1:
      description: Unexpected, unspecified error
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorMetadata'
          example:
            errors:
              - code: 'ERR_0500'
                status: '500'
                details: "Something happened, but we cannot determine what exactly."

  # ------------------------------------------------------------------------- REQUEST BODIES
  requestBodies:
    Object.JsonApi.v1:
      description: Object
      required: true
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Object.JsonApi.v1'

  # ------------------------------------------------------------------------- SECURITY SCHEMES
  securitySchemes:
    myAuth:
      type: oauth2
      flows:
        password:
          tokenUrl: /api/v1/oauth2/token
          refreshUrl: /api/v1/oauth2/token
          scopes:
            api:access: "Access My REST API"
Command line used for generation

used via the openapi-generator-maven-plugin

<plugin>
                <groupId>org.openapitools</groupId>
                <artifactId>openapi-generator-maven-plugin</artifactId>
                <version>4.0.0</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>generate</goal>
                        </goals>
                        <phase>process-sources</phase>
                        <configuration>
                            <verbose>false</verbose>
                            <inputSpec>minimal.yaml</inputSpec>
                            <skipValidateSpec>false</skipValidateSpec>
                            <generatorName>java</generatorName>
                            <output>${project.build.directory}/generated-sources/openapi</output>
                            <addCompileSourceRoot>true</addCompileSourceRoot>
                            <apiPackage>my.api.client</apiPackage>
                            <modelPackage>my.api.model</modelPackage>
                            <modelNamePrefix>DTO</modelNamePrefix>
                            <generateApis>true</generateApis>
                            <generateApiTests>false</generateApiTests>
                            <generateApiDocumentation>true</generateApiDocumentation>
                            <generateModels>true</generateModels>
                            <generateModelTests>false</generateModelTests>
                            <generateModelDocumentation>true</generateModelDocumentation>
                            <generateSupportingFiles>true</generateSupportingFiles>
                            <groupId>my.api</groupId>
                            <artifactId>client</artifactId>
                            <artifactVersion>${project.version}</artifactVersion>
                            <withXml>false</withXml>
                            <configOptions>
                                <library>okhttp-gson</library>
                                <title>${project.name}</title>
                                <fullJavaUtil>false</fullJavaUtil>
                                <sourceFolder>src/main/java</sourceFolder>
                                <dateLibrary>java8</dateLibrary>
                                <java8>true</java8>
                                <booleanGetterPrefix>is</booleanGetterPrefix>
                                <useBeanValidation>true</useBeanValidation>
                                <performBeanValidation>true</performBeanValidation>
                                <useGzipFeature>true</useGzipFeature>
                                <caseInsensitiveResponseHeaders>true</caseInsensitiveResponseHeaders>
                            </configOptions>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
Steps to reproduce
  1. generate the client with the sample file (oauth authentication included)
  2. instantiate a client by
String clientId = "myClientId";
String clientSecret = "myClientSecret";
String username = "myUsername";
String password = "myPasswd";
Map<String, String> params = new HashMap<>();
params.put("username", username);
params.put("password", password);
ApiClient client = new ApiClient(clientId, clientSecret, params);

acutal output: java.lang.UnsupportedOperationException
expected output: client is instantiated with specified parameters for OAuth

Related issues/PRs

see also https://github.com/square/okhttp/issues/2219

Suggest a fix

fix the ApiClient Mustache Template in line 147 to add the GzipRequestInterceptor before the builder is built

private void init() {
        OkHttpClient.Builder builder = new OkHttpClient.Builder();
        builder.addNetworkInterceptor(getProgressInterceptor());
        // Enable gzip request compression (MOVED HERE)
        builder.addInterceptor(new GzipRequestInterceptor());
                
        httpClient = builder.build();

       // gzip request compression MOVED FROM HERE
        
        verifyingSsl = true;

        json = new JSON();

        // Set default User-Agent.
        setUserAgent("OpenAPI-Generator/0.2.1-SNAPSHOT/java");

        authentications = new HashMap<String, Authentication>();
}

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 in modules/openapi-generator/src/main/resources/Java/libraries/okhttp-gson/ApiClient.mustache around line 147, where the generated ApiClient adds the gzip interceptor. Regenerate the client with the provided Maven plugin configuration and instantiate it with the OAuth parameters; done means initialization completes without UnsupportedOperationException when useGzipFeature is enabled.

Written by the indexing model from the issue text.

Assessment

Tech stack
java, openapi
Domain
tooling
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Stale
Clarity
Clearly specified
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.