Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[BUG] [Java Client]OneOf inside property of a schema generates broken class (OneOfstringstring) #5730

Closed
staaouat opened this issue Mar 27, 2020 · 22 comments

Comments

@staaouat
Copy link

Description

I tried to generate java client code of a model containing oneOf, the code is generated but the generated class IObject (see the example) uses the class OneOfstringstring which is not generaated.

openapi-generator version

openapi-generator-cli-4.3.0-20200326.174439-234

OpenAPI declaration file content or url

The complete example :

{
  "openapi" : "3.0.0",
  "info" : {
    "title" : "Management API",
    "contact" : {
      "name" : "RD Development",
      "email" : "rd@test.com"
    },
    "version" : "0.1",
    "x-restgeninfo" : { }
  },
  "servers" : [ {
    "url" : "/"
  } ],
  "tags" : [ {
    "name" : "ip"
  } ],
  "paths" : {
    "/ip" : {
      "get" : {
        "tags" : [ "ip" ],
        "summary" : "List IP objects",
        "description" : "Lists all IP Addresses within the Database\n",
        "operationId" : "ipList",
        "parameters" : [ {
          "$ref" : "#/components/parameters/x-page-token"
        } ],
        "responses" : {
          "200" : {
            "description" : "List of Matching IP objects",
            "headers" : {
              "X-PAGE-MARKER" : {
                "$ref" : "#/components/schemas/HeaderPageMarker"
              }
            },
            "content" : {
              "application/json" : {
                "schema" : {
                  "type" : "array",
                  "items" : {
                    "$ref" : "#/components/schemas/IPObject"
                  }
                }
              }
            }
          },
          "401" : {
            "$ref" : "#/components/responses/StatusResponse"
          },
          "403" : {
            "$ref" : "#/components/responses/StatusResponse"
          }
        },
        "x-accepts" : "application/json"
      },
      "put" : {
        "tags" : [ "ip" ],
        "summary" : "Set an IP object",
        "description" : "Create/Replace an IP Object",
        "operationId" : "ip_address_put",
        "requestBody" : {
          "description" : "IP Object",
          "content" : {
            "application/json" : {
              "schema" : {
                "$ref" : "#/components/schemas/IPObject"
              }
            }
          },
          "required" : true
        },
        "responses" : {
          "200" : {
            "$ref" : "#/components/responses/StatusResponse"
          },
          "400" : {
            "$ref" : "#/components/responses/StatusResponse"
          },
          "401" : {
            "$ref" : "#/components/responses/StatusResponse"
          },
          "403" : {
            "$ref" : "#/components/responses/StatusResponse"
          }
        },
        "x-contentType" : "application/json",
        "x-accepts" : "application/json"
      }
    },
    "/ip/{address}" : {
      "get" : {
        "tags" : [ "ip" ],
        "summary" : "Get an IP object",
        "description" : "Get an IP object by its address",
        "operationId" : "ip_address_get",
        "parameters" : [ {
          "$ref" : "#/components/parameters/address"
        } ],
        "responses" : {
          "200" : {
            "description" : "Matching IP object",
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/IPObject"
                }
              }
            }
          },
          "404" : {
            "$ref" : "#/components/responses/StatusResponse"
          },
          "401" : {
            "$ref" : "#/components/responses/StatusResponse"
          },
          "403" : {
            "$ref" : "#/components/responses/StatusResponse"
          }
        },
        "x-accepts" : "application/json"
      },
      "delete" : {
        "tags" : [ "ip" ],
        "summary" : "Delete an IP object",
        "operationId" : "ip_address_delete",
        "parameters" : [ {
          "$ref" : "#/components/parameters/address"
        } ],
        "responses" : {
          "200" : {
            "$ref" : "#/components/responses/StatusResponse"
          },
          "404" : {
            "$ref" : "#/components/responses/StatusResponse"
          },
          "401" : {
            "$ref" : "#/components/responses/StatusResponse"
          },
          "403" : {
            "$ref" : "#/components/responses/StatusResponse"
          }
        },
        "x-accepts" : "application/json"
      },
      "parameters" : [ {
        "$ref" : "#/components/parameters/address"
      } ]
    }
  },
  "components" : {
    "schemas" : {
      "HeaderPageMarker" : {
        "type" : "string"
      },
      "StatusObject" : {
        "required" : [ "message", "status" ],
        "type" : "object",
        "properties" : {
          "status" : {
            "type" : "string",
            "enum" : [ "success", "error" ]
          },
          "message" : {
            "type" : "string"
          }
        },
        "additionalProperties" : false
      },
      "IPv6Address" : {
        "type" : "string",
        "format" : "IPv6"
      },
      "IPv4Address" : {
        "type" : "string",
        "format" : "IPv4"
      },
      "IPObject" : {
        "required" : [ "address" ],
        "type" : "object",
        "properties" : {
          "address" : {
            "oneOf" : [ {
              "$ref" : "#/components/schemas/IPv4Address"
            }, {
              "$ref" : "#/components/schemas/IPv6Address"
            } ]
          },
          "start_time" : {
            "type" : "string",
            "format" : "date-time"
          },
          "end_time" : {
            "type" : "string",
            "format" : "date-time"
          },
          "action" : {
            "type" : "string",
            "enum" : [ "blackhole", "redirect" ]
          },
          "redirect_url" : {
            "type" : "string"
          },
          "notes" : {
            "type" : "string"
          },
          "created_at" : {
            "type" : "string",
            "format" : "date-time",
            "readOnly" : true
          },
          "updated_at" : {
            "type" : "string",
            "format" : "date-time",
            "readOnly" : true
          }
        },
        "additionalProperties" : false,
        "example" : {
          "start_time" : "2000-01-23T04:56:07.000+00:00",
          "address" : "",
          "notes" : "notes",
          "updated_at" : "2000-01-23T04:56:07.000+00:00",
          "end_time" : "2000-01-23T04:56:07.000+00:00",
          "action" : "blackhole",
          "created_at" : "2000-01-23T04:56:07.000+00:00",
          "redirect_url" : "redirect_url"
        }
      }
    },
    "responses" : {
      "IPObjectResponse" : {
        "description" : "IP object",
        "content" : {
          "application/json" : {
            "schema" : {
              "$ref" : "#/components/schemas/IPObject"
            }
          }
        }
      },
      "StatusResponse" : {
        "description" : "Generic Status Message Response",
        "content" : {
          "application/json" : {
            "schema" : {
              "$ref" : "#/components/schemas/StatusObject"
            }
          }
        }
      }
    },
    "parameters" : {
      "x-page-token" : {
        "name" : "x-page-token",
        "in" : "header",
        "description" : "Pagination Token, for paged responses a token will be returned,\nThis should be provided on the next request to receive the next page.\n",
        "required" : false,
        "style" : "simple",
        "explode" : false,
        "schema" : {
          "type" : "string"
        }
      },
      "domainname" : {
        "name" : "domainname",
        "in" : "path",
        "description" : "Domain Name",
        "required" : true,
        "style" : "simple",
        "explode" : false,
        "schema" : {
          "type" : "string"
        },
        "example" : "google.com"
      },
      "address" : {
        "name" : "address",
        "in" : "path",
        "description" : "IP Address",
        "required" : true,
        "style" : "simple",
        "explode" : false,
        "schema" : {
          "oneOf" : [ {
            "$ref" : "#/components/schemas/IPv6Address"
          }, {
            "$ref" : "#/components/schemas/IPv4Address"
          } ]
        },
        "example" : "1.2.3.4"
      },
      "groupname" : {
        "name" : "groupname",
        "in" : "path",
        "description" : "Group Name",
        "required" : true,
        "style" : "simple",
        "explode" : false,
        "schema" : {
          "type" : "string"
        },
        "example" : "block_group_a"
      }
    },
    "securitySchemes" : { }
  }
}
Command line used for generation

I'm using OpenApi studio

Steps to reproduce

Generate the above example with java client.

Related issues/PRs

#5382

@jfeltesse-mdsol
Copy link
Contributor

This has been fixed in master through #5400 and #5382 should be closed (not an admin so can't do).

Note the oneOf support for java clients is only for the flavors that use Jackson.

Maybe have a try with the master branch and confirm whether it's fixed for you?

@staaouat
Copy link
Author

The same problem generating using CMD:
java -jar openapi-generator-cli-4.3.0-20200326.174439-234.jar generate -i exmple.json -g java -o c:\Users\ta\gen

@jfeltesse-mdsol
Copy link
Contributor

Fix on master only works with Jackson clients, the default is not Jackson. You need to set the library, for instance --library native. See https://github.com/OpenAPITools/openapi-generator/blob/master/docs/generators/java.md

@staaouat
Copy link
Author

Thanks, it works

@jfeltesse-mdsol
Copy link
Contributor

Cool. Can you close the issue then?

@staaouat staaouat reopened this Mar 30, 2020
@staaouat
Copy link
Author

staaouat commented Mar 30, 2020

The class OneOfstringstring?? is not generated on path class IpApi:

import org.openapitools.client.model.OneOfstringstring;

@jorgerod
Copy link
Contributor

jorgerod commented Apr 16, 2020

Same issue. It's not work for me.

openapi: "3.0.1"
info:
  version: "1.0.0"
  title: "oneOf Test"
paths:
  /pets:
    post:
      requestBody:
        content:
          application/json:
            schema:
              oneOf:
                - $ref: '#/components/schemas/Cat'
                - $ref: '#/components/schemas/Dog'
      responses:
        '200':
          description: Updated
components:
  schemas:
    Dog:
      type: object
      properties:
        bark:
          type: boolean
        breed:
          type: string
          enum: [Dingo, Husky, Retriever, Shepherd]
    Cat:
      type: object
      properties:
        hunts:
          type: boolean
        age:
          type: integer

Generated Java

package org.openapitools.client.api;

import org.openapitools.client.ApiClient;

import org.openapitools.client.model.PetsPOST;
import org.openapitools.client.model.UNKNOWN_BASE_TYPE;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.util.UriComponentsBuilder;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.core.io.FileSystemResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;

@javax.annotation.Generated(value = "org.openapitools.codegen.languages.JavaClientCodegen", date = "2020-04-16T18:33:15.038+02:00[Europe/Paris]")
@Component("org.openapitools.client.api.DefaultApi")
public class DefaultApi {
    private ApiClient apiClient;

    public DefaultApi() {
        this(new ApiClient());
    }

    @Autowired
    public DefaultApi(ApiClient apiClient) {
        this.apiClient = apiClient;
    }

    public ApiClient getApiClient() {
        return apiClient;
    }

    public void setApiClient(ApiClient apiClient) {
        this.apiClient = apiClient;
    }

    /**
     * 
     * 
     * <p><b>200</b> - Updated
     * @param UNKNOWN_BASE_TYPE  (optional)
     * @throws RestClientException if an error occurs while attempting to invoke the API
     */
    public void petsPost(UNKNOWN_BASE_TYPE UNKNOWN_BASE_TYPE) throws RestClientException {
        petsPostWithHttpInfo(UNKNOWN_BASE_TYPE);
    }

    /**
     * 
     * 
     * <p><b>200</b> - Updated
     * @param UNKNOWN_BASE_TYPE  (optional)
     * @return ResponseEntity&lt;Void&gt;
     * @throws RestClientException if an error occurs while attempting to invoke the API
     */
    public ResponseEntity<Void> petsPostWithHttpInfo(UNKNOWN_BASE_TYPE UNKNOWN_BASE_TYPE) throws RestClientException {
        Object postBody = UNKNOWN_BASE_TYPE;
        
        String path = apiClient.expandPath("/pets", Collections.<String, Object>emptyMap());

        final MultiValueMap<String, String> queryParams = new LinkedMultiValueMap<String, String>();
        final HttpHeaders headerParams = new HttpHeaders();
        final MultiValueMap<String, String> cookieParams = new LinkedMultiValueMap<String, String>();
        final MultiValueMap formParams = new LinkedMultiValueMap();

        final String[] accepts = { };
        final List<MediaType> accept = apiClient.selectHeaderAccept(accepts);
        final String[] contentTypes = { 
            "application/json"
        };
        final MediaType contentType = apiClient.selectHeaderContentType(contentTypes);

        String[] authNames = new String[] {  };

        ParameterizedTypeReference<Void> returnType = new ParameterizedTypeReference<Void>() {};
        return apiClient.invokeAPI(path, HttpMethod.POST, queryParams, postBody, headerParams, cookieParams, formParams, accept, contentType, authNames, returnType);
    }
}

Step to reproduce
Run

openapi-generator generate -g java -i ./openapi-rest.yml -o ./generated_oneof_client --library resttemplate

Create method with parameter UNKNOWN_BASE_TYPE and this class not exists

@ngp-star
Copy link
Contributor

ngp-star commented Apr 20, 2020

Hi All,

Even I am facing the same issue when open API spec YAML file is given as input, implementation logic created by using open API generator utility but when we try to build the implementation using mvn clean package we are facing the same following issue.
error: cannot find symbol
symbol: class OneOfstringstring
location: package org.openapitools.client.model

we used @jfeltesse-mdsol comment using the flag --library native while generating implementation login for open API spec.

but when we try to build using command mvn clean package , again different issues as follows.
cannot find symbol
[ERROR] symbol: class Clone200
[ERROR] location: package org.openapitools.client.model

is there any other way is there for this?
support appreciated

Regards
Naveen.

@gravelld
Copy link

This has been fixed in master through #5400 and #5382 should be closed (not an admin so can't do).

Note the oneOf support for java clients is only for the flavors that use Jackson.

Is it fair to say it's fixed then? Fixed if you're using Jackson.

The current fix I'm trying for the generated code is simply replacing the OneOf... with a common superclass - I'm guessing this won't work for all cases.

@jfeltesse-mdsol
Copy link
Contributor

jfeltesse-mdsol commented Apr 22, 2020

@ngp-star you've probably run into #5903

@ngp-star
Copy link
Contributor

@jfeltesse-mdsol what could be the fix for this?

@staaouat
Copy link
Author

Just tested with openapi-generator-cli-5.0.0-20200422.090118-38.jar but the problem is not solved.

@gravelld
Copy link

gravelld commented Apr 23, 2020

@ngp-star It's a big pain but at the moment I'm just replacing use of the interface with a common superclass, not sure if this works for you:

$ find build/open-api-generated/src/main/java/ -type f -name "*.java" -exec sed -i 's/OneOfAllTheTypes/CommonSuperclass/g' {} \;

@staaouat
Copy link
Author

staaouat commented Apr 24, 2020

This work around works for me.

@ngp-star
Copy link
Contributor

@gravelld like should we build the open API source code? then use your command for replacement?

@gravelld
Copy link

I would just add it to whatever build script you have.

@ngp-star
Copy link
Contributor

@gravelld I don't have build script I installed open API generator CLI tool from that I ma generating implementations

@gravelld
Copy link

Then write a script (bash, batch, whatever) to run the workaround after the classes are generated. Or, just remember to do it every time.

@ngp-star
Copy link
Contributor

@gravelld
let me try

@ngp-star
Copy link
Contributor

ngp-star commented Apr 30, 2020

@gravelld
tried this command find src/main/java/org/openapitools/client/api/ -type f -name "*.java" -exec sed -i 's/OneOfAllTheTypes/CommonSuperclass/g' {} ;

getting following error
sed: 1: "src/main/java/org/opena ...": bad flag in substitute command: 'V'

Moreover, I don't have any class or interface named OneOfAllTheTypes in my generated implementation.

@gravelld
Copy link

Sorry - can't really help because it depends on your OS and this is getting off topic. You normally wouldn't have just ; at the end of an -exec for starters, you need \;. Also, you need to specify the text you are finding - OneOfAllTheTypes and replacing CommonSuperclass.

@wing328
Copy link
Member

wing328 commented Jul 5, 2020

Please give the latest master a try with the jersey2 library which has better support for oneOf and anyOf.

@wing328 wing328 closed this as completed Nov 9, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants