Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Python-experimental] JSON schema 'null' type should be modeled as 'none_type' #6121

Merged
merged 61 commits into from
May 13, 2020
Merged
Show file tree
Hide file tree
Changes from 46 commits
Commits
Show all changes
61 commits
Select commit Hold shift + click to select a range
489985d
Handle null type
sebastien-rosset Apr 30, 2020
c16089e
Handle null type
sebastien-rosset Apr 30, 2020
26865b3
Handle null type. Add 'null' type in the OAS document for testing pur…
sebastien-rosset Apr 30, 2020
48c310e
Handle null type. Add 'null' type in the OAS document for testing pur…
sebastien-rosset Apr 30, 2020
fddb63b
Handle null type. Add 'null' type in the OAS document for testing pur…
sebastien-rosset Apr 30, 2020
cf4e9f8
Handle null type. Add 'null' type in the OAS document for testing pur…
sebastien-rosset Apr 30, 2020
3b59b47
Handle null type. Add 'null' type in the OAS document for testing pur…
sebastien-rosset May 1, 2020
b362f92
Handle null type. Add 'null' type in the OAS document for testing pur…
sebastien-rosset May 1, 2020
ae93643
improve documentation
sebastien-rosset May 1, 2020
f5acfba
Handle 'null' type
sebastien-rosset May 1, 2020
6ba1523
Handle 'null' type. Add unit tests
sebastien-rosset May 1, 2020
de13169
Merge branch 'master' of github.com:CiscoM31/openapi-generator into p…
sebastien-rosset May 1, 2020
2a6a87a
Add NullType for go
sebastien-rosset May 2, 2020
86bc4c0
Add NullType for go
sebastien-rosset May 2, 2020
6b9c0ce
fix modeling of AnyType for go-experimental
sebastien-rosset May 2, 2020
b0d221e
Merge branch 'master' of github.com:CiscoM31/openapi-generator into p…
sebastien-rosset May 2, 2020
a302894
execute scripts in bin directory
sebastien-rosset May 3, 2020
e449c82
resolve merge conflicts
sebastien-rosset May 3, 2020
043f247
Merge branch 'master' of github.com:CiscoM31/openapi-generator into p…
sebastien-rosset May 4, 2020
1e14c94
Add review comments
sebastien-rosset May 4, 2020
747803e
Add 'null' type in oneOf
sebastien-rosset May 4, 2020
aa06a89
Improve OAS YAML file for golang openapi3 samples
sebastien-rosset May 4, 2020
543d406
'Any type' includes the null value, so 'isNullable' should be set to …
sebastien-rosset May 4, 2020
81093c7
'Any type' includes the null value, so 'isNullable' should be set to …
sebastien-rosset May 4, 2020
9a54ea1
Handle AnyType and NullType
sebastien-rosset May 5, 2020
dc7a7e0
handle anytype for go-experimental
sebastien-rosset May 5, 2020
ddc810c
Merge branch 'master' of github.com:CiscoM31/openapi-generator into p…
sebastien-rosset May 5, 2020
03ec914
Log warning instead of error
sebastien-rosset May 5, 2020
bdf3653
anyOf/oneOf
sebastien-rosset May 5, 2020
9dbae45
Change x-golang-is-container extension to x-golang-has-wrapper
sebastien-rosset May 5, 2020
e1417ec
Add code comments
sebastien-rosset May 5, 2020
480ecbe
Handle Object and any type
sebastien-rosset May 5, 2020
a58f121
Handle Object and any type
sebastien-rosset May 5, 2020
01ff36e
Handle object and any type
sebastien-rosset May 5, 2020
ec97db3
add code comments
sebastien-rosset May 6, 2020
431356c
handle additional properties
sebastien-rosset May 6, 2020
93752ff
handle additional properties
sebastien-rosset May 6, 2020
261dbcd
handle additional properties
sebastien-rosset May 6, 2020
aa6b6df
handle anytype and objecttype for go-exerimental
sebastien-rosset May 6, 2020
90c0c9e
Move golang changes to a separate branch
sebastien-rosset May 7, 2020
f56c812
Move golang changes to a separate branch
sebastien-rosset May 7, 2020
b159356
Better names for the OAS document test properties
sebastien-rosset May 7, 2020
ef5149e
Move golang changes to a separate branch
sebastien-rosset May 7, 2020
84162be
Run samples scripts
sebastien-rosset May 7, 2020
ee786cb
Run samples scripts
sebastien-rosset May 7, 2020
058694b
Merge branch 'master' of github.com:CiscoM31/openapi-generator into p…
sebastien-rosset May 7, 2020
82cb0e9
fix unit test issues
sebastien-rosset May 8, 2020
2c5c1cf
Handle none type
sebastien-rosset May 8, 2020
0694f83
Merge branch 'master' of github.com:CiscoM31/openapi-generator into p…
sebastien-rosset May 8, 2020
b6afbc6
Merge branch 'master' of github.com:CiscoM31/openapi-generator into p…
sebastien-rosset May 8, 2020
f275dc9
Fix index out of range exception
sebastien-rosset May 8, 2020
93cc8c8
fix formatting issues
sebastien-rosset May 8, 2020
6939648
fix formatting issues
sebastien-rosset May 8, 2020
8bdca05
fix formatting issues. Finally figured out how to check formatting in…
sebastien-rosset May 8, 2020
4c01344
fix formatting issues
sebastien-rosset May 8, 2020
537c28b
resolve merge conflicts
sebastien-rosset May 11, 2020
71d550c
resolve merge conflicts
sebastien-rosset May 11, 2020
11c52e4
resolve merge conflicts
sebastien-rosset May 12, 2020
e16c24e
Merge branch 'master' of github.com:CiscoM31/openapi-generator into p…
sebastien-rosset May 12, 2020
8f1c202
Merge branch 'master' of github.com:CiscoM31/openapi-generator into p…
sebastien-rosset May 12, 2020
5e48c72
run samples scripts
sebastien-rosset May 12, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,9 @@ public class CodegenProperty implements Cloneable, IJsonSchemaValidationProperti
public boolean isUri;
public boolean isEmail;
/**
* The type is a free-form object, i.e. it is a map of string to values with no declared properties
* The type is a free-form object, i.e. it is a map of string to values with no declared properties.
* A OAS free-form schema may include the 'additionalProperties' attribute, which puts a constraint
* on the type of the undeclared properties.
*/
public boolean isFreeFormObject;
/**
Expand All @@ -153,6 +155,9 @@ public class CodegenProperty implements Cloneable, IJsonSchemaValidationProperti
public boolean isDiscriminator;
public List<String> _enum;
public Map<String, Object> allowableValues;
// If 'additionalProperties' is not set, items is null.
// If 'additionalProperties' is set to a type or refers to a type, 'items' provides the type information for
// the undeclared properties.
public CodegenProperty items;
public CodegenProperty mostInnerItems;
public Map<String, Object> vendorExtensions = new HashMap<String, Object>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2180,7 +2180,14 @@ public CodegenModel fromModel(String name, Schema schema) {
m.xmlNamespace = schema.getXml().getNamespace();
m.xmlName = schema.getXml().getName();
}

if (ModelUtils.isAnyTypeSchema(schema)) {
// The 'null' value is allowed when the OAS schema is 'any type'.
// See https://github.com/OAI/OpenAPI-Specification/issues/1389
if (Boolean.FALSE.equals(schema.getNullable())) {
LOGGER.error("Schema '{}' is any type, which includes the 'null' value. 'nullable' cannot be set to 'false'", name);
}
m.isNullable = true;
}
if (ModelUtils.isArraySchema(schema)) {
m.isArrayModel = true;
m.arrayModelType = fromProperty(name, schema).complexType;
Expand Down Expand Up @@ -3003,6 +3010,12 @@ public CodegenProperty fromProperty(String name, Schema p) {
} else if (ModelUtils.isFreeFormObject(p)) {
property.isFreeFormObject = true;
} else if (ModelUtils.isAnyTypeSchema(p)) {
// The 'null' value is allowed when the OAS schema is 'any type'.
// See https://github.com/OAI/OpenAPI-Specification/issues/1389
if (Boolean.FALSE.equals(p.getNullable())) {
LOGGER.warn("Schema '{}' is any type, which includes the 'null' value. 'nullable' cannot be set to 'false'", p.getName());
}
property.isNullable = true;
property.isAnyType = true;
} else if (ModelUtils.isArraySchema(p)) {
// default to string if inner item is undefined
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ public PythonClientCodegen() {
// map uuid to string for the time being
typeMapping.put("UUID", "str");
typeMapping.put("URI", "str");
typeMapping.put("null", "none_type");

// from https://docs.python.org/3/reference/lexical_analysis.html#keywords
setReservedWordsLowerCase(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -388,7 +388,9 @@ public Map<String, Object> postProcessAllModels(Map<String, Object> objs) {
composedSchemaSets.add(cm.oneOf);
for (Set<String> importSet : composedSchemaSets) {
for (String otherModelName : importSet) {
cm.imports.add(otherModelName);
if (!languageSpecificPrimitives.contains(otherModelName)) {
spacether marked this conversation as resolved.
Show resolved Hide resolved
cm.imports.add(otherModelName);
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,11 @@ public void addFromInterfaceModel(CodegenModel cm, List<Map<String, String>> mod
// note that we can't just toAdd.removeAll(m.vars) for every interfaceModel,
// as they might have different value of `hasMore` and thus are not equal
List<String> omitAdding = new ArrayList<String>();
for (CodegenModel m : cm.interfaceModels) {
for (CodegenProperty v : m.vars) {
omitAdding.add(v.baseName);
if (cm.interfaceModels != null) {
for (CodegenModel m : cm.interfaceModels) {
for (CodegenProperty v : m.vars) {
omitAdding.add(v.baseName);
}
}
}
for (CodegenProperty v : toAdd) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@ class OpenApiModel(object):
# this function uses the discriminator to
# pick a new schema/class to instantiate because a discriminator
# propertyName value was passed in
oneof_anyof_classes = cls._composed_schemas.get('oneOf', ()) +
cls._composed_schemas.get('anyOf', ())
if oneof_anyof_classes and none_type in oneof_anyof_classes and args[0] is None:
return None

visited_composed_classes = kwargs.get('_visited_composed_classes', ())
if (
Expand All @@ -75,9 +79,6 @@ class OpenApiModel(object):
# want to instantiate it this time
return super(OpenApiModel, cls).__new__(cls)

oneof_anyof_classes = []
oneof_anyof_classes.extend(cls._composed_schemas.get('oneOf', ()))
oneof_anyof_classes.extend(cls._composed_schemas.get('anyOf', ()))
new_cls = cls.get_discriminator_class(kwargs)
if new_cls is None:
disc_prop_name_py = list(cls.discriminator.keys())[0]
Expand Down Expand Up @@ -1032,7 +1033,7 @@ def get_oneof_instance(self, model_args, constant_args):
and path to item.

Returns
oneof_instance (instance/None)
oneof_instance (instance)
"""
if len(self._composed_schemas['oneOf']) == 0:
return None
Expand Down Expand Up @@ -1086,9 +1087,11 @@ def get_anyof_instances(self, model_args, constant_args):
Args:
self: the class we are handling
model_args (dict): var_name to var_value
used to make instances
The input data, e.g. the payload that must match at least one
anyOf child schema in the OpenAPI document.
constant_args (dict): var_name to var_value
used to make instances
args that every model requires, including configuration, server
and path to item.

Returns
anyof_instances (list)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1250,22 +1250,32 @@ components:
type: integer
format: int32
description: User Status
arbitraryObject:
objectWithNoDeclaredProps:
type: object
description: test code generation for objects
Value must be a map of strings to values. It cannot be the 'null' value.
arbitraryNullableObject:
objectWithNoDeclaredPropsNullable:
type: object
description: test code generation for nullable objects.
Value must be a map of strings to values or the 'null' value.
nullable: true
arbitraryTypeValue:
anyTypeProp:
description: test code generation for any type
Value can be any type - string, number, boolean, array or object.
arbitraryNullableTypeValue:
Here the 'type' attribute is not specified, which means the value can be anything,
including the null value, string, number, boolean, array or object.
See https://github.com/OAI/OpenAPI-Specification/issues/1389
# TODO: this should be supported, currently there are some issues in the code generation.
#anyTypeExceptNullProp:
# description: any type except 'null'
# Here the 'type' attribute is not specified, which means the value can be anything,
# including the null value, string, number, boolean, array or object.
# not:
# type: 'null'
anyTypePropNullable:
description: test code generation for any type
Value can be any type - string, number, boolean, array, object or
the 'null' value.
Here the 'type' attribute is not specified, which means the value can be anything,
including the null value, string, number, boolean, array or object.
The 'nullable' attribute does not change the allowed values.
nullable: true
xml:
name: User
Expand Down Expand Up @@ -1855,6 +1865,7 @@ components:
- $ref: '#/components/schemas/banana'
fruitReq:
oneOf:
- type: 'null'
- $ref: '#/components/schemas/appleReq'
- $ref: '#/components/schemas/bananaReq'
appleReq:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,10 @@ def __new__(cls, *args, **kwargs):
# this function uses the discriminator to
# pick a new schema/class to instantiate because a discriminator
# propertyName value was passed in
oneof_anyof_classes = cls._composed_schemas.get('oneOf', ()) +
cls._composed_schemas.get('anyOf', ())
if oneof_anyof_classes and none_type in oneof_anyof_classes and args[0] is None:
return None

visited_composed_classes = kwargs.get('_visited_composed_classes', ())
if (
Expand All @@ -145,9 +149,6 @@ def __new__(cls, *args, **kwargs):
# want to instantiate it this time
return super(OpenApiModel, cls).__new__(cls)

oneof_anyof_classes = []
oneof_anyof_classes.extend(cls._composed_schemas.get('oneOf', ()))
oneof_anyof_classes.extend(cls._composed_schemas.get('anyOf', ()))
new_cls = cls.get_discriminator_class(kwargs)
if new_cls is None:
disc_prop_name_py = list(cls.discriminator.keys())[0]
Expand Down Expand Up @@ -1293,7 +1294,7 @@ def get_oneof_instance(self, model_args, constant_args):
and path to item.

Returns
oneof_instance (instance/None)
oneof_instance (instance)
"""
if len(self._composed_schemas['oneOf']) == 0:
return None
Expand Down Expand Up @@ -1347,9 +1348,11 @@ def get_anyof_instances(self, model_args, constant_args):
Args:
self: the class we are handling
model_args (dict): var_name to var_value
used to make instances
The input data, e.g. the payload that must match at least one
anyOf child schema in the OpenAPI document.
constant_args (dict): var_name to var_value
used to make instances
args that every model requires, including configuration, server
and path to item.

Returns
anyof_instances (list)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,16 @@ SetArbitraryTypeValue sets ArbitraryTypeValue field to given value.

HasArbitraryTypeValue returns a boolean if a field has been set.

### SetArbitraryTypeValueNil

`func (o *User) SetArbitraryTypeValueNil(b bool)`

SetArbitraryTypeValueNil sets the value for ArbitraryTypeValue to be an explicit nil

### UnsetArbitraryTypeValue
`func (o *User) UnsetArbitraryTypeValue()`

UnsetArbitraryTypeValue ensures that no value is present for ArbitraryTypeValue, not even an explicit nil
### GetArbitraryNullableTypeValue

`func (o *User) GetArbitraryNullableTypeValue() interface{}`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ type User struct {
// test code generation for nullable objects. Value must be a map of strings to values or the 'null' value.
ArbitraryNullableObject map[string]interface{} `json:"arbitraryNullableObject,omitempty"`
// test code generation for any type Value can be any type - string, number, boolean, array or object.
ArbitraryTypeValue *interface{} `json:"arbitraryTypeValue,omitempty"`
ArbitraryTypeValue interface{} `json:"arbitraryTypeValue,omitempty"`
// test code generation for any type Value can be any type - string, number, boolean, array, object or the 'null' value.
ArbitraryNullableTypeValue interface{} `json:"arbitraryNullableTypeValue,omitempty"`
}
Expand Down Expand Up @@ -372,22 +372,23 @@ func (o *User) SetArbitraryNullableObject(v map[string]interface{}) {
o.ArbitraryNullableObject = v
}

// GetArbitraryTypeValue returns the ArbitraryTypeValue field value if set, zero value otherwise.
// GetArbitraryTypeValue returns the ArbitraryTypeValue field value if set, zero value otherwise (both if not set or set to explicit null).
func (o *User) GetArbitraryTypeValue() interface{} {
if o == nil || o.ArbitraryTypeValue == nil {
if o == nil {
var ret interface{}
return ret
}
return *o.ArbitraryTypeValue
return o.ArbitraryTypeValue
}

// GetArbitraryTypeValueOk returns a tuple with the ArbitraryTypeValue field value if set, nil otherwise
// and a boolean to check if the value has been set.
// NOTE: If the value is an explicit nil, `nil, true` will be returned
func (o *User) GetArbitraryTypeValueOk() (*interface{}, bool) {
if o == nil || o.ArbitraryTypeValue == nil {
return nil, false
}
return o.ArbitraryTypeValue, true
return &o.ArbitraryTypeValue, true
}

// HasArbitraryTypeValue returns a boolean if a field has been set.
Expand All @@ -401,7 +402,7 @@ func (o *User) HasArbitraryTypeValue() bool {

// SetArbitraryTypeValue gets a reference to the given interface{} and assigns it to the ArbitraryTypeValue field.
func (o *User) SetArbitraryTypeValue(v interface{}) {
o.ArbitraryTypeValue = &v
o.ArbitraryTypeValue = v
}

// GetArbitraryNullableTypeValue returns the ArbitraryNullableTypeValue field value if set, zero value otherwise (both if not set or set to explicit null).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,10 @@ def __new__(cls, *args, **kwargs):
# this function uses the discriminator to
# pick a new schema/class to instantiate because a discriminator
# propertyName value was passed in
oneof_anyof_classes = cls._composed_schemas.get('oneOf', ()) +
cls._composed_schemas.get('anyOf', ())
if oneof_anyof_classes and none_type in oneof_anyof_classes and args[0] is None:
return None

visited_composed_classes = kwargs.get('_visited_composed_classes', ())
if (
Expand All @@ -145,9 +149,6 @@ def __new__(cls, *args, **kwargs):
# want to instantiate it this time
return super(OpenApiModel, cls).__new__(cls)

oneof_anyof_classes = []
oneof_anyof_classes.extend(cls._composed_schemas.get('oneOf', ()))
oneof_anyof_classes.extend(cls._composed_schemas.get('anyOf', ()))
new_cls = cls.get_discriminator_class(kwargs)
if new_cls is None:
disc_prop_name_py = list(cls.discriminator.keys())[0]
Expand Down Expand Up @@ -1293,7 +1294,7 @@ def get_oneof_instance(self, model_args, constant_args):
and path to item.

Returns
oneof_instance (instance/None)
oneof_instance (instance)
"""
if len(self._composed_schemas['oneOf']) == 0:
return None
Expand Down Expand Up @@ -1347,9 +1348,11 @@ def get_anyof_instances(self, model_args, constant_args):
Args:
self: the class we are handling
model_args (dict): var_name to var_value
used to make instances
The input data, e.g. the payload that must match at least one
anyOf child schema in the OpenAPI document.
constant_args (dict): var_name to var_value
used to make instances
args that every model requires, including configuration, server
and path to item.

Returns
anyof_instances (list)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -218,5 +218,6 @@ def _composed_schemas():
'oneOf': [
apple_req.AppleReq,
banana_req.BananaReq,
none_type,
],
}
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ def testFruitReq(self):
'oneOf': [
petstore_api.AppleReq,
petstore_api.BananaReq,
type(None),
],
}
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,4 +125,16 @@ def test_deserialize_mammal(self):
deserialized = self.deserialize(response, (petstore_api.Mammal,), True)
self.assertTrue(isinstance(deserialized, petstore_api.Zebra))
self.assertEqual(deserialized.type, zebra_type)
self.assertEqual(deserialized.class_name, class_name)
self.assertEqual(deserialized.class_name, class_name)

def test_deserialize_fruit(self):
"""
deserialize fruit
fruitReq is a oneOf composed schema model with discriminator, including 'null' type.
"""

# Unmarshal 'null' value
data = None
response = MockResponse(data=json.dumps(data))
deserialized = self.deserialize(response, (petstore_api.FruitReq, type(None)), True)
self.assertEqual(type(deserialized), type(None))
spacether marked this conversation as resolved.
Show resolved Hide resolved