Better allOf handling in fromProperty (#15035)

* fix allOf handling in fromProperty

* add null check, update samples

* update dart generator to handle allof with a single ref
This commit is contained in:
William Cheng 2023-03-26 15:06:27 +08:00 committed by GitHub
parent 56e5122a6a
commit a4dd90c01d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 129 additions and 37 deletions

View File

@ -3795,8 +3795,8 @@ public class DefaultCodegen implements CodegenConfig {
}
Schema original = null;
// check if it's allOf (only 1 sub schema) with default/nullable/etc set in the top level
if (ModelUtils.isAllOf(p) && p.getAllOf().size() == 1 && ModelUtils.hasCommonAttributesDefined(p) ) {
// check if it's allOf (only 1 sub schema) with or without default/nullable/etc set in the top level
if (ModelUtils.isAllOf(p) && p.getAllOf().size() == 1 && !(this instanceof PythonClientCodegen)) {
if (p.getAllOf().get(0) instanceof Schema) {
original = p;
p = (Schema) p.getAllOf().get(0);
@ -4002,7 +4002,7 @@ public class DefaultCodegen implements CodegenConfig {
// restore original schema with default value, nullable, readonly etc
if (original != null) {
p = original;
// evaluate common attributes defined in the top level
// evaluate common attributes if defined in the top level
if (p.getNullable() != null) {
property.isNullable = p.getNullable();
} else if (p.getExtensions() != null && p.getExtensions().containsKey("x-nullable")) {

View File

@ -555,8 +555,8 @@ public abstract class AbstractDartCodegen extends DefaultCodegen {
public CodegenProperty fromProperty(String name, Schema p, boolean required) {
final CodegenProperty property = super.fromProperty(name, p, required);
// Handle composed properties
if (ModelUtils.isComposedSchema(p)) {
// Handle composed properties and it's NOT allOf with a single ref only
if (ModelUtils.isComposedSchema(p) && !(ModelUtils.isAllOf(p) && p.getAllOf().size() == 1)) {
ComposedSchema composed = (ComposedSchema) p;
// Count the occurrences of allOf/anyOf/oneOf with exactly one child element

View File

@ -4582,6 +4582,18 @@ public class DefaultCodegenTest {
Assert.assertFalse(defaultEnumSchemaProperty.isContainer);
Assert.assertFalse(defaultEnumSchemaProperty.isPrimitiveType);
Assert.assertEquals(defaultEnumSchemaProperty.defaultValue, "2");
// test allOf with a single sub-schema and no default value set in the top level
CodegenProperty allOfEnumSchemaProperty = modelWithReferencedSchema.vars.get(5);
Assert.assertEquals(allOfEnumSchemaProperty.getName(), "allofMinusnumberMinusenum");
Assert.assertFalse(allOfEnumSchemaProperty.isEnum);
Assert.assertTrue(allOfEnumSchemaProperty.getIsEnumOrRef());
Assert.assertTrue(allOfEnumSchemaProperty.isEnumRef);
Assert.assertFalse(allOfEnumSchemaProperty.isInnerEnum);
Assert.assertFalse(allOfEnumSchemaProperty.isString);
Assert.assertFalse(allOfEnumSchemaProperty.isContainer);
Assert.assertFalse(allOfEnumSchemaProperty.isPrimitiveType);
Assert.assertEquals(allOfEnumSchemaProperty.defaultValue, "null");
}
@Test

View File

@ -215,4 +215,7 @@ components:
default: 2
allOf:
- $ref: "#/components/schemas/NumberEnum"
allof-number-enum:
allOf:
- $ref: "#/components/schemas/NumberEnum"

View File

@ -14,13 +14,15 @@ defmodule OpenapiPetstore.Model.AllOfWithSingleRef do
@type t :: %__MODULE__{
:username => String.t | nil,
:SingleRefType => any() | nil
:SingleRefType => OpenapiPetstore.Model.SingleRefType.t | nil
}
end
defimpl Poison.Decoder, for: OpenapiPetstore.Model.AllOfWithSingleRef do
def decode(value, _options) do
import OpenapiPetstore.Deserializer
def decode(value, options) do
value
|> deserialize(:SingleRefType, :struct, OpenapiPetstore.Model.SingleRefType, options)
end
end

View File

@ -8,7 +8,7 @@
| Name | Type | Description | Notes |
|------------ | ------------- | ------------- | -------------|
|**username** | **String** | | [optional] |
|**singleRefType** | [**SingleRefType**](SingleRefType.md) | | [optional] |
|**singleRefType** | **SingleRefType** | | [optional] |

View File

@ -8,7 +8,7 @@
| Name | Type | Description | Notes |
|------------ | ------------- | ------------- | -------------|
|**username** | **String** | | [optional] |
|**singleRefType** | [**SingleRefType**](SingleRefType.md) | | [optional] |
|**singleRefType** | **SingleRefType** | | [optional] |

View File

@ -8,7 +8,7 @@
| Name | Type | Description | Notes |
|------------ | ------------- | ------------- | -------------|
|**username** | **String** | | [optional] |
|**singleRefType** | [**SingleRefType**](SingleRefType.md) | | [optional] |
|**singleRefType** | **SingleRefType** | | [optional] |

View File

@ -180,7 +180,12 @@ public class AllOfWithSingleRef {
// add `SingleRefType` to the URL query string
if (getSingleRefType() != null) {
joiner.add(getSingleRefType().toUrlQueryString(prefix + "SingleRefType" + suffix));
try {
joiner.add(String.format("%sSingleRefType%s=%s", prefix, suffix, URLEncoder.encode(String.valueOf(getSingleRefType()), "UTF-8").replaceAll("\\+", "%20")));
} catch (UnsupportedEncodingException e) {
// Should never happen, UTF-8 is always supported
throw new RuntimeException(e);
}
}
return joiner.toString();

View File

@ -8,7 +8,7 @@
| Name | Type | Description | Notes |
|------------ | ------------- | ------------- | -------------|
|**username** | **String** | | [optional] |
|**singleRefType** | [**SingleRefType**](SingleRefType.md) | | [optional] |
|**singleRefType** | **SingleRefType** | | [optional] |

View File

@ -8,7 +8,7 @@
| Name | Type | Description | Notes |
|------------ | ------------- | ------------- | -------------|
|**username** | **String** | | [optional] |
|**singleRefType** | [**SingleRefType**](SingleRefType.md) | | [optional] |
|**singleRefType** | **SingleRefType** | | [optional] |

View File

@ -5,6 +5,6 @@
Name | Type | Description | Notes
------------ | ------------- | ------------- | -------------
**username** | **string** | | [optional]
**single_ref_type** | [**SingleRefType**](SingleRefType.md) | | [optional]
**single_ref_type** | [**\OpenAPI\Client\Model\SingleRefType**](SingleRefType.md) | | [optional]
[[Back to Model list]](../../README.md#models) [[Back to API list]](../../README.md#endpoints) [[Back to README]](../../README.md)

View File

@ -58,7 +58,7 @@ class AllOfWithSingleRef implements ModelInterface, ArrayAccess, \JsonSerializab
*/
protected static $openAPITypes = [
'username' => 'string',
'single_ref_type' => 'SingleRefType'
'single_ref_type' => '\OpenAPI\Client\Model\SingleRefType'
];
/**
@ -326,7 +326,7 @@ class AllOfWithSingleRef implements ModelInterface, ArrayAccess, \JsonSerializab
/**
* Gets single_ref_type
*
* @return SingleRefType|null
* @return \OpenAPI\Client\Model\SingleRefType|null
*/
public function getSingleRefType()
{
@ -336,7 +336,7 @@ class AllOfWithSingleRef implements ModelInterface, ArrayAccess, \JsonSerializab
/**
* Sets single_ref_type
*
* @param SingleRefType|null $single_ref_type single_ref_type
* @param \OpenAPI\Client\Model\SingleRefType|null $single_ref_type single_ref_type
*
* @return self
*/

View File

@ -19,6 +19,28 @@ module Petstore
attr_accessor :single_ref_type
class EnumAttributeValidator
attr_reader :datatype
attr_reader :allowable_values
def initialize(datatype, allowable_values)
@allowable_values = allowable_values.map do |value|
case datatype.to_s
when /Integer/i
value.to_i
when /Float/i
value.to_f
else
value
end
end
end
def valid?(value)
!value || allowable_values.include?(value)
end
end
# Attribute mapping from ruby-style variable name to JSON key.
def self.attribute_map
{

View File

@ -19,6 +19,28 @@ module Petstore
attr_accessor :single_ref_type
class EnumAttributeValidator
attr_reader :datatype
attr_reader :allowable_values
def initialize(datatype, allowable_values)
@allowable_values = allowable_values.map do |value|
case datatype.to_s
when /Integer/i
value.to_i
when /Float/i
value.to_f
else
value
end
end
end
def valid?(value)
!value || allowable_values.include?(value)
end
end
# Attribute mapping from ruby-style variable name to JSON key.
def self.attribute_map
{

View File

@ -19,6 +19,28 @@ module Petstore
attr_accessor :single_ref_type
class EnumAttributeValidator
attr_reader :datatype
attr_reader :allowable_values
def initialize(datatype, allowable_values)
@allowable_values = allowable_values.map do |value|
case datatype.to_s
when /Integer/i
value.to_i
when /Float/i
value.to_f
else
value
end
end
end
def valid?(value)
!value || allowable_values.include?(value)
end
end
# Attribute mapping from ruby-style variable name to JSON key.
def self.attribute_map
{

View File

@ -21,6 +21,7 @@ abstract class AllOfWithSingleRef implements Built<AllOfWithSingleRef, AllOfWith
@BuiltValueField(wireName: r'SingleRefType')
SingleRefType? get singleRefType;
// enum singleRefTypeEnum { admin, user, };
AllOfWithSingleRef._();

View File

@ -25,6 +25,12 @@ class AllOfWithSingleRef {
///
String? username;
///
/// Please note: This property should have been non-nullable! Since the specification file
/// does not include a default value (using the "default:" property), however, the generated
/// source code must fall back to having a nullable type.
/// Consider adding a "default:" property in the specification file to hide this note.
///
SingleRefType? singleRefType;
@override

View File

@ -19,15 +19,16 @@ import re # noqa: F401
import json
from typing import Any, Optional
from typing import Optional
from pydantic import BaseModel, Field, StrictStr
from petstore_api.models.single_ref_type import SingleRefType
class AllOfWithSingleRef(BaseModel):
"""
AllOfWithSingleRef
"""
username: Optional[StrictStr] = None
single_ref_type: Optional[Any] = Field(None, alias="SingleRefType")
single_ref_type: Optional[SingleRefType] = Field(None, alias="SingleRefType")
__properties = ["username", "SingleRefType"]
class Config:
@ -53,9 +54,6 @@ class AllOfWithSingleRef(BaseModel):
exclude={
},
exclude_none=True)
# override the default output from pydantic by calling `to_dict()` of single_ref_type
if self.single_ref_type:
_dict['SingleRefType'] = self.single_ref_type.to_dict()
return _dict
@classmethod
@ -69,7 +67,7 @@ class AllOfWithSingleRef(BaseModel):
_obj = AllOfWithSingleRef.parse_obj({
"username": obj.get("username"),
"single_ref_type": SingleRefType.from_dict(obj.get("SingleRefType")) if obj.get("SingleRefType") is not None else None
"single_ref_type": obj.get("SingleRefType")
})
return _obj

View File

@ -19,15 +19,16 @@ import re # noqa: F401
import json
from typing import Any, Optional
from typing import Optional
from pydantic import BaseModel, Field, StrictStr
from petstore_api.models.single_ref_type import SingleRefType
class AllOfWithSingleRef(BaseModel):
"""
AllOfWithSingleRef
"""
username: Optional[StrictStr] = None
single_ref_type: Optional[Any] = Field(None, alias="SingleRefType")
single_ref_type: Optional[SingleRefType] = Field(None, alias="SingleRefType")
additional_properties: Dict[str, Any] = {}
__properties = ["username", "SingleRefType"]
@ -55,9 +56,6 @@ class AllOfWithSingleRef(BaseModel):
"additional_properties"
},
exclude_none=True)
# override the default output from pydantic by calling `to_dict()` of single_ref_type
if self.single_ref_type:
_dict['SingleRefType'] = self.single_ref_type.to_dict()
# puts key-value pairs in additional_properties in the top level
if self.additional_properties is not None:
for _key, _value in self.additional_properties.items():
@ -76,7 +74,7 @@ class AllOfWithSingleRef(BaseModel):
_obj = AllOfWithSingleRef.parse_obj({
"username": obj.get("username"),
"single_ref_type": SingleRefType.from_dict(obj.get("SingleRefType")) if obj.get("SingleRefType") is not None else None
"single_ref_type": obj.get("SingleRefType")
})
# store additional fields in additional_properties
for _key in obj.keys():

View File

@ -20,6 +20,7 @@
#include <sstream>
#include <stdexcept>
#include <regex>
#include <algorithm>
#include <boost/lexical_cast.hpp>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
@ -63,7 +64,6 @@ ptree AllOfWithSingleRef::toPropertyTree() const
ptree pt;
ptree tmp_node;
pt.put("username", m_Username);
pt.add_child("SingleRefType", m_SingleRefType.toPropertyTree());
return pt;
}
@ -71,9 +71,6 @@ void AllOfWithSingleRef::fromPropertyTree(ptree const &pt)
{
ptree tmp_node;
m_Username = pt.get("username", "");
if (pt.get_child_optional("SingleRefType")) {
m_SingleRefType = fromPt<SingleRefType>(pt.get_child("SingleRefType"));
}
}
std::string AllOfWithSingleRef::getUsername() const

View File

@ -25,6 +25,7 @@
#include "SingleRefType.h"
#include <memory>
#include <vector>
#include <array>
#include <boost/property_tree/ptree.hpp>
#include "helpers.h"
@ -72,7 +73,7 @@ public:
protected:
std::string m_Username = "";
SingleRefType m_SingleRefType;
SingleRefType m_SingleRefType = SingleRefType{};
};
std::vector<AllOfWithSingleRef> createAllOfWithSingleRefVectorFromJsonString(const std::string& json);

View File

@ -12,6 +12,8 @@
package org.openapitools.server.model;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonValue;
import org.openapitools.server.model.SingleRefType;
import jakarta.validation.constraints.*;
import jakarta.validation.Valid;

View File

@ -1,5 +1,7 @@
package org.openapitools.server.model;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonValue;
import org.openapitools.server.model.SingleRefType;

View File

@ -16,6 +16,7 @@ package org.openapitools.model;
import java.util.Objects;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonValue;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import org.openapitools.model.SingleRefType;

View File

@ -12,7 +12,7 @@ class AllOfWithSingleRef {
/** @var string $username */
public $username = "";
/** @var SingleRefType $singleRefType */
/** @var \app\Models\SingleRefType $singleRefType */
public $singleRefType;
}