Skip to content

Commit

Permalink
[python] Fix date-time parsing (#6458)
Browse files Browse the repository at this point in the history
  • Loading branch information
jirikuncar authored Jun 4, 2020
1 parent e2e3405 commit d07f459
Show file tree
Hide file tree
Showing 6 changed files with 49 additions and 24 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -722,7 +722,7 @@ private String toExampleValueRecursive(Schema schema, List<String> included_sche
}

// correct "&#39;"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();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,9 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.io.File;
import java.util.*;
import java.util.regex.Pattern;
Expand Down Expand Up @@ -196,15 +197,15 @@ public String getName() {
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
if (!(ModelUtils.isDateSchema(p) || ModelUtils.isDateTimeSchema(p))) {
throw new RuntimeException("passed schema must be of type Date or DateTime");
}
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) + "')";
}

/**
Expand All @@ -228,20 +229,17 @@ public String toDefaultValue(Schema p) {
}

// convert datetime and date enums if they exist
DateFormat iso8601Date = new SimpleDateFormat("yyyy-MM-dd", Locale.ROOT);
DateFormat iso8601DateTime = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX", Locale.ROOT);
TimeZone utc = TimeZone.getTimeZone("UTC");
iso8601Date.setTimeZone(utc);
iso8601DateTime.setTimeZone(utc);
DateTimeFormatter iso8601Date = DateTimeFormatter.ISO_DATE;
DateTimeFormatter iso8601DateTime = DateTimeFormatter.ISO_DATE_TIME;

if (ModelUtils.isDateSchema(p) || ModelUtils.isDateTimeSchema(p)) {
List<Object> currentEnum = p.getEnum();
List<String> fixedEnum = new ArrayList<String>();
String fixedValue = null;
Date date = null;
OffsetDateTime date = null;
if (currentEnum != null && !currentEnum.isEmpty()) {
for (Object enumItem : currentEnum) {
date = (Date) enumItem;
date = (OffsetDateTime) enumItem;
fixedValue = dateToString(p, date, iso8601Date, iso8601DateTime);
fixedEnum.add(fixedValue);
}
Expand All @@ -251,15 +249,21 @@ public String toDefaultValue(Schema p) {
// convert the example if it exists
Object currentExample = p.getExample();
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);
fixedEnum.add(fixedValue);
p.setExample(fixedValue);
LOGGER.warn(fixedValue);
}

// fix defaultObject
if (defaultObject != null) {
date = (Date) defaultObject;
date = (OffsetDateTime) defaultObject;
fixedValue = dateToString(p, date, iso8601Date, iso8601DateTime);
p.setDefault(fixedValue);
defaultObject = fixedValue;
Expand Down Expand Up @@ -377,7 +381,7 @@ private void fixModelImports(Set<String> imports) {

/**
* Override with special post-processing for all models.
*/
*/
@SuppressWarnings({"static-method", "unchecked"})
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
Expand Down Expand Up @@ -905,15 +909,15 @@ public String getSimpleTypeDeclaration(Schema schema) {
* Primitive types in the OAS specification are implemented in Python using the corresponding
* Python primitive 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
* getTypeString invokes itself recursively. A non-empty prefix/suffix may be specified
* to wrap the return value in a python dict, list or tuple.
*
* Examples:
* - "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.
*
*
* @param p The OAS schema.
* @param prefix prepended to the returned value.
* @param suffix appended to the returned value.
Expand All @@ -922,7 +926,6 @@ public String getSimpleTypeDeclaration(Schema schema) {
* @return a comma-separated string representation of the Python types
*/
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;
if (")".equals(suffix)) {
fullSuffix = "," + suffix;
Expand Down Expand Up @@ -968,7 +971,7 @@ private String getTypeString(Schema p, String prefix, String suffix, List<String
} else {
return prefix + getTypeString(inner, "[", "]", referencedModelNames) + fullSuffix;
}
}
}
if (ModelUtils.isFileSchema(p)) {
return prefix + "file_type" + fullSuffix;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,10 @@
import io.swagger.v3.oas.models.Operation;
import io.swagger.v3.oas.models.media.*;
import io.swagger.v3.parser.util.SchemaTypeUtil;
import java.time.OffsetDateTime;
import org.openapitools.codegen.*;
import org.openapitools.codegen.languages.PythonClientExperimentalCodegen;
import org.openapitools.codegen.utils.ModelUtils;
import org.testng.Assert;
import org.testng.annotations.Test;

Expand Down Expand Up @@ -293,4 +295,14 @@ public void mapModelTest() {
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')");
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -758,6 +758,8 @@ paths:
description: None
type: string
format: date-time
default: '2010-02-01T10:20:10.11111+01:00'
example: '2020-02-02T20:20:20.22222Z'
password:
description: None
type: string
Expand Down Expand Up @@ -1202,6 +1204,7 @@ components:
shipDate:
type: string
format: date-time
example: '2020-02-02T20:20:20.000222Z'
status:
type: string
description: Order Status
Expand Down Expand Up @@ -1443,7 +1446,7 @@ components:
maximum: 543.2
minimum: 32.1
type: number
multipleOf: 32.5
multipleOf: 32.5
float:
type: number
format: float
Expand All @@ -1466,9 +1469,11 @@ components:
date:
type: string
format: date
example: '2020-02-02'
dateTime:
type: string
format: date-time
example: '2007-12-03T10:15:30+01:00'
uuid:
type: string
format: uuid
Expand Down Expand Up @@ -1969,7 +1974,7 @@ components:
# Here the additional properties are specified using a referenced schema.
# This is just to validate the generated code works when using $ref
# under 'additionalProperties'.
$ref: '#/components/schemas/fruit'
$ref: '#/components/schemas/fruit'
Shape:
oneOf:
- $ref: '#/components/schemas/Triangle'
Expand Down Expand Up @@ -2069,3 +2074,8 @@ components:
properties:
name:
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
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ Name | Type | Description | Notes
**string** | **str** | None | [optional]
**binary** | **file_type** | 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]
**callback** | **str** | None | [optional]

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ def __init__(self, number, double, pattern_without_delimiter, byte, *args, **kwa
string (str): None. [optional] # noqa: E501
binary (file_type): 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
callback (str): None. [optional] # noqa: E501
"""
Expand Down

0 comments on commit d07f459

Please sign in to comment.