mirror of
https://github.com/OpenAPITools/openapi-generator.git
synced 2025-05-12 12:40:53 +00:00
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:
parent
02204d0e4b
commit
1eee6038df
@ -38,6 +38,7 @@ import java.util.*;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.openapitools.codegen.utils.ModelUtils.simplyOneOfAnyOfWithOnlyOneNonNullSubSchema;
|
||||
import static org.openapitools.codegen.utils.StringUtils.getUniqueString;
|
||||
|
||||
public class OpenAPINormalizer {
|
||||
@ -1266,17 +1267,7 @@ public class OpenAPINormalizer {
|
||||
}
|
||||
}
|
||||
|
||||
if (oneOfSchemas.removeIf(oneOf -> ModelUtils.isNullTypeSchema(openAPI, oneOf))) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
schema = simplyOneOfAnyOfWithOnlyOneNonNullSubSchema(openAPI, schema, oneOfSchemas);
|
||||
|
||||
if (ModelUtils.isIntegerSchema(schema) || ModelUtils.isNumberSchema(schema) || ModelUtils.isStringSchema(schema)) {
|
||||
// TODO convert oneOf const to enum
|
||||
@ -1403,17 +1394,7 @@ public class OpenAPINormalizer {
|
||||
}
|
||||
}
|
||||
|
||||
if (anyOfSchemas.removeIf(anyOf -> ModelUtils.isNullTypeSchema(openAPI, anyOf))) {
|
||||
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);
|
||||
}
|
||||
schema = simplyOneOfAnyOfWithOnlyOneNonNullSubSchema(openAPI, schema, anyOfSchemas);
|
||||
}
|
||||
|
||||
return schema;
|
||||
|
@ -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
|
||||
* <p>
|
||||
|
@ -168,6 +168,12 @@ public class OpenAPINormalizerTest {
|
||||
Schema schema15 = openAPI.getComponents().getSchemas().get("AnyOfAnyTypeWithRef");
|
||||
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<>();
|
||||
options.put("SIMPLIFY_ONEOF_ANYOF", "true");
|
||||
OpenAPINormalizer openAPINormalizer = new OpenAPINormalizer(openAPI, options);
|
||||
@ -209,6 +215,30 @@ public class OpenAPINormalizerTest {
|
||||
Schema schema16 = openAPI.getComponents().getSchemas().get("AnyOfAnyTypeWithRef");
|
||||
assertEquals(schema16.getAnyOf(), 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
|
||||
@ -830,6 +860,12 @@ public class OpenAPINormalizerTest {
|
||||
Schema schema17 = openAPI.getComponents().getSchemas().get("OneOfNullAndRef3");
|
||||
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<>();
|
||||
options.put("SIMPLIFY_ONEOF_ANYOF", "true");
|
||||
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
|
||||
assertEquals(schema18.getOneOf(), null);
|
||||
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
|
||||
|
@ -2272,7 +2272,7 @@ public class SpringCodegenTest {
|
||||
"SchemaA.java", "public final class SchemaA extends RepresentationModel<SchemaA> implements PostRequest {",
|
||||
"PostRequest.java", "public sealed interface PostRequest permits SchemaA {")},
|
||||
{"oneOf_array.yaml", Map.of(
|
||||
"MyExampleGet200Response.java", "public interface MyExampleGet200Response")},
|
||||
"MyExampleGet200Response.java", "public sealed interface MyExampleGet200Response")},
|
||||
{"oneOf_duplicateArray.yaml", Map.of(
|
||||
"Example.java", "public interface Example {")},
|
||||
{"oneOf_nonPrimitive.yaml", Map.of(
|
||||
|
@ -32,8 +32,7 @@ import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.testng.Assert.assertFalse;
|
||||
import static org.testng.Assert.assertTrue;
|
||||
import static org.testng.Assert.*;
|
||||
|
||||
public class ModelUtilsTest {
|
||||
|
||||
@ -476,6 +475,54 @@ public class ModelUtilsTest {
|
||||
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
|
||||
public void isNullTypeSchemaTest() {
|
||||
OpenAPI openAPI = TestUtils.parseSpec("src/test/resources/3_0/null_schema_test.yaml");
|
||||
|
@ -15,6 +15,8 @@ paths:
|
||||
- type: array
|
||||
items:
|
||||
"$ref": "#/components/schemas/OneOf1"
|
||||
- type: object
|
||||
"$ref": "#/components/schemas/OneOf1"
|
||||
components:
|
||||
schemas:
|
||||
OneOf1:
|
||||
|
@ -30,4 +30,5 @@ components:
|
||||
- items:
|
||||
$ref: '#/components/schemas/OneOf1'
|
||||
type: array
|
||||
- $ref: '#/components/schemas/OneOf1'
|
||||
|
||||
|
@ -7,6 +7,7 @@
|
||||
|
||||
| Name | Type | Description | Notes |
|
||||
|------------ | ------------- | ------------- | -------------|
|
||||
|**message1** | **String** | | [optional] |
|
||||
|
||||
|
||||
|
||||
|
@ -14,6 +14,13 @@
|
||||
package org.openapitools.client.model;
|
||||
|
||||
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 org.openapitools.client.model.OneOf1;
|
||||
import javax.validation.constraints.*;
|
||||
@ -69,6 +76,7 @@ public class MyExampleGet200Response extends AbstractOpenApiSchema {
|
||||
|
||||
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<OneOf1> adapterOneOf1 = gson.getDelegateAdapter(this, TypeToken.get(OneOf1.class));
|
||||
|
||||
return (TypeAdapter<T>) new TypeAdapter<MyExampleGet200Response>() {
|
||||
@Override
|
||||
@ -87,7 +95,13 @@ public class MyExampleGet200Response extends AbstractOpenApiSchema {
|
||||
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
|
||||
@ -119,6 +133,18 @@ public class MyExampleGet200Response extends AbstractOpenApiSchema {
|
||||
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);
|
||||
}
|
||||
// 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) {
|
||||
MyExampleGet200Response ret = new MyExampleGet200Response();
|
||||
@ -146,6 +172,7 @@ public class MyExampleGet200Response extends AbstractOpenApiSchema {
|
||||
|
||||
static {
|
||||
schemas.put("List<@Valid OneOf1>", List.class);
|
||||
schemas.put("OneOf1", OneOf1.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -156,7 +183,7 @@ public class MyExampleGet200Response extends AbstractOpenApiSchema {
|
||||
/**
|
||||
* Set the instance that matches the oneOf child schema, check
|
||||
* 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.
|
||||
*/
|
||||
@ -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:
|
||||
* List<@Valid OneOf1>
|
||||
* List<@Valid OneOf1>, OneOf1
|
||||
*
|
||||
* @return The actual instance (List<@Valid OneOf1>)
|
||||
* @return The actual instance (List<@Valid OneOf1>, OneOf1)
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
@ -196,6 +228,17 @@ public class MyExampleGet200Response extends AbstractOpenApiSchema {
|
||||
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
|
||||
*
|
||||
@ -221,8 +264,16 @@ public class MyExampleGet200Response extends AbstractOpenApiSchema {
|
||||
errorMessages.add(String.format("Deserialization for List<@Valid OneOf1> failed with `%s`.", e.getMessage()));
|
||||
// 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) {
|
||||
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()));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -37,7 +37,6 @@ docs/FormatTest.md
|
||||
docs/HasOnlyReadOnly.md
|
||||
docs/HealthCheckResult.md
|
||||
docs/List.md
|
||||
docs/MamalWithEnum.md
|
||||
docs/Mammal.md
|
||||
docs/MammalAnyof.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/health_check_result.rb
|
||||
lib/petstore/models/list.rb
|
||||
lib/petstore/models/mamal_with_enum.rb
|
||||
lib/petstore/models/mammal.rb
|
||||
lib/petstore/models/mammal_anyof.rb
|
||||
lib/petstore/models/mammal_without_discriminator.rb
|
||||
|
@ -153,7 +153,6 @@ Class | Method | HTTP request | Description
|
||||
- [Petstore::HasOnlyReadOnly](docs/HasOnlyReadOnly.md)
|
||||
- [Petstore::HealthCheckResult](docs/HealthCheckResult.md)
|
||||
- [Petstore::List](docs/List.md)
|
||||
- [Petstore::MamalWithEnum](docs/MamalWithEnum.md)
|
||||
- [Petstore::Mammal](docs/Mammal.md)
|
||||
- [Petstore::MammalAnyof](docs/MammalAnyof.md)
|
||||
- [Petstore::MammalWithoutDiscriminator](docs/MammalWithoutDiscriminator.md)
|
||||
|
@ -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)
|
||||
|
@ -42,7 +42,6 @@ require 'petstore/models/format_test'
|
||||
require 'petstore/models/has_only_read_only'
|
||||
require 'petstore/models/health_check_result'
|
||||
require 'petstore/models/list'
|
||||
require 'petstore/models/mamal_with_enum'
|
||||
require 'petstore/models/mammal'
|
||||
require 'petstore/models/mammal_anyof'
|
||||
require 'petstore/models/mammal_without_discriminator'
|
||||
|
@ -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
|
@ -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
|
@ -37,7 +37,6 @@ docs/FormatTest.md
|
||||
docs/HasOnlyReadOnly.md
|
||||
docs/HealthCheckResult.md
|
||||
docs/List.md
|
||||
docs/MamalWithEnum.md
|
||||
docs/Mammal.md
|
||||
docs/MammalAnyof.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/health_check_result.rb
|
||||
lib/petstore/models/list.rb
|
||||
lib/petstore/models/mamal_with_enum.rb
|
||||
lib/petstore/models/mammal.rb
|
||||
lib/petstore/models/mammal_anyof.rb
|
||||
lib/petstore/models/mammal_without_discriminator.rb
|
||||
|
@ -153,7 +153,6 @@ Class | Method | HTTP request | Description
|
||||
- [Petstore::HasOnlyReadOnly](docs/HasOnlyReadOnly.md)
|
||||
- [Petstore::HealthCheckResult](docs/HealthCheckResult.md)
|
||||
- [Petstore::List](docs/List.md)
|
||||
- [Petstore::MamalWithEnum](docs/MamalWithEnum.md)
|
||||
- [Petstore::Mammal](docs/Mammal.md)
|
||||
- [Petstore::MammalAnyof](docs/MammalAnyof.md)
|
||||
- [Petstore::MammalWithoutDiscriminator](docs/MammalWithoutDiscriminator.md)
|
||||
|
@ -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)
|
||||
|
@ -42,7 +42,6 @@ require 'petstore/models/format_test'
|
||||
require 'petstore/models/has_only_read_only'
|
||||
require 'petstore/models/health_check_result'
|
||||
require 'petstore/models/list'
|
||||
require 'petstore/models/mamal_with_enum'
|
||||
require 'petstore/models/mammal'
|
||||
require 'petstore/models/mammal_anyof'
|
||||
require 'petstore/models/mammal_without_discriminator'
|
||||
|
@ -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
|
@ -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
|
Loading…
x
Reference in New Issue
Block a user