Skip to content

Commit 64c763b

Browse files
authored
[OAS 3.1] Fix null type check in normalizer (#17609)
* fix null type check in normalizer * add back ref check * add files
1 parent 51dbd32 commit 64c763b

9 files changed

Lines changed: 290 additions & 0 deletions

File tree

modules/openapi-generator/src/main/java/org/openapitools/codegen/OpenAPINormalizer.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -743,6 +743,16 @@ private boolean isNullTypeSchema(Schema schema) {
743743
return true;
744744
}
745745

746+
if (schema.getTypes() != null && !schema.getTypes().isEmpty()) {
747+
// 3.1 spec
748+
if (schema.getTypes().size() ==1) { // 1 type only
749+
String type = (String) schema.getTypes().iterator().next();
750+
return type == null || "null".equals(type);
751+
} else { // more than 1 type so must not be just null
752+
return false;
753+
}
754+
}
755+
746756
if ((schema.getType() == null || schema.getType().equals("null")) && schema.get$ref() == null) {
747757
return true;
748758
}

modules/openapi-generator/src/test/resources/3_1/java/petstore.yaml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -889,3 +889,11 @@ components:
889889
properties: {}
890890
type: object
891891
title: HTTPValidationError
892+
AnyOfArray:
893+
anyOf:
894+
- type: array
895+
items:
896+
type: string
897+
- type: array
898+
items:
899+
type: integer

samples/client/petstore/java/okhttp-gson-3.1/.openapi-generator/FILES

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ api/openapi.yaml
66
build.gradle
77
build.sbt
88
docs/Animal.md
9+
docs/AnyOfArray.md
910
docs/AnyTypeTest.md
1011
docs/Cat.md
1112
docs/Category.md
@@ -57,6 +58,7 @@ src/main/java/org/openapitools/client/auth/OAuthOkHttpClient.java
5758
src/main/java/org/openapitools/client/auth/RetryingOAuth.java
5859
src/main/java/org/openapitools/client/model/AbstractOpenApiSchema.java
5960
src/main/java/org/openapitools/client/model/Animal.java
61+
src/main/java/org/openapitools/client/model/AnyOfArray.java
6062
src/main/java/org/openapitools/client/model/AnyTypeTest.java
6163
src/main/java/org/openapitools/client/model/Cat.java
6264
src/main/java/org/openapitools/client/model/Category.java

samples/client/petstore/java/okhttp-gson-3.1/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,7 @@ Class | Method | HTTP request | Description
143143
## Documentation for Models
144144

145145
- [Animal](docs/Animal.md)
146+
- [AnyOfArray](docs/AnyOfArray.md)
146147
- [AnyTypeTest](docs/AnyTypeTest.md)
147148
- [Cat](docs/Cat.md)
148149
- [Category](docs/Category.md)

samples/client/petstore/java/okhttp-gson-3.1/api/openapi.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -956,6 +956,12 @@ components:
956956
properties: {}
957957
title: HTTPValidationError
958958
type: object
959+
AnyOfArray:
960+
anyOf:
961+
- items:
962+
type: string
963+
- items:
964+
type: integer
959965
updatePetWithForm_request:
960966
properties:
961967
name:
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
2+
3+
# AnyOfArray
4+
5+
6+
## Properties
7+
8+
| Name | Type | Description | Notes |
9+
|------------ | ------------- | ------------- | -------------|
10+
11+
12+

samples/client/petstore/java/okhttp-gson-3.1/src/main/java/org/openapitools/client/JSON.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ private static Class getClassByDiscriminator(Map classByDiscriminatorValue, Stri
129129
gsonBuilder.registerTypeAdapter(OffsetDateTime.class, offsetDateTimeTypeAdapter);
130130
gsonBuilder.registerTypeAdapter(LocalDate.class, localDateTypeAdapter);
131131
gsonBuilder.registerTypeAdapter(byte[].class, byteArrayAdapter);
132+
gsonBuilder.registerTypeAdapterFactory(new org.openapitools.client.model.AnyOfArray.CustomTypeAdapterFactory());
132133
gsonBuilder.registerTypeAdapterFactory(new org.openapitools.client.model.AnyTypeTest.CustomTypeAdapterFactory());
133134
gsonBuilder.registerTypeAdapterFactory(new org.openapitools.client.model.Cat.CustomTypeAdapterFactory());
134135
gsonBuilder.registerTypeAdapterFactory(new org.openapitools.client.model.Category.CustomTypeAdapterFactory());
Lines changed: 217 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
1+
/*
2+
* OpenAPI Petstore
3+
* This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters.
4+
*
5+
* The version of the OpenAPI document: 1.0.0
6+
*
7+
*
8+
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
9+
* https://openapi-generator.tech
10+
* Do not edit the class manually.
11+
*/
12+
13+
14+
package org.openapitools.client.model;
15+
16+
import java.util.Objects;
17+
18+
19+
20+
import java.io.IOException;
21+
import java.lang.reflect.Type;
22+
import java.util.logging.Level;
23+
import java.util.logging.Logger;
24+
import java.util.ArrayList;
25+
import java.util.Collections;
26+
import java.util.HashSet;
27+
import java.util.HashMap;
28+
import java.util.List;
29+
import java.util.Map;
30+
31+
import com.google.gson.Gson;
32+
import com.google.gson.GsonBuilder;
33+
import com.google.gson.JsonParseException;
34+
import com.google.gson.TypeAdapter;
35+
import com.google.gson.TypeAdapterFactory;
36+
import com.google.gson.reflect.TypeToken;
37+
import com.google.gson.JsonPrimitive;
38+
import com.google.gson.annotations.JsonAdapter;
39+
import com.google.gson.annotations.SerializedName;
40+
import com.google.gson.stream.JsonReader;
41+
import com.google.gson.stream.JsonWriter;
42+
import com.google.gson.JsonDeserializationContext;
43+
import com.google.gson.JsonDeserializer;
44+
import com.google.gson.JsonSerializationContext;
45+
import com.google.gson.JsonSerializer;
46+
import com.google.gson.JsonElement;
47+
import com.google.gson.JsonObject;
48+
import com.google.gson.JsonArray;
49+
import com.google.gson.JsonParseException;
50+
51+
import org.openapitools.client.JSON;
52+
53+
@javax.annotation.Generated(value = "org.openapitools.codegen.languages.JavaClientCodegen")
54+
public class AnyOfArray extends AbstractOpenApiSchema {
55+
private static final Logger log = Logger.getLogger(AnyOfArray.class.getName());
56+
57+
public static class CustomTypeAdapterFactory implements TypeAdapterFactory {
58+
@SuppressWarnings("unchecked")
59+
@Override
60+
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
61+
if (!AnyOfArray.class.isAssignableFrom(type.getRawType())) {
62+
return null; // this class only serializes 'AnyOfArray' and its subtypes
63+
}
64+
final TypeAdapter<JsonElement> elementAdapter = gson.getAdapter(JsonElement.class);
65+
final TypeAdapter<Object> adapterObject = gson.getDelegateAdapter(this, TypeToken.get(Object.class));
66+
67+
return (TypeAdapter<T>) new TypeAdapter<AnyOfArray>() {
68+
@Override
69+
public void write(JsonWriter out, AnyOfArray value) throws IOException {
70+
if (value == null || value.getActualInstance() == null) {
71+
elementAdapter.write(out, null);
72+
return;
73+
}
74+
75+
// check if the actual instance is of the type `Object`
76+
if (value.getActualInstance() instanceof Object) {
77+
JsonPrimitive primitive = adapterObject.toJsonTree((Object)value.getActualInstance()).getAsJsonPrimitive();
78+
elementAdapter.write(out, primitive);
79+
return;
80+
}
81+
throw new IOException("Failed to serialize as the type doesn't match anyOf schemae: Object");
82+
}
83+
84+
@Override
85+
public AnyOfArray read(JsonReader in) throws IOException {
86+
Object deserialized = null;
87+
JsonElement jsonElement = elementAdapter.read(in);
88+
89+
ArrayList<String> errorMessages = new ArrayList<>();
90+
TypeAdapter actualAdapter = elementAdapter;
91+
92+
// deserialize Object
93+
try {
94+
// validate the JSON object to see if any exception is thrown
95+
if(!jsonElement.getAsJsonPrimitive().isNumber()) {
96+
throw new IllegalArgumentException(String.format("Expected json element to be of type Number in the JSON string but got `%s`", jsonElement.toString()));
97+
}
98+
actualAdapter = adapterObject;
99+
AnyOfArray ret = new AnyOfArray();
100+
ret.setActualInstance(actualAdapter.fromJsonTree(jsonElement));
101+
return ret;
102+
} catch (Exception e) {
103+
// deserialization failed, continue
104+
errorMessages.add(String.format("Deserialization for Object failed with `%s`.", e.getMessage()));
105+
log.log(Level.FINER, "Input data does not match schema 'Object'", e);
106+
}
107+
108+
throw new IOException(String.format("Failed deserialization for AnyOfArray: no class matches result, expected at least 1. Detailed failure message for anyOf schemas: %s. JSON: %s", errorMessages, jsonElement.toString()));
109+
}
110+
}.nullSafe();
111+
}
112+
}
113+
114+
// store a list of schema names defined in anyOf
115+
public static final Map<String, Class<?>> schemas = new HashMap<String, Class<?>>();
116+
117+
public AnyOfArray() {
118+
super("anyOf", Boolean.FALSE);
119+
}
120+
121+
public AnyOfArray(Object o) {
122+
super("anyOf", Boolean.FALSE);
123+
setActualInstance(o);
124+
}
125+
126+
static {
127+
schemas.put("Object", Object.class);
128+
}
129+
130+
@Override
131+
public Map<String, Class<?>> getSchemas() {
132+
return AnyOfArray.schemas;
133+
}
134+
135+
/**
136+
* Set the instance that matches the anyOf child schema, check
137+
* the instance parameter is valid against the anyOf child schemas:
138+
* Object
139+
*
140+
* It could be an instance of the 'anyOf' schemas.
141+
*/
142+
@Override
143+
public void setActualInstance(Object instance) {
144+
if (instance instanceof Object) {
145+
super.setActualInstance(instance);
146+
return;
147+
}
148+
149+
throw new RuntimeException("Invalid instance type. Must be Object");
150+
}
151+
152+
/**
153+
* Get the actual instance, which can be the following:
154+
* Object
155+
*
156+
* @return The actual instance (Object)
157+
*/
158+
@Override
159+
public Object getActualInstance() {
160+
return super.getActualInstance();
161+
}
162+
163+
/**
164+
* Get the actual instance of `Object`. If the actual instance is not `Object`,
165+
* the ClassCastException will be thrown.
166+
*
167+
* @return The actual instance of `Object`
168+
* @throws ClassCastException if the instance is not `Object`
169+
*/
170+
public Object getObject() throws ClassCastException {
171+
return (Object)super.getActualInstance();
172+
}
173+
174+
/**
175+
* Validates the JSON Element and throws an exception if issues found
176+
*
177+
* @param jsonElement JSON Element
178+
* @throws IOException if the JSON Element is invalid with respect to AnyOfArray
179+
*/
180+
public static void validateJsonElement(JsonElement jsonElement) throws IOException {
181+
// validate anyOf schemas one by one
182+
ArrayList<String> errorMessages = new ArrayList<>();
183+
// validate the json string with Object
184+
try {
185+
if(!jsonElement.getAsJsonPrimitive().isNumber()) {
186+
throw new IllegalArgumentException(String.format("Expected json element to be of type Number in the JSON string but got `%s`", jsonElement.toString()));
187+
}
188+
return;
189+
} catch (Exception e) {
190+
errorMessages.add(String.format("Deserialization for Object failed with `%s`.", e.getMessage()));
191+
// continue to the next one
192+
}
193+
throw new IOException(String.format("The JSON string is invalid for AnyOfArray with anyOf schemas: Object. no class match the result, expected at least 1. Detailed failure message for anyOf schemas: %s. JSON: %s", errorMessages, jsonElement.toString()));
194+
195+
}
196+
197+
/**
198+
* Create an instance of AnyOfArray given an JSON string
199+
*
200+
* @param jsonString JSON string
201+
* @return An instance of AnyOfArray
202+
* @throws IOException if the JSON string is invalid with respect to AnyOfArray
203+
*/
204+
public static AnyOfArray fromJson(String jsonString) throws IOException {
205+
return JSON.getGson().fromJson(jsonString, AnyOfArray.class);
206+
}
207+
208+
/**
209+
* Convert an instance of AnyOfArray to an JSON string
210+
*
211+
* @return JSON string
212+
*/
213+
public String toJson() {
214+
return JSON.getGson().toJson(this);
215+
}
216+
}
217+
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/*
2+
* OpenAPI Petstore
3+
* This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters.
4+
*
5+
* The version of the OpenAPI document: 1.0.0
6+
*
7+
*
8+
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
9+
* https://openapi-generator.tech
10+
* Do not edit the class manually.
11+
*/
12+
13+
14+
package org.openapitools.client.model;
15+
16+
import org.junit.jupiter.api.Disabled;
17+
import org.junit.jupiter.api.Test;
18+
19+
/**
20+
* Model tests for AnyOfArray
21+
*/
22+
public class AnyOfArrayTest {
23+
private final AnyOfArray model = new AnyOfArray();
24+
25+
/**
26+
* Model tests for AnyOfArray
27+
*/
28+
@Test
29+
public void testAnyOfArray() {
30+
// TODO: test AnyOfArray
31+
}
32+
33+
}

0 commit comments

Comments
 (0)