From 82516c75d26fe7d41a5faf2680df1fceb4b264ac Mon Sep 17 00:00:00 2001 From: William Cheng Date: Thu, 3 Aug 2023 21:09:00 +0800 Subject: [PATCH] Fix implements in oneof, anyof templates in java okhttp client generator (#16241) * fix implements in oneof, anyof templates in java okhttp * add tests * update samples --- bin/configs/java-okhttp-gson-streaming.yaml | 1 + .../okhttp-gson/anyof_model.mustache | 2 +- .../okhttp-gson/oneof_model.mustache | 2 +- .../src/test/resources/3_0/streaming.yaml | 4 + .../.openapi-generator/FILES | 2 + .../java/okhttp-gson-streaming/README.md | 1 + .../okhttp-gson-streaming/api/openapi.yaml | 4 + .../okhttp-gson-streaming/docs/SimpleOneOf.md | 16 + .../okhttp-gson-streaming/docs/SomeObj.md | 4 + .../java/org/openapitools/client/JSON.java | 1 + .../client/model/SimpleOneOf.java | 280 ++++++++++++++++++ .../openapitools/client/model/SomeObj.java | 5 +- .../client/model/SimpleOneOfTest.java | 33 +++ 13 files changed, 352 insertions(+), 3 deletions(-) create mode 100644 samples/client/others/java/okhttp-gson-streaming/docs/SimpleOneOf.md create mode 100644 samples/client/others/java/okhttp-gson-streaming/src/main/java/org/openapitools/client/model/SimpleOneOf.java create mode 100644 samples/client/others/java/okhttp-gson-streaming/src/test/java/org/openapitools/client/model/SimpleOneOfTest.java diff --git a/bin/configs/java-okhttp-gson-streaming.yaml b/bin/configs/java-okhttp-gson-streaming.yaml index 34da56581a7..9e75c93bac4 100644 --- a/bin/configs/java-okhttp-gson-streaming.yaml +++ b/bin/configs/java-okhttp-gson-streaming.yaml @@ -7,3 +7,4 @@ additionalProperties: artifactId: petstore-okhttp-gson hideGenerationTimestamp: "true" supportStreaming: true + serializableModel: true diff --git a/modules/openapi-generator/src/main/resources/Java/libraries/okhttp-gson/anyof_model.mustache b/modules/openapi-generator/src/main/resources/Java/libraries/okhttp-gson/anyof_model.mustache index 333dfbd81cf..39a942e04a3 100644 --- a/modules/openapi-generator/src/main/resources/Java/libraries/okhttp-gson/anyof_model.mustache +++ b/modules/openapi-generator/src/main/resources/Java/libraries/okhttp-gson/anyof_model.mustache @@ -32,7 +32,7 @@ import com.google.gson.JsonParseException; import {{invokerPackage}}.JSON; {{>additionalModelTypeAnnotations}}{{>generatedAnnotation}}{{>xmlAnnotation}} -public class {{classname}} extends AbstractOpenApiSchema{{#vendorExtensions.x-implements}}, {{{.}}}{{/vendorExtensions.x-implements}} { +public class {{classname}} extends AbstractOpenApiSchema{{#vendorExtensions.x-implements}} implements {{{.}}}{{^-last}}, {{/-last}}{{/vendorExtensions.x-implements}} { private static final Logger log = Logger.getLogger({{classname}}.class.getName()); public static class CustomTypeAdapterFactory implements TypeAdapterFactory { diff --git a/modules/openapi-generator/src/main/resources/Java/libraries/okhttp-gson/oneof_model.mustache b/modules/openapi-generator/src/main/resources/Java/libraries/okhttp-gson/oneof_model.mustache index fc817fbbf0d..6ab9ac9d396 100644 --- a/modules/openapi-generator/src/main/resources/Java/libraries/okhttp-gson/oneof_model.mustache +++ b/modules/openapi-generator/src/main/resources/Java/libraries/okhttp-gson/oneof_model.mustache @@ -34,7 +34,7 @@ import com.google.gson.JsonParseException; import {{invokerPackage}}.JSON; {{>additionalModelTypeAnnotations}}{{>generatedAnnotation}}{{>xmlAnnotation}} -public class {{classname}} extends AbstractOpenApiSchema{{#vendorExtensions.x-implements}}, {{{.}}}{{/vendorExtensions.x-implements}} { +public class {{classname}} extends AbstractOpenApiSchema{{#vendorExtensions.x-implements}} implements {{{.}}}{{^-last}}, {{/-last}}{{/vendorExtensions.x-implements}} { private static final Logger log = Logger.getLogger({{classname}}.class.getName()); public static class CustomTypeAdapterFactory implements TypeAdapterFactory { diff --git a/modules/openapi-generator/src/test/resources/3_0/streaming.yaml b/modules/openapi-generator/src/test/resources/3_0/streaming.yaml index 28c7114f1d5..79ecef76a0e 100644 --- a/modules/openapi-generator/src/test/resources/3_0/streaming.yaml +++ b/modules/openapi-generator/src/test/resources/3_0/streaming.yaml @@ -77,3 +77,7 @@ components: type: boolean type: type: string + SimpleOneOf: + oneOf: + - type: string + - type: integer diff --git a/samples/client/others/java/okhttp-gson-streaming/.openapi-generator/FILES b/samples/client/others/java/okhttp-gson-streaming/.openapi-generator/FILES index 1f0129a4d8c..e86711088b6 100644 --- a/samples/client/others/java/okhttp-gson-streaming/.openapi-generator/FILES +++ b/samples/client/others/java/okhttp-gson-streaming/.openapi-generator/FILES @@ -6,6 +6,7 @@ api/openapi.yaml build.gradle build.sbt docs/PingApi.md +docs/SimpleOneOf.md docs/SomeObj.md git_push.sh gradle.properties @@ -35,4 +36,5 @@ src/main/java/org/openapitools/client/auth/Authentication.java src/main/java/org/openapitools/client/auth/HttpBasicAuth.java src/main/java/org/openapitools/client/auth/HttpBearerAuth.java src/main/java/org/openapitools/client/model/AbstractOpenApiSchema.java +src/main/java/org/openapitools/client/model/SimpleOneOf.java src/main/java/org/openapitools/client/model/SomeObj.java diff --git a/samples/client/others/java/okhttp-gson-streaming/README.md b/samples/client/others/java/okhttp-gson-streaming/README.md index bed7f7484a4..5308de7dee1 100644 --- a/samples/client/others/java/okhttp-gson-streaming/README.md +++ b/samples/client/others/java/okhttp-gson-streaming/README.md @@ -124,6 +124,7 @@ Class | Method | HTTP request | Description ## Documentation for Models + - [SimpleOneOf](docs/SimpleOneOf.md) - [SomeObj](docs/SomeObj.md) diff --git a/samples/client/others/java/okhttp-gson-streaming/api/openapi.yaml b/samples/client/others/java/okhttp-gson-streaming/api/openapi.yaml index 50bf4ce1ec8..b2d364cc6ad 100644 --- a/samples/client/others/java/okhttp-gson-streaming/api/openapi.yaml +++ b/samples/client/others/java/okhttp-gson-streaming/api/openapi.yaml @@ -80,6 +80,10 @@ components: type: type: string type: object + SimpleOneOf: + oneOf: + - type: string + - type: integer getPing_request: properties: name: diff --git a/samples/client/others/java/okhttp-gson-streaming/docs/SimpleOneOf.md b/samples/client/others/java/okhttp-gson-streaming/docs/SimpleOneOf.md new file mode 100644 index 00000000000..aa7149fde93 --- /dev/null +++ b/samples/client/others/java/okhttp-gson-streaming/docs/SimpleOneOf.md @@ -0,0 +1,16 @@ + + +# SimpleOneOf + + +## Properties + +| Name | Type | Description | Notes | +|------------ | ------------- | ------------- | -------------| + + +## Implemented Interfaces + +* Serializable + + diff --git a/samples/client/others/java/okhttp-gson-streaming/docs/SomeObj.md b/samples/client/others/java/okhttp-gson-streaming/docs/SomeObj.md index 49a0726b5a3..70bf3335f7b 100644 --- a/samples/client/others/java/okhttp-gson-streaming/docs/SomeObj.md +++ b/samples/client/others/java/okhttp-gson-streaming/docs/SomeObj.md @@ -22,4 +22,8 @@ | SOMEOBJIDENTIFIER | "SomeObjIdentifier" | +## Implemented Interfaces + +* Serializable + diff --git a/samples/client/others/java/okhttp-gson-streaming/src/main/java/org/openapitools/client/JSON.java b/samples/client/others/java/okhttp-gson-streaming/src/main/java/org/openapitools/client/JSON.java index 1e547ada431..417a0011024 100644 --- a/samples/client/others/java/okhttp-gson-streaming/src/main/java/org/openapitools/client/JSON.java +++ b/samples/client/others/java/okhttp-gson-streaming/src/main/java/org/openapitools/client/JSON.java @@ -93,6 +93,7 @@ public class JSON { gsonBuilder.registerTypeAdapter(OffsetDateTime.class, offsetDateTimeTypeAdapter); gsonBuilder.registerTypeAdapter(LocalDate.class, localDateTypeAdapter); gsonBuilder.registerTypeAdapter(byte[].class, byteArrayAdapter); + gsonBuilder.registerTypeAdapterFactory(new org.openapitools.client.model.SimpleOneOf.CustomTypeAdapterFactory()); gsonBuilder.registerTypeAdapterFactory(new org.openapitools.client.model.SomeObj.CustomTypeAdapterFactory()); gson = gsonBuilder.create(); } diff --git a/samples/client/others/java/okhttp-gson-streaming/src/main/java/org/openapitools/client/model/SimpleOneOf.java b/samples/client/others/java/okhttp-gson-streaming/src/main/java/org/openapitools/client/model/SimpleOneOf.java new file mode 100644 index 00000000000..36de4fc0b02 --- /dev/null +++ b/samples/client/others/java/okhttp-gson-streaming/src/main/java/org/openapitools/client/model/SimpleOneOf.java @@ -0,0 +1,280 @@ +/* + * ping some object + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 1.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +package org.openapitools.client.model; + +import java.util.Objects; +import java.util.Arrays; +import java.io.Serializable; + + + +import java.io.IOException; +import java.lang.reflect.Type; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonParseException; +import com.google.gson.TypeAdapter; +import com.google.gson.TypeAdapterFactory; +import com.google.gson.reflect.TypeToken; +import com.google.gson.JsonPrimitive; +import com.google.gson.annotations.JsonAdapter; +import com.google.gson.annotations.SerializedName; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonSerializationContext; +import com.google.gson.JsonSerializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonArray; +import com.google.gson.JsonParseException; + +import org.openapitools.client.JSON; + +@javax.annotation.Generated(value = "org.openapitools.codegen.languages.JavaClientCodegen") +public class SimpleOneOf extends AbstractOpenApiSchema implements Serializable { + private static final Logger log = Logger.getLogger(SimpleOneOf.class.getName()); + + public static class CustomTypeAdapterFactory implements TypeAdapterFactory { + @SuppressWarnings("unchecked") + @Override + public TypeAdapter create(Gson gson, TypeToken type) { + if (!SimpleOneOf.class.isAssignableFrom(type.getRawType())) { + return null; // this class only serializes 'SimpleOneOf' and its subtypes + } + final TypeAdapter elementAdapter = gson.getAdapter(JsonElement.class); + final TypeAdapter adapterString = gson.getDelegateAdapter(this, TypeToken.get(String.class)); + final TypeAdapter adapterInteger = gson.getDelegateAdapter(this, TypeToken.get(Integer.class)); + + return (TypeAdapter) new TypeAdapter() { + @Override + public void write(JsonWriter out, SimpleOneOf value) throws IOException { + if (value == null || value.getActualInstance() == null) { + elementAdapter.write(out, null); + return; + } + + // check if the actual instance is of the type `String` + if (value.getActualInstance() instanceof String) { + JsonPrimitive primitive = adapterString.toJsonTree((String)value.getActualInstance()).getAsJsonPrimitive(); + elementAdapter.write(out, primitive); + return; + } + // check if the actual instance is of the type `Integer` + if (value.getActualInstance() instanceof Integer) { + JsonPrimitive primitive = adapterInteger.toJsonTree((Integer)value.getActualInstance()).getAsJsonPrimitive(); + elementAdapter.write(out, primitive); + return; + } + throw new IOException("Failed to serialize as the type doesn't match oneOf schemas: Integer, String"); + } + + @Override + public SimpleOneOf read(JsonReader in) throws IOException { + Object deserialized = null; + JsonElement jsonElement = elementAdapter.read(in); + + int match = 0; + ArrayList errorMessages = new ArrayList<>(); + TypeAdapter actualAdapter = elementAdapter; + + // deserialize String + try { + // validate the JSON object to see if any exception is thrown + if(!jsonElement.getAsJsonPrimitive().isString()) { + throw new IllegalArgumentException(String.format("Expected json element to be of type String in the JSON string but got `%s`", jsonElement.toString())); + } + actualAdapter = adapterString; + match++; + log.log(Level.FINER, "Input data matches schema 'String'"); + } catch (Exception e) { + // deserialization failed, continue + errorMessages.add(String.format("Deserialization for String failed with `%s`.", e.getMessage())); + log.log(Level.FINER, "Input data does not match schema 'String'", e); + } + // deserialize Integer + try { + // validate the JSON object to see if any exception is thrown + if(!jsonElement.getAsJsonPrimitive().isNumber()) { + throw new IllegalArgumentException(String.format("Expected json element to be of type Number in the JSON string but got `%s`", jsonElement.toString())); + } + actualAdapter = adapterInteger; + match++; + log.log(Level.FINER, "Input data matches schema 'Integer'"); + } catch (Exception e) { + // deserialization failed, continue + errorMessages.add(String.format("Deserialization for Integer failed with `%s`.", e.getMessage())); + log.log(Level.FINER, "Input data does not match schema 'Integer'", e); + } + + if (match == 1) { + SimpleOneOf ret = new SimpleOneOf(); + ret.setActualInstance(actualAdapter.fromJsonTree(jsonElement)); + return ret; + } + + throw new IOException(String.format("Failed deserialization for SimpleOneOf: %d classes match result, expected 1. Detailed failure message for oneOf schemas: %s. JSON: %s", match, errorMessages, jsonElement.toString())); + } + }.nullSafe(); + } + } + + // store a list of schema names defined in oneOf + public static final Map> schemas = new HashMap>(); + + public SimpleOneOf() { + super("oneOf", Boolean.FALSE); + } + + public SimpleOneOf(Integer o) { + super("oneOf", Boolean.FALSE); + setActualInstance(o); + } + + public SimpleOneOf(String o) { + super("oneOf", Boolean.FALSE); + setActualInstance(o); + } + + static { + schemas.put("String", String.class); + schemas.put("Integer", Integer.class); + } + + @Override + public Map> getSchemas() { + return SimpleOneOf.schemas; + } + + /** + * Set the instance that matches the oneOf child schema, check + * the instance parameter is valid against the oneOf child schemas: + * Integer, String + * + * It could be an instance of the 'oneOf' schemas. + * The oneOf child schemas may themselves be a composed schema (allOf, anyOf, oneOf). + */ + @Override + public void setActualInstance(Object instance) { + if (instance instanceof String) { + super.setActualInstance(instance); + return; + } + + if (instance instanceof Integer) { + super.setActualInstance(instance); + return; + } + + throw new RuntimeException("Invalid instance type. Must be Integer, String"); + } + + /** + * Get the actual instance, which can be the following: + * Integer, String + * + * @return The actual instance (Integer, String) + */ + @Override + public Object getActualInstance() { + return super.getActualInstance(); + } + + /** + * Get the actual instance of `String`. If the actual instance is not `String`, + * the ClassCastException will be thrown. + * + * @return The actual instance of `String` + * @throws ClassCastException if the instance is not `String` + */ + public String getString() throws ClassCastException { + return (String)super.getActualInstance(); + } + /** + * Get the actual instance of `Integer`. If the actual instance is not `Integer`, + * the ClassCastException will be thrown. + * + * @return The actual instance of `Integer` + * @throws ClassCastException if the instance is not `Integer` + */ + public Integer getInteger() throws ClassCastException { + return (Integer)super.getActualInstance(); + } + + /** + * Validates the JSON Element and throws an exception if issues found + * + * @param jsonElement JSON Element + * @throws IOException if the JSON Element is invalid with respect to SimpleOneOf + */ + public static void validateJsonElement(JsonElement jsonElement) throws IOException { + // validate oneOf schemas one by one + int validCount = 0; + ArrayList errorMessages = new ArrayList<>(); + // validate the json string with String + try { + if(!jsonElement.getAsJsonPrimitive().isString()) { + throw new IllegalArgumentException(String.format("Expected json element to be of type String in the JSON string but got `%s`", jsonElement.toString())); + } + validCount++; + } catch (Exception e) { + errorMessages.add(String.format("Deserialization for String failed with `%s`.", e.getMessage())); + // continue to the next one + } + // validate the json string with Integer + try { + if(!jsonElement.getAsJsonPrimitive().isNumber()) { + throw new IllegalArgumentException(String.format("Expected json element to be of type Number in the JSON string but got `%s`", jsonElement.toString())); + } + validCount++; + } catch (Exception e) { + errorMessages.add(String.format("Deserialization for Integer failed with `%s`.", e.getMessage())); + // continue to the next one + } + if (validCount != 1) { + throw new IOException(String.format("The JSON string is invalid for SimpleOneOf with oneOf schemas: Integer, String. %d class(es) match the result, expected 1. Detailed failure message for oneOf schemas: %s. JSON: %s", validCount, errorMessages, jsonElement.toString())); + } + } + + /** + * Create an instance of SimpleOneOf given an JSON string + * + * @param jsonString JSON string + * @return An instance of SimpleOneOf + * @throws IOException if the JSON string is invalid with respect to SimpleOneOf + */ + public static SimpleOneOf fromJson(String jsonString) throws IOException { + return JSON.getGson().fromJson(jsonString, SimpleOneOf.class); + } + + /** + * Convert an instance of SimpleOneOf to an JSON string + * + * @return JSON string + */ + public String toJson() { + return JSON.getGson().toJson(this); + } +} + diff --git a/samples/client/others/java/okhttp-gson-streaming/src/main/java/org/openapitools/client/model/SomeObj.java b/samples/client/others/java/okhttp-gson-streaming/src/main/java/org/openapitools/client/model/SomeObj.java index d52b087d3e9..b55567f3d79 100644 --- a/samples/client/others/java/okhttp-gson-streaming/src/main/java/org/openapitools/client/model/SomeObj.java +++ b/samples/client/others/java/okhttp-gson-streaming/src/main/java/org/openapitools/client/model/SomeObj.java @@ -21,6 +21,7 @@ import com.google.gson.annotations.SerializedName; import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonWriter; import java.io.IOException; +import java.io.Serializable; import com.google.gson.Gson; import com.google.gson.GsonBuilder; @@ -51,7 +52,9 @@ import org.openapitools.client.JSON; * SomeObj */ @javax.annotation.Generated(value = "org.openapitools.codegen.languages.JavaClientCodegen") -public class SomeObj { +public class SomeObj implements Serializable { + private static final long serialVersionUID = 1L; + /** * Gets or Sets $type */ diff --git a/samples/client/others/java/okhttp-gson-streaming/src/test/java/org/openapitools/client/model/SimpleOneOfTest.java b/samples/client/others/java/okhttp-gson-streaming/src/test/java/org/openapitools/client/model/SimpleOneOfTest.java new file mode 100644 index 00000000000..b1d2d8042a4 --- /dev/null +++ b/samples/client/others/java/okhttp-gson-streaming/src/test/java/org/openapitools/client/model/SimpleOneOfTest.java @@ -0,0 +1,33 @@ +/* + * ping some object + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 1.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +package org.openapitools.client.model; + +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +/** + * Model tests for SimpleOneOf + */ +public class SimpleOneOfTest { + private final SimpleOneOf model = new SimpleOneOf(); + + /** + * Model tests for SimpleOneOf + */ + @Test + public void testSimpleOneOf() { + // TODO: test SimpleOneOf + } + +}