swagger-api / swagger-api/swagger-codegen

[JAVA + okhttp] additionalProperties of type array not properly handled

Open
#9,425 0 comments 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

An additionalProperties of type array is not properly handled by the JavaClientCodegen with okhttp + GSON. See this example:

swagger: '2.0'
info:
  version: 0.0.0
  title: Foo
  
paths:
  /foo1:
    get:
      responses:
        200:
          description: Foo
          schema:
            type: object
            additionalProperties:
              type: array
              items:
                $ref: '#/definitions/Foo'

definitions:
  Foo:
    type: object
    properties:
      foo:
        type: string

This causes the code generator to produce the following class which has no additional field:

public class FooResponse extends HashMap<String, List> {

Note that the type of values in the hashmap is a List where it should have been a List<Foo>. Because of that, GSON doesn't deserialise the values to Foo objects. Instead, it gives just a LinkedHashMap<String, Object>.

If I modify the spec above and create a wrapper definition such as:

swagger: '2.0'
info:
  version: 0.0.0
  title: Foo
  
paths:      
  /foo2:
    get:
      responses:
        200:
          description: Foo
          schema:
            $ref: '#/definitions/FooResponse'

definitions:
  Foo:
    type: object
    properties:
      foo:
        type: string

  FooResponse:
    type: object
    additionalProperties:
      type: array
      items:
        $ref: '#/definitions/Foo'

The generated DefaultApi class now lacks the import of Foo:

package io.swagger.client.api;

import io.swagger.client.ApiCallback;
import io.swagger.client.ApiClient;
import io.swagger.client.ApiException;
import io.swagger.client.ApiResponse;
import io.swagger.client.Configuration;
import io.swagger.client.Pair;
import io.swagger.client.ProgressRequestBody;
import io.swagger.client.ProgressResponseBody;

import com.google.gson.reflect.TypeToken;


import java.io.IOException;


import io.swagger.client.model.FooResponse;

import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class DefaultApi {

   // omitted

    /**
     * 
     * 
     * @return Map&lt;String, List&lt;Foo&gt;&gt;
     * @throws ApiException If fail to call the API, e.g. server error or cannot deserialize the response body
     */
    public Map<String, List<Foo>> foo1Get() throws ApiException {
        ApiResponse<Map<String, List<Foo>>> resp = foo1GetWithHttpInfo();
        return resp.getData();
    }

   // omitted
}
Swagger-codegen version

2.4.5 (latest)

Swagger declaration file content or url

See above.

Command line used for generation

Copy the YAML specs above and paste on https://editor.swagger.io . Then go "Generate Client" > "Java". This is going to produce a Java client that uses the OkHTTP and GSON libraries.

Steps to reproduce

Explained above

Related issues/PRs

Not quite sure if this one is related: https://github.com/swagger-api/swagger-codegen/issues/6884

Suggest a fix/enhancement

Adding import {{modelPackage}}.*; to Java/libraries/okhttp-gson/api.mustache "fixes" the problem with the import.

I managed to get FooResponse to be generated as a HashMap<String, ArrayList<Foo>> by making the following change (hack):

diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/DefaultCodegen.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/DefaultCodegen.java
index 096926470..e43fd30ec 100644
--- a/modules/swagger-codegen/src/main/java/io/swagger/codegen/DefaultCodegen.java
+++ b/modules/swagger-codegen/src/main/java/io/swagger/codegen/DefaultCodegen.java
@@ -994,8 +994,15 @@ public class DefaultCodegen {
                 LOGGER.error("No Type defined for Additional Property " + additionalProperties2 + "\n" //
                         + "\tIn Property: " + p);
             }
-            String inner = getSwaggerType(additionalProperties2);
-            return instantiationTypes.get("map") + "<String, " + inner + ">";
+
+            String additionalPropInstType = toInstantiationType(additionalProperties2);
+
+            if (additionalPropInstType != null) {
+                return instantiationTypes.get("map") + "<String, " + additionalPropInstType + ">";
+            } else {
+                String inner = getSwaggerType(additionalProperties2);
+                return instantiationTypes.get("map") + "<String, " + inner + ">";
+            }
         } else if (p instanceof ArrayProperty) {
             ArrayProperty ap = (ArrayProperty) p;
             String inner = getSwaggerType(ap.getItems());

All tests pass with both changes, but given that I'm not familiar with the codebase I don't feel confident to open a PR for this.

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/swagger-codegen/src/main/java/io/swagger/codegen/DefaultCodegen.java and the Java/libraries/okhttp-gson/api.mustache template, then run the supplied Swagger specs through Java okhttp-gson generation. Done means array-valued additionalProperties preserve the Foo generic type and generated APIs include the required model import.

Written by the indexing model from the issue text.

Assessment

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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.