Skip to content

Commit

Permalink
fix OpenAPITools#11958 [BUG] python generates wrong model name and mo…
Browse files Browse the repository at this point in the history
…del file name (OpenAPITools#11959)

* fix OpenAPITools#11958 [BUG] python generates wrong model name and model file name

Modify AbstractPythonCodegen.toModelName just like AbstractJavaCodegen.toModelName

* add unit test

* update samples and docs
by
./bin/generate-samples.sh
./bin/utils/export_docs_generators.sh

* fix AbstractPythonCodegen#toModelName logic, remove underscore

* update samples and docs
by
./bin/generate-samples.sh
./bin/utils/export_docs_generators.sh
  • Loading branch information
chanjarster authored Apr 2, 2022
1 parent 5d5e753 commit 41451ff
Show file tree
Hide file tree
Showing 34 changed files with 2,123 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -634,33 +634,40 @@ public String getSchemaType(Schema p) {

@Override
public String toModelName(String name) {
name = sanitizeName(name); // FIXME: a parameter should not be assigned. Also declare the methods parameters as 'final'.
String sanitizedName = sanitizeName(name); // FIXME: a parameter should not be assigned. Also declare the methods parameters as 'final'.
// remove dollar sign
name = name.replaceAll("$", "");

// model name cannot use reserved keyword, e.g. return
if (isReservedWord(name)) {
LOGGER.warn("{} (reserved word) cannot be used as model name. Renamed to {}", name, camelize("model_" + name));
name = "model_" + name; // e.g. return => ModelReturn (after camelize)
}

// model name starts with number
if (name.matches("^\\d.*")) {
LOGGER.warn("{} (model name starts with number) cannot be used as model name. Renamed to {}", name, camelize("model_" + name));
name = "model_" + name; // e.g. 200Response => Model200Response (after camelize)
}
sanitizedName = sanitizedName.replaceAll("$", "");

String nameWithPrefixSuffix = sanitizedName;
if (!StringUtils.isEmpty(modelNamePrefix)) {
name = modelNamePrefix + "_" + name;
// add '_' so that model name can be camelized correctly
nameWithPrefixSuffix = modelNamePrefix + "_" + nameWithPrefixSuffix;
}

if (!StringUtils.isEmpty(modelNameSuffix)) {
name = name + "_" + modelNameSuffix;
// add '_' so that model name can be camelized correctly
nameWithPrefixSuffix = nameWithPrefixSuffix + "_" + modelNameSuffix;
}

// camelize the model name
// phone_number => PhoneNumber
return camelize(name);
String camelizedName = camelize(nameWithPrefixSuffix);

// model name cannot use reserved keyword, e.g. return
if (isReservedWord(camelizedName)) {
String modelName = "Model" + camelizedName; // e.g. return => ModelReturn (after camelize)
LOGGER.warn("{} (reserved word) cannot be used as model name. Renamed to {}", camelizedName, modelName);
return modelName;
}

// model name starts with number
if (camelizedName.matches("^\\d.*")) {
String modelName = "Model" + camelizedName; // e.g. return => ModelReturn (after camelize)
LOGGER.warn("{} (model name starts with number) cannot be used as model name. Renamed to {}", camelizedName, modelName);
return modelName;
}

return camelizedName;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
import org.openapitools.codegen.languages.PythonExperimentalClientCodegen;
import org.openapitools.codegen.utils.ModelUtils;
import org.testng.Assert;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;

@SuppressWarnings("static-method")
Expand Down Expand Up @@ -519,4 +520,33 @@ public void testRecursiveExampleValueWithCycle() throws Exception {
Assert.assertEquals(exampleValue.trim(), expectedValue.trim());
}

@DataProvider
public Object[][] testToModelData() {
return new Object[][] {
new Object[] {"", "", "foo", "Foo"},
new Object[] {"Abc", "", "foo", "AbcFoo"},
new Object[] {"", "Abc", "foo", "FooAbc"},
new Object[] {"Abc", "Xyz", "foo", "AbcFooXyz"},

new Object[] {"", "", "1", "Model1"},
new Object[] {"Abc", "", "1", "Abc1"},
new Object[] {"", "Abc", "1", "Model1Abc"},
new Object[] {"Abc", "Xyz", "1", "Abc1Xyz"},

new Object[] {"", "", "and", "ModelAnd"},
new Object[] {"Abc", "", "and", "AbcAnd"},
new Object[] {"", "Abc", "and", "AndAbc"},
new Object[] {"Abc", "Xyz", "and", "AbcAndXyz"},
};
}

@Test(dataProvider = "testToModelData")
public void testToModel(String prefix, String suffix, String input, String want) {
PythonClientCodegen codegen = new PythonClientCodegen();
codegen.setModelNamePrefix(prefix);
codegen.setModelNameSuffix(suffix);
Assert.assertEquals(codegen.toModelName(input), want);
}


}
13 changes: 13 additions & 0 deletions samples/client/petstore/python-asyncio/docs/Model_200Response.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Model_200Response

Model for testing model name starting with number

## Properties
Name | Type | Description | Notes
------------ | ------------- | ------------- | -------------
**name** | **int** | | [optional]
**_class** | **str** | | [optional]

[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)


12 changes: 12 additions & 0 deletions samples/client/petstore/python-asyncio/docs/Model_Return.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Model_Return

Model for testing reserved words

## Properties
Name | Type | Description | Notes
------------ | ------------- | ------------- | -------------
**_return** | **int** | | [optional]

[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)


Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
# coding: utf-8

"""
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: \" \\ # noqa: E501
The version of the OpenAPI document: 1.0.0
Generated by: https://openapi-generator.tech
"""


try:
from inspect import getfullargspec
except ImportError:
from inspect import getargspec as getfullargspec
import pprint
import re # noqa: F401
import six

from petstore_api.configuration import Configuration


class Model_200Response(object):
"""NOTE: This class is auto generated by OpenAPI Generator.
Ref: https://openapi-generator.tech
Do not edit the class manually.
"""

"""
Attributes:
openapi_types (dict): The key is attribute name
and the value is attribute type.
attribute_map (dict): The key is attribute name
and the value is json key in definition.
"""
openapi_types = {
'name': 'int',
'_class': 'str'
}

attribute_map = {
'name': 'name',
'_class': 'class'
}

def __init__(self, name=None, _class=None, local_vars_configuration=None): # noqa: E501
"""Model_200Response - a model defined in OpenAPI""" # noqa: E501
if local_vars_configuration is None:
local_vars_configuration = Configuration.get_default_copy()
self.local_vars_configuration = local_vars_configuration

self._name = None
self.__class = None
self.discriminator = None

if name is not None:
self.name = name
if _class is not None:
self._class = _class

@property
def name(self):
"""Gets the name of this Model_200Response. # noqa: E501
:return: The name of this Model_200Response. # noqa: E501
:rtype: int
"""
return self._name

@name.setter
def name(self, name):
"""Sets the name of this Model_200Response.
:param name: The name of this Model_200Response. # noqa: E501
:type name: int
"""

self._name = name

@property
def _class(self):
"""Gets the _class of this Model_200Response. # noqa: E501
:return: The _class of this Model_200Response. # noqa: E501
:rtype: str
"""
return self.__class

@_class.setter
def _class(self, _class):
"""Sets the _class of this Model_200Response.
:param _class: The _class of this Model_200Response. # noqa: E501
:type _class: str
"""

self.__class = _class

def to_dict(self, serialize=False):
"""Returns the model properties as a dict"""
result = {}

def convert(x):
if hasattr(x, "to_dict"):
args = getfullargspec(x.to_dict).args
if len(args) == 1:
return x.to_dict()
else:
return x.to_dict(serialize)
else:
return x

for attr, _ in six.iteritems(self.openapi_types):
value = getattr(self, attr)
attr = self.attribute_map.get(attr, attr) if serialize else attr
if isinstance(value, list):
result[attr] = list(map(
lambda x: convert(x),
value
))
elif isinstance(value, dict):
result[attr] = dict(map(
lambda item: (item[0], convert(item[1])),
value.items()
))
else:
result[attr] = convert(value)

return result

def to_str(self):
"""Returns the string representation of the model"""
return pprint.pformat(self.to_dict())

def __repr__(self):
"""For `print` and `pprint`"""
return self.to_str()

def __eq__(self, other):
"""Returns true if both objects are equal"""
if not isinstance(other, Model_200Response):
return False

return self.to_dict() == other.to_dict()

def __ne__(self, other):
"""Returns true if both objects are not equal"""
if not isinstance(other, Model_200Response):
return True

return self.to_dict() != other.to_dict()
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# coding: utf-8

"""
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: \" \\ # noqa: E501
The version of the OpenAPI document: 1.0.0
Generated by: https://openapi-generator.tech
"""


from __future__ import absolute_import

import unittest
import datetime

import petstore_api
from petstore_api.models.model_200_response import Model_200Response # noqa: E501
from petstore_api.rest import ApiException

class TestModel_200Response(unittest.TestCase):
"""Model_200Response unit test stubs"""

def setUp(self):
pass

def tearDown(self):
pass

def make_instance(self, include_optional):
"""Test Model_200Response
include_option is a boolean, when False only required
params are included, when True both required and
optional params are included """
# model = petstore_api.models.model_200_response.Model_200Response() # noqa: E501
if include_optional :
return Model_200Response(
name = 56,
_class = ''
)
else :
return Model_200Response(
)

def testModel_200Response(self):
"""Test Model_200Response"""
inst_req_only = self.make_instance(include_optional=False)
inst_req_and_optional = self.make_instance(include_optional=True)

if __name__ == '__main__':
unittest.main()
13 changes: 13 additions & 0 deletions samples/client/petstore/python-legacy/docs/Model_200Response.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Model_200Response

Model for testing model name starting with number

## Properties
Name | Type | Description | Notes
------------ | ------------- | ------------- | -------------
**name** | **int** | | [optional]
**_class** | **str** | | [optional]

[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)


12 changes: 12 additions & 0 deletions samples/client/petstore/python-legacy/docs/Model_Return.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Model_Return

Model for testing reserved words

## Properties
Name | Type | Description | Notes
------------ | ------------- | ------------- | -------------
**_return** | **int** | | [optional]

[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)


Loading

0 comments on commit 41451ff

Please sign in to comment.