forked from loafle/openapi-generator-original
[python] Fix date-time parsing (#6458)
This commit is contained in:
parent
e2e3405689
commit
d07f459ce3
@ -722,7 +722,7 @@ public class PythonClientCodegen extends DefaultCodegen implements CodegenConfig
|
|||||||
}
|
}
|
||||||
|
|
||||||
// correct "'"s into "'"s after toString()
|
// correct "'"s into "'"s after toString()
|
||||||
if (ModelUtils.isStringSchema(schema) && schema.getDefault() != null) {
|
if (ModelUtils.isStringSchema(schema) && schema.getDefault() != null && !ModelUtils.isDateSchema(schema) && !ModelUtils.isDateTimeSchema(schema)) {
|
||||||
example = (String) schema.getDefault();
|
example = (String) schema.getDefault();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,8 +38,9 @@ import org.openapitools.codegen.meta.Stability;
|
|||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import java.text.DateFormat;
|
import java.time.OffsetDateTime;
|
||||||
import java.text.SimpleDateFormat;
|
import java.time.ZoneOffset;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
@ -196,15 +197,15 @@ public class PythonClientExperimentalCodegen extends PythonClientCodegen {
|
|||||||
return "python-experimental";
|
return "python-experimental";
|
||||||
}
|
}
|
||||||
|
|
||||||
public String dateToString(Schema p, Date date, DateFormat dateFormatter, DateFormat dateTimeFormatter) {
|
public String dateToString(Schema p, OffsetDateTime date, DateTimeFormatter dateFormatter, DateTimeFormatter dateTimeFormatter) {
|
||||||
// converts a date into a date or date-time python string
|
// converts a date into a date or date-time python string
|
||||||
if (!(ModelUtils.isDateSchema(p) || ModelUtils.isDateTimeSchema(p))) {
|
if (!(ModelUtils.isDateSchema(p) || ModelUtils.isDateTimeSchema(p))) {
|
||||||
throw new RuntimeException("passed schema must be of type Date or DateTime");
|
throw new RuntimeException("passed schema must be of type Date or DateTime");
|
||||||
}
|
}
|
||||||
if (ModelUtils.isDateSchema(p)) {
|
if (ModelUtils.isDateSchema(p)) {
|
||||||
return "dateutil_parser('" + dateFormatter.format(date) + "').date()";
|
return "dateutil_parser('" + date.format(dateFormatter) + "').date()";
|
||||||
}
|
}
|
||||||
return "dateutil_parser('" + dateTimeFormatter.format(date) + "')";
|
return "dateutil_parser('" + date.format(dateTimeFormatter) + "')";
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -228,20 +229,17 @@ public class PythonClientExperimentalCodegen extends PythonClientCodegen {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// convert datetime and date enums if they exist
|
// convert datetime and date enums if they exist
|
||||||
DateFormat iso8601Date = new SimpleDateFormat("yyyy-MM-dd", Locale.ROOT);
|
DateTimeFormatter iso8601Date = DateTimeFormatter.ISO_DATE;
|
||||||
DateFormat iso8601DateTime = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX", Locale.ROOT);
|
DateTimeFormatter iso8601DateTime = DateTimeFormatter.ISO_DATE_TIME;
|
||||||
TimeZone utc = TimeZone.getTimeZone("UTC");
|
|
||||||
iso8601Date.setTimeZone(utc);
|
|
||||||
iso8601DateTime.setTimeZone(utc);
|
|
||||||
|
|
||||||
if (ModelUtils.isDateSchema(p) || ModelUtils.isDateTimeSchema(p)) {
|
if (ModelUtils.isDateSchema(p) || ModelUtils.isDateTimeSchema(p)) {
|
||||||
List<Object> currentEnum = p.getEnum();
|
List<Object> currentEnum = p.getEnum();
|
||||||
List<String> fixedEnum = new ArrayList<String>();
|
List<String> fixedEnum = new ArrayList<String>();
|
||||||
String fixedValue = null;
|
String fixedValue = null;
|
||||||
Date date = null;
|
OffsetDateTime date = null;
|
||||||
if (currentEnum != null && !currentEnum.isEmpty()) {
|
if (currentEnum != null && !currentEnum.isEmpty()) {
|
||||||
for (Object enumItem : currentEnum) {
|
for (Object enumItem : currentEnum) {
|
||||||
date = (Date) enumItem;
|
date = (OffsetDateTime) enumItem;
|
||||||
fixedValue = dateToString(p, date, iso8601Date, iso8601DateTime);
|
fixedValue = dateToString(p, date, iso8601Date, iso8601DateTime);
|
||||||
fixedEnum.add(fixedValue);
|
fixedEnum.add(fixedValue);
|
||||||
}
|
}
|
||||||
@ -251,15 +249,21 @@ public class PythonClientExperimentalCodegen extends PythonClientCodegen {
|
|||||||
// convert the example if it exists
|
// convert the example if it exists
|
||||||
Object currentExample = p.getExample();
|
Object currentExample = p.getExample();
|
||||||
if (currentExample != null) {
|
if (currentExample != null) {
|
||||||
date = (Date) currentExample;
|
try {
|
||||||
|
date = (OffsetDateTime) currentExample;
|
||||||
|
} catch (ClassCastException e) {
|
||||||
|
date = ((Date) currentExample).toInstant().atOffset(ZoneOffset.UTC);
|
||||||
|
LOGGER.warn("Invalid `date-time` format for value {}", currentExample);
|
||||||
|
}
|
||||||
fixedValue = dateToString(p, date, iso8601Date, iso8601DateTime);
|
fixedValue = dateToString(p, date, iso8601Date, iso8601DateTime);
|
||||||
fixedEnum.add(fixedValue);
|
fixedEnum.add(fixedValue);
|
||||||
p.setExample(fixedValue);
|
p.setExample(fixedValue);
|
||||||
|
LOGGER.warn(fixedValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
// fix defaultObject
|
// fix defaultObject
|
||||||
if (defaultObject != null) {
|
if (defaultObject != null) {
|
||||||
date = (Date) defaultObject;
|
date = (OffsetDateTime) defaultObject;
|
||||||
fixedValue = dateToString(p, date, iso8601Date, iso8601DateTime);
|
fixedValue = dateToString(p, date, iso8601Date, iso8601DateTime);
|
||||||
p.setDefault(fixedValue);
|
p.setDefault(fixedValue);
|
||||||
defaultObject = fixedValue;
|
defaultObject = fixedValue;
|
||||||
@ -377,7 +381,7 @@ public class PythonClientExperimentalCodegen extends PythonClientCodegen {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Override with special post-processing for all models.
|
* Override with special post-processing for all models.
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings({"static-method", "unchecked"})
|
@SuppressWarnings({"static-method", "unchecked"})
|
||||||
public Map<String, Object> postProcessAllModels(Map<String, Object> objs) {
|
public Map<String, Object> postProcessAllModels(Map<String, Object> objs) {
|
||||||
// loop through all models and delete ones where type!=object and the model has no validations and enums
|
// loop through all models and delete ones where type!=object and the model has no validations and enums
|
||||||
@ -905,7 +909,7 @@ public class PythonClientExperimentalCodegen extends PythonClientCodegen {
|
|||||||
* Primitive types in the OAS specification are implemented in Python using the corresponding
|
* Primitive types in the OAS specification are implemented in Python using the corresponding
|
||||||
* Python primitive types.
|
* Python primitive types.
|
||||||
* Composed types (e.g. allAll, oneOf, anyOf) are represented in Python using list of types.
|
* Composed types (e.g. allAll, oneOf, anyOf) are represented in Python using list of types.
|
||||||
*
|
*
|
||||||
* The caller should set the prefix and suffix arguments to empty string, except when
|
* The caller should set the prefix and suffix arguments to empty string, except when
|
||||||
* getTypeString invokes itself recursively. A non-empty prefix/suffix may be specified
|
* getTypeString invokes itself recursively. A non-empty prefix/suffix may be specified
|
||||||
* to wrap the return value in a python dict, list or tuple.
|
* to wrap the return value in a python dict, list or tuple.
|
||||||
@ -913,7 +917,7 @@ public class PythonClientExperimentalCodegen extends PythonClientCodegen {
|
|||||||
* Examples:
|
* Examples:
|
||||||
* - "bool, date, float" The data must be a bool, date or float.
|
* - "bool, date, float" The data must be a bool, date or float.
|
||||||
* - "[bool, date]" The data must be an array, and the array items must be a bool or date.
|
* - "[bool, date]" The data must be an array, and the array items must be a bool or date.
|
||||||
*
|
*
|
||||||
* @param p The OAS schema.
|
* @param p The OAS schema.
|
||||||
* @param prefix prepended to the returned value.
|
* @param prefix prepended to the returned value.
|
||||||
* @param suffix appended to the returned value.
|
* @param suffix appended to the returned value.
|
||||||
@ -922,7 +926,6 @@ public class PythonClientExperimentalCodegen extends PythonClientCodegen {
|
|||||||
* @return a comma-separated string representation of the Python types
|
* @return a comma-separated string representation of the Python types
|
||||||
*/
|
*/
|
||||||
private String getTypeString(Schema p, String prefix, String suffix, List<String> referencedModelNames) {
|
private String getTypeString(Schema p, String prefix, String suffix, List<String> referencedModelNames) {
|
||||||
// this is used to set dataType, which defines a python tuple of classes
|
|
||||||
String fullSuffix = suffix;
|
String fullSuffix = suffix;
|
||||||
if (")".equals(suffix)) {
|
if (")".equals(suffix)) {
|
||||||
fullSuffix = "," + suffix;
|
fullSuffix = "," + suffix;
|
||||||
@ -968,7 +971,7 @@ public class PythonClientExperimentalCodegen extends PythonClientCodegen {
|
|||||||
} else {
|
} else {
|
||||||
return prefix + getTypeString(inner, "[", "]", referencedModelNames) + fullSuffix;
|
return prefix + getTypeString(inner, "[", "]", referencedModelNames) + fullSuffix;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (ModelUtils.isFileSchema(p)) {
|
if (ModelUtils.isFileSchema(p)) {
|
||||||
return prefix + "file_type" + fullSuffix;
|
return prefix + "file_type" + fullSuffix;
|
||||||
}
|
}
|
||||||
|
@ -21,8 +21,10 @@ import io.swagger.v3.oas.models.OpenAPI;
|
|||||||
import io.swagger.v3.oas.models.Operation;
|
import io.swagger.v3.oas.models.Operation;
|
||||||
import io.swagger.v3.oas.models.media.*;
|
import io.swagger.v3.oas.models.media.*;
|
||||||
import io.swagger.v3.parser.util.SchemaTypeUtil;
|
import io.swagger.v3.parser.util.SchemaTypeUtil;
|
||||||
|
import java.time.OffsetDateTime;
|
||||||
import org.openapitools.codegen.*;
|
import org.openapitools.codegen.*;
|
||||||
import org.openapitools.codegen.languages.PythonClientExperimentalCodegen;
|
import org.openapitools.codegen.languages.PythonClientExperimentalCodegen;
|
||||||
|
import org.openapitools.codegen.utils.ModelUtils;
|
||||||
import org.testng.Assert;
|
import org.testng.Assert;
|
||||||
import org.testng.annotations.Test;
|
import org.testng.annotations.Test;
|
||||||
|
|
||||||
@ -293,4 +295,14 @@ public class PythonClientExperimentalTest {
|
|||||||
Assert.assertEquals(cm.imports.size(), 0);
|
Assert.assertEquals(cm.imports.size(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test(description = "parse date and date-time example value")
|
||||||
|
public void parseDateAndDateTimeExamplesTest() {
|
||||||
|
final OpenAPI openAPI = TestUtils.parseFlattenSpec("src/test/resources/3_0/python-experimental/petstore-with-fake-endpoints-models-for-testing-with-http-signature.yaml");
|
||||||
|
final DefaultCodegen codegen = new PythonClientExperimentalCodegen();
|
||||||
|
|
||||||
|
Schema modelSchema = ModelUtils.getSchema(openAPI, "DateTimeTest");
|
||||||
|
String defaultValue = codegen.toDefaultValue(modelSchema);
|
||||||
|
Assert.assertEquals(defaultValue, "dateutil_parser('2010-01-01T10:10:10.000111+01:00')");
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -758,6 +758,8 @@ paths:
|
|||||||
description: None
|
description: None
|
||||||
type: string
|
type: string
|
||||||
format: date-time
|
format: date-time
|
||||||
|
default: '2010-02-01T10:20:10.11111+01:00'
|
||||||
|
example: '2020-02-02T20:20:20.22222Z'
|
||||||
password:
|
password:
|
||||||
description: None
|
description: None
|
||||||
type: string
|
type: string
|
||||||
@ -1202,6 +1204,7 @@ components:
|
|||||||
shipDate:
|
shipDate:
|
||||||
type: string
|
type: string
|
||||||
format: date-time
|
format: date-time
|
||||||
|
example: '2020-02-02T20:20:20.000222Z'
|
||||||
status:
|
status:
|
||||||
type: string
|
type: string
|
||||||
description: Order Status
|
description: Order Status
|
||||||
@ -1443,7 +1446,7 @@ components:
|
|||||||
maximum: 543.2
|
maximum: 543.2
|
||||||
minimum: 32.1
|
minimum: 32.1
|
||||||
type: number
|
type: number
|
||||||
multipleOf: 32.5
|
multipleOf: 32.5
|
||||||
float:
|
float:
|
||||||
type: number
|
type: number
|
||||||
format: float
|
format: float
|
||||||
@ -1466,9 +1469,11 @@ components:
|
|||||||
date:
|
date:
|
||||||
type: string
|
type: string
|
||||||
format: date
|
format: date
|
||||||
|
example: '2020-02-02'
|
||||||
dateTime:
|
dateTime:
|
||||||
type: string
|
type: string
|
||||||
format: date-time
|
format: date-time
|
||||||
|
example: '2007-12-03T10:15:30+01:00'
|
||||||
uuid:
|
uuid:
|
||||||
type: string
|
type: string
|
||||||
format: uuid
|
format: uuid
|
||||||
@ -1969,7 +1974,7 @@ components:
|
|||||||
# Here the additional properties are specified using a referenced schema.
|
# Here the additional properties are specified using a referenced schema.
|
||||||
# This is just to validate the generated code works when using $ref
|
# This is just to validate the generated code works when using $ref
|
||||||
# under 'additionalProperties'.
|
# under 'additionalProperties'.
|
||||||
$ref: '#/components/schemas/fruit'
|
$ref: '#/components/schemas/fruit'
|
||||||
Shape:
|
Shape:
|
||||||
oneOf:
|
oneOf:
|
||||||
- $ref: '#/components/schemas/Triangle'
|
- $ref: '#/components/schemas/Triangle'
|
||||||
@ -2069,3 +2074,8 @@ components:
|
|||||||
properties:
|
properties:
|
||||||
name:
|
name:
|
||||||
type: string
|
type: string
|
||||||
|
DateTimeTest:
|
||||||
|
type: string
|
||||||
|
default: '2010-01-01T10:10:10.000111+01:00'
|
||||||
|
example: '2010-01-01T10:10:10.000111+01:00'
|
||||||
|
format: date-time
|
||||||
|
@ -14,7 +14,7 @@ Name | Type | Description | Notes
|
|||||||
**string** | **str** | None | [optional]
|
**string** | **str** | None | [optional]
|
||||||
**binary** | **file_type** | None | [optional]
|
**binary** | **file_type** | None | [optional]
|
||||||
**date** | **date** | None | [optional]
|
**date** | **date** | None | [optional]
|
||||||
**date_time** | **datetime** | None | [optional]
|
**date_time** | **datetime** | None | [optional] if omitted the server will use the default value of dateutil_parser('2010-02-01T10:20:10.11111+01:00')
|
||||||
**password** | **str** | None | [optional]
|
**password** | **str** | None | [optional]
|
||||||
**callback** | **str** | None | [optional]
|
**callback** | **str** | None | [optional]
|
||||||
|
|
||||||
|
@ -210,7 +210,7 @@ class InlineObject3(ModelNormal):
|
|||||||
string (str): None. [optional] # noqa: E501
|
string (str): None. [optional] # noqa: E501
|
||||||
binary (file_type): None. [optional] # noqa: E501
|
binary (file_type): None. [optional] # noqa: E501
|
||||||
date (date): None. [optional] # noqa: E501
|
date (date): None. [optional] # noqa: E501
|
||||||
date_time (datetime): None. [optional] # noqa: E501
|
date_time (datetime): None. [optional] if omitted the server will use the default value of dateutil_parser('2010-02-01T10:20:10.11111+01:00') # noqa: E501
|
||||||
password (str): None. [optional] # noqa: E501
|
password (str): None. [optional] # noqa: E501
|
||||||
callback (str): None. [optional] # noqa: E501
|
callback (str): None. [optional] # noqa: E501
|
||||||
"""
|
"""
|
||||||
|
Loading…
x
Reference in New Issue
Block a user