Skip to content

Commit

Permalink
[OAS 3.1] Fix null type check in normalizer (OpenAPITools#17609)
Browse files Browse the repository at this point in the history
* fix null type check in normalizer

* add back ref check

* add files
  • Loading branch information
wing328 authored Jan 13, 2024
1 parent 51dbd32 commit 64c763b
Show file tree
Hide file tree
Showing 9 changed files with 290 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -743,6 +743,16 @@ private boolean isNullTypeSchema(Schema schema) {
return true;
}

if (schema.getTypes() != null && !schema.getTypes().isEmpty()) {
// 3.1 spec
if (schema.getTypes().size() ==1) { // 1 type only
String type = (String) schema.getTypes().iterator().next();
return type == null || "null".equals(type);
} else { // more than 1 type so must not be just null
return false;
}
}

if ((schema.getType() == null || schema.getType().equals("null")) && schema.get$ref() == null) {
return true;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -889,3 +889,11 @@ components:
properties: {}
type: object
title: HTTPValidationError
AnyOfArray:
anyOf:
- type: array
items:
type: string
- type: array
items:
type: integer
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ api/openapi.yaml
build.gradle
build.sbt
docs/Animal.md
docs/AnyOfArray.md
docs/AnyTypeTest.md
docs/Cat.md
docs/Category.md
Expand Down Expand Up @@ -57,6 +58,7 @@ src/main/java/org/openapitools/client/auth/OAuthOkHttpClient.java
src/main/java/org/openapitools/client/auth/RetryingOAuth.java
src/main/java/org/openapitools/client/model/AbstractOpenApiSchema.java
src/main/java/org/openapitools/client/model/Animal.java
src/main/java/org/openapitools/client/model/AnyOfArray.java
src/main/java/org/openapitools/client/model/AnyTypeTest.java
src/main/java/org/openapitools/client/model/Cat.java
src/main/java/org/openapitools/client/model/Category.java
Expand Down
1 change: 1 addition & 0 deletions samples/client/petstore/java/okhttp-gson-3.1/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ Class | Method | HTTP request | Description
## Documentation for Models

- [Animal](docs/Animal.md)
- [AnyOfArray](docs/AnyOfArray.md)
- [AnyTypeTest](docs/AnyTypeTest.md)
- [Cat](docs/Cat.md)
- [Category](docs/Category.md)
Expand Down
6 changes: 6 additions & 0 deletions samples/client/petstore/java/okhttp-gson-3.1/api/openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -956,6 +956,12 @@ components:
properties: {}
title: HTTPValidationError
type: object
AnyOfArray:
anyOf:
- items:
type: string
- items:
type: integer
updatePetWithForm_request:
properties:
name:
Expand Down
12 changes: 12 additions & 0 deletions samples/client/petstore/java/okhttp-gson-3.1/docs/AnyOfArray.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@


# AnyOfArray


## Properties

| Name | Type | Description | Notes |
|------------ | ------------- | ------------- | -------------|



Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ private static Class getClassByDiscriminator(Map classByDiscriminatorValue, Stri
gsonBuilder.registerTypeAdapter(OffsetDateTime.class, offsetDateTimeTypeAdapter);
gsonBuilder.registerTypeAdapter(LocalDate.class, localDateTypeAdapter);
gsonBuilder.registerTypeAdapter(byte[].class, byteArrayAdapter);
gsonBuilder.registerTypeAdapterFactory(new org.openapitools.client.model.AnyOfArray.CustomTypeAdapterFactory());
gsonBuilder.registerTypeAdapterFactory(new org.openapitools.client.model.AnyTypeTest.CustomTypeAdapterFactory());
gsonBuilder.registerTypeAdapterFactory(new org.openapitools.client.model.Cat.CustomTypeAdapterFactory());
gsonBuilder.registerTypeAdapterFactory(new org.openapitools.client.model.Category.CustomTypeAdapterFactory());
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,217 @@
/*
* OpenAPI Petstore
* This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters.
*
* The version of the OpenAPI document: 1.0.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.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 AnyOfArray extends AbstractOpenApiSchema {
private static final Logger log = Logger.getLogger(AnyOfArray.class.getName());

public static class CustomTypeAdapterFactory implements TypeAdapterFactory {
@SuppressWarnings("unchecked")
@Override
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
if (!AnyOfArray.class.isAssignableFrom(type.getRawType())) {
return null; // this class only serializes 'AnyOfArray' and its subtypes
}
final TypeAdapter<JsonElement> elementAdapter = gson.getAdapter(JsonElement.class);
final TypeAdapter<Object> adapterObject = gson.getDelegateAdapter(this, TypeToken.get(Object.class));

return (TypeAdapter<T>) new TypeAdapter<AnyOfArray>() {
@Override
public void write(JsonWriter out, AnyOfArray value) throws IOException {
if (value == null || value.getActualInstance() == null) {
elementAdapter.write(out, null);
return;
}

// check if the actual instance is of the type `Object`
if (value.getActualInstance() instanceof Object) {
JsonPrimitive primitive = adapterObject.toJsonTree((Object)value.getActualInstance()).getAsJsonPrimitive();
elementAdapter.write(out, primitive);
return;
}
throw new IOException("Failed to serialize as the type doesn't match anyOf schemae: Object");
}

@Override
public AnyOfArray read(JsonReader in) throws IOException {
Object deserialized = null;
JsonElement jsonElement = elementAdapter.read(in);

ArrayList<String> errorMessages = new ArrayList<>();
TypeAdapter actualAdapter = elementAdapter;

// deserialize Object
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 = adapterObject;
AnyOfArray ret = new AnyOfArray();
ret.setActualInstance(actualAdapter.fromJsonTree(jsonElement));
return ret;
} catch (Exception e) {
// deserialization failed, continue
errorMessages.add(String.format("Deserialization for Object failed with `%s`.", e.getMessage()));
log.log(Level.FINER, "Input data does not match schema 'Object'", e);
}

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()));
}
}.nullSafe();
}
}

// store a list of schema names defined in anyOf
public static final Map<String, Class<?>> schemas = new HashMap<String, Class<?>>();

public AnyOfArray() {
super("anyOf", Boolean.FALSE);
}

public AnyOfArray(Object o) {
super("anyOf", Boolean.FALSE);
setActualInstance(o);
}

static {
schemas.put("Object", Object.class);
}

@Override
public Map<String, Class<?>> getSchemas() {
return AnyOfArray.schemas;
}

/**
* Set the instance that matches the anyOf child schema, check
* the instance parameter is valid against the anyOf child schemas:
* Object
*
* It could be an instance of the 'anyOf' schemas.
*/
@Override
public void setActualInstance(Object instance) {
if (instance instanceof Object) {
super.setActualInstance(instance);
return;
}

throw new RuntimeException("Invalid instance type. Must be Object");
}

/**
* Get the actual instance, which can be the following:
* Object
*
* @return The actual instance (Object)
*/
@Override
public Object getActualInstance() {
return super.getActualInstance();
}

/**
* Get the actual instance of `Object`. If the actual instance is not `Object`,
* the ClassCastException will be thrown.
*
* @return The actual instance of `Object`
* @throws ClassCastException if the instance is not `Object`
*/
public Object getObject() throws ClassCastException {
return (Object)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 AnyOfArray
*/
public static void validateJsonElement(JsonElement jsonElement) throws IOException {
// validate anyOf schemas one by one
ArrayList<String> errorMessages = new ArrayList<>();
// validate the json string with Object
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()));
}
return;
} catch (Exception e) {
errorMessages.add(String.format("Deserialization for Object failed with `%s`.", e.getMessage()));
// continue to the next one
}
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()));

}

/**
* Create an instance of AnyOfArray given an JSON string
*
* @param jsonString JSON string
* @return An instance of AnyOfArray
* @throws IOException if the JSON string is invalid with respect to AnyOfArray
*/
public static AnyOfArray fromJson(String jsonString) throws IOException {
return JSON.getGson().fromJson(jsonString, AnyOfArray.class);
}

/**
* Convert an instance of AnyOfArray to an JSON string
*
* @return JSON string
*/
public String toJson() {
return JSON.getGson().toJson(this);
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* OpenAPI Petstore
* This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters.
*
* The version of the OpenAPI document: 1.0.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 AnyOfArray
*/
public class AnyOfArrayTest {
private final AnyOfArray model = new AnyOfArray();

/**
* Model tests for AnyOfArray
*/
@Test
public void testAnyOfArray() {
// TODO: test AnyOfArray
}

}

0 comments on commit 64c763b

Please sign in to comment.