Fixes so that a oneOf schema with a single sub-schema is simplified (#21043)

* Fixes so that a oneOf schema with a single sub-schema is simplified when SIMPLIFY_ONEOF_ANYOF is set to true

* Adjusts oneOf_array test to ensure that it is generated as an interface instead of being simplified

* Update ruby samples so that they no longer refer to a model that is now gone due to the schema being simplified
This commit is contained in:
Mattias Sehlstedt 2025-04-26 16:53:21 +02:00 committed by GitHub
parent 02204d0e4b
commit 1eee6038df
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
21 changed files with 198 additions and 409 deletions

View File

@ -38,6 +38,7 @@ import java.util.*;
import java.util.function.Function; import java.util.function.Function;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static org.openapitools.codegen.utils.ModelUtils.simplyOneOfAnyOfWithOnlyOneNonNullSubSchema;
import static org.openapitools.codegen.utils.StringUtils.getUniqueString; import static org.openapitools.codegen.utils.StringUtils.getUniqueString;
public class OpenAPINormalizer { public class OpenAPINormalizer {
@ -1266,17 +1267,7 @@ public class OpenAPINormalizer {
} }
} }
if (oneOfSchemas.removeIf(oneOf -> ModelUtils.isNullTypeSchema(openAPI, oneOf))) { schema = simplyOneOfAnyOfWithOnlyOneNonNullSubSchema(openAPI, schema, oneOfSchemas);
schema.setNullable(true);
// if only one element left, simplify to just the element (schema)
if (oneOfSchemas.size() == 1) {
if (Boolean.TRUE.equals(schema.getNullable())) { // retain nullable setting
((Schema) oneOfSchemas.get(0)).setNullable(true);
}
return (Schema) oneOfSchemas.get(0);
}
}
if (ModelUtils.isIntegerSchema(schema) || ModelUtils.isNumberSchema(schema) || ModelUtils.isStringSchema(schema)) { if (ModelUtils.isIntegerSchema(schema) || ModelUtils.isNumberSchema(schema) || ModelUtils.isStringSchema(schema)) {
// TODO convert oneOf const to enum // TODO convert oneOf const to enum
@ -1403,17 +1394,7 @@ public class OpenAPINormalizer {
} }
} }
if (anyOfSchemas.removeIf(anyOf -> ModelUtils.isNullTypeSchema(openAPI, anyOf))) { schema = simplyOneOfAnyOfWithOnlyOneNonNullSubSchema(openAPI, schema, anyOfSchemas);
schema.setNullable(true);
}
// if only one element left, simplify to just the element (schema)
if (anyOfSchemas.size() == 1) {
if (Boolean.TRUE.equals(schema.getNullable())) { // retain nullable setting
((Schema) anyOfSchemas.get(0)).setNullable(true);
}
return (Schema) anyOfSchemas.get(0);
}
} }
return schema; return schema;

View File

@ -2219,6 +2219,30 @@ public class ModelUtils {
} }
} }
/**
* Simplifies the schema by removing the oneOfAnyOf if the oneOfAnyOf only contains a single non-null sub-schema
*
* @param openAPI OpenAPI
* @param schema Schema
* @param subSchemas The oneOf or AnyOf schemas
* @return The simplified schema
*/
public static Schema simplyOneOfAnyOfWithOnlyOneNonNullSubSchema(OpenAPI openAPI, Schema schema, List<Schema> subSchemas) {
if (subSchemas.removeIf(subSchema -> isNullTypeSchema(openAPI, subSchema))) {
schema.setNullable(true);
}
// if only one element left, simplify to just the element (schema)
if (subSchemas.size() == 1) {
if (Boolean.TRUE.equals(schema.getNullable())) { // retain nullable setting
subSchemas.get(0).setNullable(true);
}
return subSchemas.get(0);
}
return schema;
}
/** /**
* Check if the schema is of type 'null' or schema itself is pointing to null * Check if the schema is of type 'null' or schema itself is pointing to null
* <p> * <p>

View File

@ -168,6 +168,12 @@ public class OpenAPINormalizerTest {
Schema schema15 = openAPI.getComponents().getSchemas().get("AnyOfAnyTypeWithRef"); Schema schema15 = openAPI.getComponents().getSchemas().get("AnyOfAnyTypeWithRef");
assertEquals(schema15.getAnyOf().size(), 6); assertEquals(schema15.getAnyOf().size(), 6);
Schema schema17 = openAPI.getComponents().getSchemas().get("ParentWithOneOfProperty");
assertEquals(((Schema) schema17.getProperties().get("number")).getOneOf().size(), 1);
Schema schema19 = openAPI.getComponents().getSchemas().get("SingleAnyOfTest");
assertEquals(schema19.getAnyOf().size(), 1);
Map<String, String> options = new HashMap<>(); Map<String, String> options = new HashMap<>();
options.put("SIMPLIFY_ONEOF_ANYOF", "true"); options.put("SIMPLIFY_ONEOF_ANYOF", "true");
OpenAPINormalizer openAPINormalizer = new OpenAPINormalizer(openAPI, options); OpenAPINormalizer openAPINormalizer = new OpenAPINormalizer(openAPI, options);
@ -209,6 +215,30 @@ public class OpenAPINormalizerTest {
Schema schema16 = openAPI.getComponents().getSchemas().get("AnyOfAnyTypeWithRef"); Schema schema16 = openAPI.getComponents().getSchemas().get("AnyOfAnyTypeWithRef");
assertEquals(schema16.getAnyOf(), null); assertEquals(schema16.getAnyOf(), null);
assertEquals(schema16.getType(), null); assertEquals(schema16.getType(), null);
Schema schema18 = openAPI.getComponents().getSchemas().get("ParentWithOneOfProperty");
assertEquals(((Schema) schema18.getProperties().get("number")).get$ref(), "#/components/schemas/Number");
Schema schema20 = openAPI.getComponents().getSchemas().get("SingleAnyOfTest");
assertEquals(schema20.getAnyOf(), null);
assertEquals(schema20.getType(), "string");
assertEquals(schema20.getEnum().size(), 2);
}
@Test
public void testOpenAPINormalizerSimplifyOneOfWithSingleRef() {
OpenAPI openAPI = TestUtils.parseSpec("src/test/resources/3_0/simplifyOneOfAnyOf_test.yaml");
Schema oneOfWithSingleRef = openAPI.getComponents().getSchemas().get("ParentWithOneOfProperty");
assertEquals(((Schema) oneOfWithSingleRef.getProperties().get("number")).getOneOf().size(), 1);
Map<String, String> options = new HashMap<>();
options.put("SIMPLIFY_ONEOF_ANYOF", "true");
OpenAPINormalizer openAPINormalizer = new OpenAPINormalizer(openAPI, options);
openAPINormalizer.normalize();
oneOfWithSingleRef = openAPI.getComponents().getSchemas().get("ParentWithOneOfProperty");
assertEquals(((Schema) oneOfWithSingleRef.getProperties().get("number")).get$ref(), "#/components/schemas/Number");
} }
@Test @Test
@ -830,6 +860,12 @@ public class OpenAPINormalizerTest {
Schema schema17 = openAPI.getComponents().getSchemas().get("OneOfNullAndRef3"); Schema schema17 = openAPI.getComponents().getSchemas().get("OneOfNullAndRef3");
assertEquals(schema17.getOneOf().size(), 2); assertEquals(schema17.getOneOf().size(), 2);
Schema schema19 = openAPI.getComponents().getSchemas().get("ParentWithOneOfProperty");
assertEquals(((Schema) schema19.getProperties().get("number")).getOneOf().size(), 1);
Schema schema21 = openAPI.getComponents().getSchemas().get("SingleAnyOfTest");
assertEquals(schema21.getAnyOf().size(), 1);
Map<String, String> options = new HashMap<>(); Map<String, String> options = new HashMap<>();
options.put("SIMPLIFY_ONEOF_ANYOF", "true"); options.put("SIMPLIFY_ONEOF_ANYOF", "true");
OpenAPINormalizer openAPINormalizer = new OpenAPINormalizer(openAPI, options); OpenAPINormalizer openAPINormalizer = new OpenAPINormalizer(openAPI, options);
@ -876,6 +912,30 @@ public class OpenAPINormalizerTest {
// original oneOf removed and simplified to just $ref (oneOf sub-schema) instead // original oneOf removed and simplified to just $ref (oneOf sub-schema) instead
assertEquals(schema18.getOneOf(), null); assertEquals(schema18.getOneOf(), null);
assertEquals(schema18.get$ref(), "#/components/schemas/Parent"); assertEquals(schema18.get$ref(), "#/components/schemas/Parent");
Schema schema20 = openAPI.getComponents().getSchemas().get("ParentWithOneOfProperty");
assertEquals(((Schema) schema20.getProperties().get("number")).get$ref(), "#/components/schemas/Number");
Schema schema22 = openAPI.getComponents().getSchemas().get("SingleAnyOfTest");
assertEquals(schema22.getAnyOf(), null);
assertEquals(schema22.getTypes(), Set.of("string"));
assertEquals(schema22.getEnum().size(), 2);
}
@Test
public void testOpenAPINormalizerSimplifyOneOfWithSingleRef31Spec() {
OpenAPI openAPI = TestUtils.parseSpec("src/test/resources/3_1/simplifyOneOfAnyOf_test.yaml");
Schema oneOfWithSingleRef = openAPI.getComponents().getSchemas().get("ParentWithOneOfProperty");
assertEquals(((Schema) oneOfWithSingleRef.getProperties().get("number")).getOneOf().size(), 1);
Map<String, String> options = new HashMap<>();
options.put("SIMPLIFY_ONEOF_ANYOF", "true");
OpenAPINormalizer openAPINormalizer = new OpenAPINormalizer(openAPI, options);
openAPINormalizer.normalize();
oneOfWithSingleRef = openAPI.getComponents().getSchemas().get("ParentWithOneOfProperty");
assertEquals(((Schema) oneOfWithSingleRef.getProperties().get("number")).get$ref(), "#/components/schemas/Number");
} }
@Test @Test

View File

@ -2272,7 +2272,7 @@ public class SpringCodegenTest {
"SchemaA.java", "public final class SchemaA extends RepresentationModel<SchemaA> implements PostRequest {", "SchemaA.java", "public final class SchemaA extends RepresentationModel<SchemaA> implements PostRequest {",
"PostRequest.java", "public sealed interface PostRequest permits SchemaA {")}, "PostRequest.java", "public sealed interface PostRequest permits SchemaA {")},
{"oneOf_array.yaml", Map.of( {"oneOf_array.yaml", Map.of(
"MyExampleGet200Response.java", "public interface MyExampleGet200Response")}, "MyExampleGet200Response.java", "public sealed interface MyExampleGet200Response")},
{"oneOf_duplicateArray.yaml", Map.of( {"oneOf_duplicateArray.yaml", Map.of(
"Example.java", "public interface Example {")}, "Example.java", "public interface Example {")},
{"oneOf_nonPrimitive.yaml", Map.of( {"oneOf_nonPrimitive.yaml", Map.of(

View File

@ -32,8 +32,7 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import static org.testng.Assert.assertFalse; import static org.testng.Assert.*;
import static org.testng.Assert.assertTrue;
public class ModelUtilsTest { public class ModelUtilsTest {
@ -476,6 +475,54 @@ public class ModelUtilsTest {
Assert.assertNotNull(ModelUtils.getSchemaItems((Schema) arrayWithPrefixItems.getProperties().get("without_items"))); Assert.assertNotNull(ModelUtils.getSchemaItems((Schema) arrayWithPrefixItems.getProperties().get("without_items")));
} }
@Test
public void simplyOneOfAnyOfWithOnlyOneNonNullSubSchema() {
OpenAPI openAPI = TestUtils.parseSpec("src/test/resources/3_0/simplifyOneOfAnyOf_test.yaml");
Schema schema;
List<Schema> subSchemas;
Schema anyOfWithSeveralSubSchemasButSingleNonNull = ModelUtils.getSchema(openAPI, "AnyOfTest");
subSchemas = anyOfWithSeveralSubSchemasButSingleNonNull.getAnyOf();
schema = ModelUtils.simplyOneOfAnyOfWithOnlyOneNonNullSubSchema(openAPI, anyOfWithSeveralSubSchemasButSingleNonNull, subSchemas);
assertNull(schema.getOneOf());
assertNull(schema.getAnyOf());
assertTrue(schema.getNullable());
assertEquals("string", schema.getType());
Schema anyOfWithSingleNonNullSubSchema = ModelUtils.getSchema(openAPI, "Parent");
subSchemas = ((Schema) anyOfWithSingleNonNullSubSchema.getProperties().get("number")).getAnyOf();
schema = ModelUtils.simplyOneOfAnyOfWithOnlyOneNonNullSubSchema(openAPI, anyOfWithSingleNonNullSubSchema, subSchemas);
assertNull(schema.getOneOf());
assertNull(schema.getAnyOf());
assertNull(schema.getNullable());
assertEquals(schema.get$ref(), "#/components/schemas/Number");
Schema oneOfWithSeveralSubSchemasButSingleNonNull = ModelUtils.getSchema(openAPI, "OneOfTest");
subSchemas = oneOfWithSeveralSubSchemasButSingleNonNull.getOneOf();
schema = ModelUtils.simplyOneOfAnyOfWithOnlyOneNonNullSubSchema(openAPI, oneOfWithSeveralSubSchemasButSingleNonNull, subSchemas);
assertNull(schema.getOneOf());
assertNull(schema.getAnyOf());
assertTrue(schema.getNullable());
assertEquals("integer", schema.getType());
Schema oneOfWithSingleNonNullSubSchema = ModelUtils.getSchema(openAPI, "ParentWithOneOfProperty");
subSchemas = ((Schema) oneOfWithSingleNonNullSubSchema.getProperties().get("number")).getOneOf();
schema = ModelUtils.simplyOneOfAnyOfWithOnlyOneNonNullSubSchema(openAPI, oneOfWithSingleNonNullSubSchema, subSchemas);
assertNull(schema.getOneOf());
assertNull(schema.getAnyOf());
assertNull(schema.getNullable());
assertEquals(schema.get$ref(), "#/components/schemas/Number");
Schema oneOfWithSeveralSubSchemas = ModelUtils.getSchema(openAPI, "ParentWithPluralOneOfProperty");
subSchemas = ((Schema) oneOfWithSeveralSubSchemas.getProperties().get("number")).getOneOf();
schema = ModelUtils.simplyOneOfAnyOfWithOnlyOneNonNullSubSchema(openAPI, oneOfWithSeveralSubSchemas, subSchemas);
assertNull(schema.getOneOf());
assertNotNull(oneOfWithSeveralSubSchemas.getProperties().get("number"));
assertNull(schema.getAnyOf());
assertNull(schema.getNullable());
assertEquals(((Schema) oneOfWithSeveralSubSchemas.getProperties().get("number")).getOneOf().size(), 2);
}
@Test @Test
public void isNullTypeSchemaTest() { public void isNullTypeSchemaTest() {
OpenAPI openAPI = TestUtils.parseSpec("src/test/resources/3_0/null_schema_test.yaml"); OpenAPI openAPI = TestUtils.parseSpec("src/test/resources/3_0/null_schema_test.yaml");

View File

@ -15,6 +15,8 @@ paths:
- type: array - type: array
items: items:
"$ref": "#/components/schemas/OneOf1" "$ref": "#/components/schemas/OneOf1"
- type: object
"$ref": "#/components/schemas/OneOf1"
components: components:
schemas: schemas:
OneOf1: OneOf1:

View File

@ -30,4 +30,5 @@ components:
- items: - items:
$ref: '#/components/schemas/OneOf1' $ref: '#/components/schemas/OneOf1'
type: array type: array
- $ref: '#/components/schemas/OneOf1'

View File

@ -7,6 +7,7 @@
| Name | Type | Description | Notes | | Name | Type | Description | Notes |
|------------ | ------------- | ------------- | -------------| |------------ | ------------- | ------------- | -------------|
|**message1** | **String** | | [optional] |

View File

@ -14,6 +14,13 @@
package org.openapitools.client.model; package org.openapitools.client.model;
import java.util.Objects; import java.util.Objects;
import com.google.gson.TypeAdapter;
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 java.io.IOException;
import java.util.Arrays;
import java.util.List; import java.util.List;
import org.openapitools.client.model.OneOf1; import org.openapitools.client.model.OneOf1;
import javax.validation.constraints.*; import javax.validation.constraints.*;
@ -69,6 +76,7 @@ public class MyExampleGet200Response extends AbstractOpenApiSchema {
final Type typeInstanceListOneOf1 = new TypeToken<List<@Valid OneOf1>>(){}.getType(); final Type typeInstanceListOneOf1 = new TypeToken<List<@Valid OneOf1>>(){}.getType();
final TypeAdapter<List<@Valid OneOf1>> adapterListOneOf1 = (TypeAdapter<List<@Valid OneOf1>>) gson.getDelegateAdapter(this, TypeToken.get(typeInstanceListOneOf1)); final TypeAdapter<List<@Valid OneOf1>> adapterListOneOf1 = (TypeAdapter<List<@Valid OneOf1>>) gson.getDelegateAdapter(this, TypeToken.get(typeInstanceListOneOf1));
final TypeAdapter<OneOf1> adapterOneOf1 = gson.getDelegateAdapter(this, TypeToken.get(OneOf1.class));
return (TypeAdapter<T>) new TypeAdapter<MyExampleGet200Response>() { return (TypeAdapter<T>) new TypeAdapter<MyExampleGet200Response>() {
@Override @Override
@ -87,7 +95,13 @@ public class MyExampleGet200Response extends AbstractOpenApiSchema {
return; return;
} }
} }
throw new IOException("Failed to serialize as the type doesn't match oneOf schemas: List<@Valid OneOf1>"); // check if the actual instance is of the type `OneOf1`
if (value.getActualInstance() instanceof OneOf1) {
JsonElement element = adapterOneOf1.toJsonTree((OneOf1)value.getActualInstance());
elementAdapter.write(out, element);
return;
}
throw new IOException("Failed to serialize as the type doesn't match oneOf schemas: List<@Valid OneOf1>, OneOf1");
} }
@Override @Override
@ -119,6 +133,18 @@ public class MyExampleGet200Response extends AbstractOpenApiSchema {
errorMessages.add(String.format("Deserialization for List<@Valid OneOf1> failed with `%s`.", e.getMessage())); errorMessages.add(String.format("Deserialization for List<@Valid OneOf1> failed with `%s`.", e.getMessage()));
log.log(Level.FINER, "Input data does not match schema 'List<@Valid OneOf1>'", e); log.log(Level.FINER, "Input data does not match schema 'List<@Valid OneOf1>'", e);
} }
// deserialize OneOf1
try {
// validate the JSON object to see if any exception is thrown
OneOf1.validateJsonElement(jsonElement);
actualAdapter = adapterOneOf1;
match++;
log.log(Level.FINER, "Input data matches schema 'OneOf1'");
} catch (Exception e) {
// deserialization failed, continue
errorMessages.add(String.format("Deserialization for OneOf1 failed with `%s`.", e.getMessage()));
log.log(Level.FINER, "Input data does not match schema 'OneOf1'", e);
}
if (match == 1) { if (match == 1) {
MyExampleGet200Response ret = new MyExampleGet200Response(); MyExampleGet200Response ret = new MyExampleGet200Response();
@ -146,6 +172,7 @@ public class MyExampleGet200Response extends AbstractOpenApiSchema {
static { static {
schemas.put("List<@Valid OneOf1>", List.class); schemas.put("List<@Valid OneOf1>", List.class);
schemas.put("OneOf1", OneOf1.class);
} }
@Override @Override
@ -156,7 +183,7 @@ public class MyExampleGet200Response extends AbstractOpenApiSchema {
/** /**
* Set the instance that matches the oneOf child schema, check * Set the instance that matches the oneOf child schema, check
* the instance parameter is valid against the oneOf child schemas: * the instance parameter is valid against the oneOf child schemas:
* List<@Valid OneOf1> * List<@Valid OneOf1>, OneOf1
* *
* It could be an instance of the 'oneOf' schemas. * It could be an instance of the 'oneOf' schemas.
*/ */
@ -170,14 +197,19 @@ public class MyExampleGet200Response extends AbstractOpenApiSchema {
} }
} }
throw new RuntimeException("Invalid instance type. Must be List<@Valid OneOf1>"); if (instance instanceof OneOf1) {
super.setActualInstance(instance);
return;
}
throw new RuntimeException("Invalid instance type. Must be List<@Valid OneOf1>, OneOf1");
} }
/** /**
* Get the actual instance, which can be the following: * Get the actual instance, which can be the following:
* List<@Valid OneOf1> * List<@Valid OneOf1>, OneOf1
* *
* @return The actual instance (List<@Valid OneOf1>) * @return The actual instance (List<@Valid OneOf1>, OneOf1)
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Override @Override
@ -196,6 +228,17 @@ public class MyExampleGet200Response extends AbstractOpenApiSchema {
return (List<@Valid OneOf1>)super.getActualInstance(); return (List<@Valid OneOf1>)super.getActualInstance();
} }
/**
* Get the actual instance of `OneOf1`. If the actual instance is not `OneOf1`,
* the ClassCastException will be thrown.
*
* @return The actual instance of `OneOf1`
* @throws ClassCastException if the instance is not `OneOf1`
*/
public OneOf1 getOneOf1() throws ClassCastException {
return (OneOf1)super.getActualInstance();
}
/** /**
* Validates the JSON Element and throws an exception if issues found * Validates the JSON Element and throws an exception if issues found
* *
@ -221,8 +264,16 @@ public class MyExampleGet200Response extends AbstractOpenApiSchema {
errorMessages.add(String.format("Deserialization for List<@Valid OneOf1> failed with `%s`.", e.getMessage())); errorMessages.add(String.format("Deserialization for List<@Valid OneOf1> failed with `%s`.", e.getMessage()));
// continue to the next one // continue to the next one
} }
// validate the json string with OneOf1
try {
OneOf1.validateJsonElement(jsonElement);
validCount++;
} catch (Exception e) {
errorMessages.add(String.format("Deserialization for OneOf1 failed with `%s`.", e.getMessage()));
// continue to the next one
}
if (validCount != 1) { if (validCount != 1) {
throw new IOException(String.format("The JSON string is invalid for MyExampleGet200Response with oneOf schemas: List<@Valid OneOf1>. %d class(es) match the result, expected 1. Detailed failure message for oneOf schemas: %s. JSON: %s", validCount, errorMessages, jsonElement.toString())); throw new IOException(String.format("The JSON string is invalid for MyExampleGet200Response with oneOf schemas: List<@Valid OneOf1>, OneOf1. %d class(es) match the result, expected 1. Detailed failure message for oneOf schemas: %s. JSON: %s", validCount, errorMessages, jsonElement.toString()));
} }
} }

View File

@ -37,7 +37,6 @@ docs/FormatTest.md
docs/HasOnlyReadOnly.md docs/HasOnlyReadOnly.md
docs/HealthCheckResult.md docs/HealthCheckResult.md
docs/List.md docs/List.md
docs/MamalWithEnum.md
docs/Mammal.md docs/Mammal.md
docs/MammalAnyof.md docs/MammalAnyof.md
docs/MammalWithoutDiscriminator.md docs/MammalWithoutDiscriminator.md
@ -108,7 +107,6 @@ lib/petstore/models/format_test.rb
lib/petstore/models/has_only_read_only.rb lib/petstore/models/has_only_read_only.rb
lib/petstore/models/health_check_result.rb lib/petstore/models/health_check_result.rb
lib/petstore/models/list.rb lib/petstore/models/list.rb
lib/petstore/models/mamal_with_enum.rb
lib/petstore/models/mammal.rb lib/petstore/models/mammal.rb
lib/petstore/models/mammal_anyof.rb lib/petstore/models/mammal_anyof.rb
lib/petstore/models/mammal_without_discriminator.rb lib/petstore/models/mammal_without_discriminator.rb

View File

@ -153,7 +153,6 @@ Class | Method | HTTP request | Description
- [Petstore::HasOnlyReadOnly](docs/HasOnlyReadOnly.md) - [Petstore::HasOnlyReadOnly](docs/HasOnlyReadOnly.md)
- [Petstore::HealthCheckResult](docs/HealthCheckResult.md) - [Petstore::HealthCheckResult](docs/HealthCheckResult.md)
- [Petstore::List](docs/List.md) - [Petstore::List](docs/List.md)
- [Petstore::MamalWithEnum](docs/MamalWithEnum.md)
- [Petstore::Mammal](docs/Mammal.md) - [Petstore::Mammal](docs/Mammal.md)
- [Petstore::MammalAnyof](docs/MammalAnyof.md) - [Petstore::MammalAnyof](docs/MammalAnyof.md)
- [Petstore::MammalWithoutDiscriminator](docs/MammalWithoutDiscriminator.md) - [Petstore::MammalWithoutDiscriminator](docs/MammalWithoutDiscriminator.md)

View File

@ -1,47 +0,0 @@
# Petstore::MamalWithEnum
## Class instance methods
### `openapi_one_of`
Returns the list of classes defined in oneOf.
#### Example
```ruby
require 'petstore'
Petstore::MamalWithEnum.openapi_one_of
# =>
# [
# :'Cow'
# ]
```
### build
Find the appropriate object from the `openapi_one_of` list and casts the data into it.
#### Example
```ruby
require 'petstore'
Petstore::MamalWithEnum.build(data)
# => #<Cow:0x00007fdd4aab02a0>
Petstore::MamalWithEnum.build(data_that_doesnt_match)
# => nil
```
#### Parameters
| Name | Type | Description |
| ---- | ---- | ----------- |
| **data** | **Mixed** | data to be matched against the list of oneOf items |
#### Return type
- `Cow`
- `nil` (if no type matches)

View File

@ -42,7 +42,6 @@ require 'petstore/models/format_test'
require 'petstore/models/has_only_read_only' require 'petstore/models/has_only_read_only'
require 'petstore/models/health_check_result' require 'petstore/models/health_check_result'
require 'petstore/models/list' require 'petstore/models/list'
require 'petstore/models/mamal_with_enum'
require 'petstore/models/mammal' require 'petstore/models/mammal'
require 'petstore/models/mammal_anyof' require 'petstore/models/mammal_anyof'
require 'petstore/models/mammal_without_discriminator' require 'petstore/models/mammal_without_discriminator'

View File

@ -1,104 +0,0 @@
=begin
#OpenAPI Petstore
#This spec is mainly for testing Petstore server and contains fake endpoints, models. Please do not use this for any other purpose. Special characters: \" \\
The version of the OpenAPI document: 1.0.0
Generated by: https://openapi-generator.tech
Generator version: 7.13.0-SNAPSHOT
=end
require 'date'
require 'time'
module Petstore
module MamalWithEnum
class << self
# List of class defined in oneOf (OpenAPI v3)
def openapi_one_of
[
:'Cow'
]
end
# Builds the object
# @param [Mixed] Data to be matched against the list of oneOf items
# @return [Object] Returns the model or the data itself
def build(data)
# Go through the list of oneOf items and attempt to identify the appropriate one.
# Note:
# - We do not attempt to check whether exactly one item matches.
# - No advanced validation of types in some cases (e.g. "x: { type: string }" will happily match { x: 123 })
# due to the way the deserialization is made in the base_object template (it just casts without verifying).
# - TODO: scalar values are de facto behaving as if they were nullable.
# - TODO: logging when debugging is set.
openapi_one_of.each do |klass|
begin
next if klass == :AnyType # "nullable: true"
typed_data = find_and_cast_into_type(klass, data)
return typed_data if typed_data
rescue # rescue all errors so we keep iterating even if the current item lookup raises
end
end
openapi_one_of.include?(:AnyType) ? data : nil
end
private
SchemaMismatchError = Class.new(StandardError)
# Note: 'File' is missing here because in the regular case we get the data _after_ a call to JSON.parse.
def find_and_cast_into_type(klass, data)
return if data.nil?
case klass.to_s
when 'Boolean'
return data if data.instance_of?(TrueClass) || data.instance_of?(FalseClass)
when 'Float'
return data if data.instance_of?(Float)
when 'Integer'
return data if data.instance_of?(Integer)
when 'Time'
return Time.parse(data)
when 'Date'
return Date.parse(data)
when 'String'
return data if data.instance_of?(String)
when 'Object' # "type: object"
return data if data.instance_of?(Hash)
when /\AArray<(?<sub_type>.+)>\z/ # "type: array"
if data.instance_of?(Array)
sub_type = Regexp.last_match[:sub_type]
return data.map { |item| find_and_cast_into_type(sub_type, item) }
end
when /\AHash<String, (?<sub_type>.+)>\z/ # "type: object" with "additionalProperties: { ... }"
if data.instance_of?(Hash) && data.keys.all? { |k| k.instance_of?(Symbol) || k.instance_of?(String) }
sub_type = Regexp.last_match[:sub_type]
return data.each_with_object({}) { |(k, v), hsh| hsh[k] = find_and_cast_into_type(sub_type, v) }
end
else # model
const = Petstore.const_get(klass)
if const
if const.respond_to?(:openapi_one_of) # nested oneOf model
model = const.build(data)
return model if model
else
# raise if data contains keys that are not known to the model
raise if const.respond_to?(:acceptable_attributes) && !(data.keys - const.acceptable_attributes).empty?
model = const.build_from_hash(data)
return model if model
end
end
end
raise # if no match by now, raise
rescue
raise SchemaMismatchError, "#{data} doesn't match the #{klass} type"
end
end
end
end

View File

@ -1,32 +0,0 @@
=begin
#OpenAPI Petstore
#This spec is mainly for testing Petstore server and contains fake endpoints, models. Please do not use this for any other purpose. Special characters: \" \\
The version of the OpenAPI document: 1.0.0
Generated by: https://openapi-generator.tech
Generator version: 7.11.0-SNAPSHOT
=end
require 'spec_helper'
require 'json'
require 'date'
# Unit tests for Petstore::MamalWithEnum
# Automatically generated by openapi-generator (https://openapi-generator.tech)
# Please update as you see appropriate
describe Petstore::MamalWithEnum do
describe '.openapi_one_of' do
it 'lists the items referenced in the oneOf array' do
expect(described_class.openapi_one_of).to_not be_empty
end
end
describe '.build' do
it 'returns the correct model' do
# assertion here. ref: https://rspec.info/features/3-12/rspec-expectations/built-in-matchers/
end
end
end

View File

@ -37,7 +37,6 @@ docs/FormatTest.md
docs/HasOnlyReadOnly.md docs/HasOnlyReadOnly.md
docs/HealthCheckResult.md docs/HealthCheckResult.md
docs/List.md docs/List.md
docs/MamalWithEnum.md
docs/Mammal.md docs/Mammal.md
docs/MammalAnyof.md docs/MammalAnyof.md
docs/MammalWithoutDiscriminator.md docs/MammalWithoutDiscriminator.md
@ -108,7 +107,6 @@ lib/petstore/models/format_test.rb
lib/petstore/models/has_only_read_only.rb lib/petstore/models/has_only_read_only.rb
lib/petstore/models/health_check_result.rb lib/petstore/models/health_check_result.rb
lib/petstore/models/list.rb lib/petstore/models/list.rb
lib/petstore/models/mamal_with_enum.rb
lib/petstore/models/mammal.rb lib/petstore/models/mammal.rb
lib/petstore/models/mammal_anyof.rb lib/petstore/models/mammal_anyof.rb
lib/petstore/models/mammal_without_discriminator.rb lib/petstore/models/mammal_without_discriminator.rb

View File

@ -153,7 +153,6 @@ Class | Method | HTTP request | Description
- [Petstore::HasOnlyReadOnly](docs/HasOnlyReadOnly.md) - [Petstore::HasOnlyReadOnly](docs/HasOnlyReadOnly.md)
- [Petstore::HealthCheckResult](docs/HealthCheckResult.md) - [Petstore::HealthCheckResult](docs/HealthCheckResult.md)
- [Petstore::List](docs/List.md) - [Petstore::List](docs/List.md)
- [Petstore::MamalWithEnum](docs/MamalWithEnum.md)
- [Petstore::Mammal](docs/Mammal.md) - [Petstore::Mammal](docs/Mammal.md)
- [Petstore::MammalAnyof](docs/MammalAnyof.md) - [Petstore::MammalAnyof](docs/MammalAnyof.md)
- [Petstore::MammalWithoutDiscriminator](docs/MammalWithoutDiscriminator.md) - [Petstore::MammalWithoutDiscriminator](docs/MammalWithoutDiscriminator.md)

View File

@ -1,47 +0,0 @@
# Petstore::MamalWithEnum
## Class instance methods
### `openapi_one_of`
Returns the list of classes defined in oneOf.
#### Example
```ruby
require 'petstore'
Petstore::MamalWithEnum.openapi_one_of
# =>
# [
# :'Cow'
# ]
```
### build
Find the appropriate object from the `openapi_one_of` list and casts the data into it.
#### Example
```ruby
require 'petstore'
Petstore::MamalWithEnum.build(data)
# => #<Cow:0x00007fdd4aab02a0>
Petstore::MamalWithEnum.build(data_that_doesnt_match)
# => nil
```
#### Parameters
| Name | Type | Description |
| ---- | ---- | ----------- |
| **data** | **Mixed** | data to be matched against the list of oneOf items |
#### Return type
- `Cow`
- `nil` (if no type matches)

View File

@ -42,7 +42,6 @@ require 'petstore/models/format_test'
require 'petstore/models/has_only_read_only' require 'petstore/models/has_only_read_only'
require 'petstore/models/health_check_result' require 'petstore/models/health_check_result'
require 'petstore/models/list' require 'petstore/models/list'
require 'petstore/models/mamal_with_enum'
require 'petstore/models/mammal' require 'petstore/models/mammal'
require 'petstore/models/mammal_anyof' require 'petstore/models/mammal_anyof'
require 'petstore/models/mammal_without_discriminator' require 'petstore/models/mammal_without_discriminator'

View File

@ -1,104 +0,0 @@
=begin
#OpenAPI Petstore
#This spec is mainly for testing Petstore server and contains fake endpoints, models. Please do not use this for any other purpose. Special characters: \" \\
The version of the OpenAPI document: 1.0.0
Generated by: https://openapi-generator.tech
Generator version: 7.13.0-SNAPSHOT
=end
require 'date'
require 'time'
module Petstore
module MamalWithEnum
class << self
# List of class defined in oneOf (OpenAPI v3)
def openapi_one_of
[
:'Cow'
]
end
# Builds the object
# @param [Mixed] Data to be matched against the list of oneOf items
# @return [Object] Returns the model or the data itself
def build(data)
# Go through the list of oneOf items and attempt to identify the appropriate one.
# Note:
# - We do not attempt to check whether exactly one item matches.
# - No advanced validation of types in some cases (e.g. "x: { type: string }" will happily match { x: 123 })
# due to the way the deserialization is made in the base_object template (it just casts without verifying).
# - TODO: scalar values are de facto behaving as if they were nullable.
# - TODO: logging when debugging is set.
openapi_one_of.each do |klass|
begin
next if klass == :AnyType # "nullable: true"
typed_data = find_and_cast_into_type(klass, data)
return typed_data if typed_data
rescue # rescue all errors so we keep iterating even if the current item lookup raises
end
end
openapi_one_of.include?(:AnyType) ? data : nil
end
private
SchemaMismatchError = Class.new(StandardError)
# Note: 'File' is missing here because in the regular case we get the data _after_ a call to JSON.parse.
def find_and_cast_into_type(klass, data)
return if data.nil?
case klass.to_s
when 'Boolean'
return data if data.instance_of?(TrueClass) || data.instance_of?(FalseClass)
when 'Float'
return data if data.instance_of?(Float)
when 'Integer'
return data if data.instance_of?(Integer)
when 'Time'
return Time.parse(data)
when 'Date'
return Date.parse(data)
when 'String'
return data if data.instance_of?(String)
when 'Object' # "type: object"
return data if data.instance_of?(Hash)
when /\AArray<(?<sub_type>.+)>\z/ # "type: array"
if data.instance_of?(Array)
sub_type = Regexp.last_match[:sub_type]
return data.map { |item| find_and_cast_into_type(sub_type, item) }
end
when /\AHash<String, (?<sub_type>.+)>\z/ # "type: object" with "additionalProperties: { ... }"
if data.instance_of?(Hash) && data.keys.all? { |k| k.instance_of?(Symbol) || k.instance_of?(String) }
sub_type = Regexp.last_match[:sub_type]
return data.each_with_object({}) { |(k, v), hsh| hsh[k] = find_and_cast_into_type(sub_type, v) }
end
else # model
const = Petstore.const_get(klass)
if const
if const.respond_to?(:openapi_one_of) # nested oneOf model
model = const.build(data)
return model if model
else
# raise if data contains keys that are not known to the model
raise if const.respond_to?(:acceptable_attributes) && !(data.keys - const.acceptable_attributes).empty?
model = const.build_from_hash(data)
return model if model
end
end
end
raise # if no match by now, raise
rescue
raise SchemaMismatchError, "#{data} doesn't match the #{klass} type"
end
end
end
end

View File

@ -1,36 +0,0 @@
=begin
#OpenAPI Petstore
#This spec is mainly for testing Petstore server and contains fake endpoints, models. Please do not use this for any other purpose. Special characters: \" \\
The version of the OpenAPI document: 1.0.0
Generated by: https://openapi-generator.tech
OpenAPI Generator version: 7.3.0-SNAPSHOT
=end
require 'spec_helper'
require 'json'
require 'date'
# Unit tests for Petstore::MamalWithEnum
# Automatically generated by openapi-generator (https://openapi-generator.tech)
# Please update as you see appropriate
describe Petstore::MamalWithEnum do
describe '.openapi_one_of' do
it 'lists the items referenced in the oneOf array' do
expect(described_class.openapi_one_of).to_not be_empty
end
end
describe '.build' do
it 'returns the correct model' do
expect(described_class.build("BlackAndWhiteCow")).to eq(Petstore::Cow::BLACK_AND_WHITE_COW)
end
it 'returns nil for unknown model' do
expect(described_class.build({ classname: 'monkey', type: 'gorilla' })).to be_nil
end
end
end