Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
6 changes: 6 additions & 0 deletions bin/configs/typescript-fetch-date-library-date.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
generatorName: typescript-fetch
outputDir: samples/client/petstore/typescript-fetch/builds/date-library-date
inputSpec: modules/openapi-generator/src/test/resources/3_0/typescript-fetch/date-handling.yaml
templateDir: modules/openapi-generator/src/main/resources/typescript-fetch
additionalProperties:
dateLibrary: date
6 changes: 6 additions & 0 deletions bin/configs/typescript-fetch-date-library-string.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
generatorName: typescript-fetch
outputDir: samples/client/petstore/typescript-fetch/builds/date-library-string
inputSpec: modules/openapi-generator/src/test/resources/3_0/typescript-fetch/date-handling.yaml
templateDir: modules/openapi-generator/src/main/resources/typescript-fetch
additionalProperties:
dateLibrary: string
1 change: 1 addition & 0 deletions docs/generators/typescript-fetch.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl
| Option | Description | Values | Default |
| ------ | ----------- | ------ | ------- |
|allowUnicodeIdentifiers|boolean, toggles whether unicode identifiers are allowed in names or not, default is false| |false|
|dateLibrary|Option. Date library to use.|<dl><dt>**date**</dt><dd>Native Date. `format: date` and `format: date-time` are both mapped to Date and (de)serialized by the runtime.</dd><dt>**string**</dt><dd>Plain string. Values are passed through untouched, leaving date handling to the consumer.</dd></dl>|date|
|disallowAdditionalPropertiesIfNotPresent|If false, the 'additionalProperties' implementation (set to true by default) is compliant with the OAS and JSON schema specifications. If true (default), keep the old (incorrect) behaviour that 'additionalProperties' is set to false by default.|<dl><dt>**false**</dt><dd>The 'additionalProperties' implementation is compliant with the OAS and JSON schema specifications.</dd><dt>**true**</dt><dd>Keep the old (incorrect) behaviour that 'additionalProperties' is set to false by default.</dd></dl>|true|
|ensureUniqueParams|Whether to ensure parameter names are unique in an operation (rename parameters that are not).| |true|
|enumNameSuffix|Suffix that will be appended to all enum names.| |Enum|
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@
import org.openapitools.codegen.model.OperationsMap;
import org.openapitools.codegen.templating.mustache.IndentedLambda;
import org.openapitools.codegen.utils.ModelUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.util.*;
Expand All @@ -52,11 +54,17 @@
* <p>Mustache templates are located in {@code src/main/resources/typescript-fetch/}.
*/
public class TypeScriptFetchClientCodegen extends AbstractTypeScriptClientCodegen {
private final Logger LOGGER = LoggerFactory.getLogger(TypeScriptFetchClientCodegen.class);

public static final String NPM_REPOSITORY = "npmRepository";
public static final String WITH_INTERFACES = "withInterfaces";
public static final String USE_SINGLE_REQUEST_PARAMETER = "useSingleRequestParameter";
public static final String PREFIX_PARAMETER_INTERFACES = "prefixParameterInterfaces";
public static final String WITHOUT_RUNTIME_CHECKS = "withoutRuntimeChecks";
public static final String DATE_LIBRARY = "dateLibrary";
public static final String DATE_LIBRARY_DESC = "Option. Date library to use.";
public static final String DATE_LIBRARY_DATE = "date";
public static final String DATE_LIBRARY_STRING = "string";
public static final String STRING_ENUMS = "stringEnums";
public static final String STRING_ENUMS_DESC = "Generate string enums instead of objects for enum values.";
public static final String IMPORT_FILE_EXTENSION_SWITCH = "importFileExtension";
Expand All @@ -79,6 +87,7 @@ public class TypeScriptFetchClientCodegen extends AbstractTypeScriptClientCodege
protected boolean addedApiIndex = false;
protected boolean addedModelIndex = false;
protected boolean withoutRuntimeChecks = false;
protected String dateLibrary = DATE_LIBRARY_DATE;
protected boolean stringEnums = false;
protected String fileNaming = PASCAL_CASE;
protected String apiDocPath = "docs";
Expand All @@ -96,6 +105,7 @@ public class TypeScriptFetchClientCodegen extends AbstractTypeScriptClientCodege
private static final String X_OPERATION_RETURN_PASSTHROUGH = "x-operationReturnPassthrough";
private static final String X_KEEP_AS_JS_OBJECT = "x-keepAsJSObject";
private static final String X_TYPESCRIPT_FETCH_API_EXAMPLE = "x-typescriptFetchApiExample";
private static final String X_HAS_DATE_VARS = "x-hasDateVars";
private static final String BLOB_API_EXAMPLE = "new Blob(['example file content'], { type: 'application/octet-stream' })";

protected boolean sagasAndRecords = false;
Expand Down Expand Up @@ -134,6 +144,13 @@ public TypeScriptFetchClientCodegen() {
this.cliOptions.add(new CliOption(CodegenConstants.USE_SINGLE_REQUEST_PARAMETER, CodegenConstants.USE_SINGLE_REQUEST_PARAMETER_DESC, SchemaTypeUtil.BOOLEAN_TYPE).defaultValue(Boolean.TRUE.toString()));
this.cliOptions.add(new CliOption(PREFIX_PARAMETER_INTERFACES, "Setting this property to true will generate parameter interface declarations prefixed with API class name to avoid name conflicts.", SchemaTypeUtil.BOOLEAN_TYPE).defaultValue(Boolean.FALSE.toString()));
this.cliOptions.add(new CliOption(WITHOUT_RUNTIME_CHECKS, "Setting this property to true will remove any runtime checks on the request and response payloads. Payloads will be casted to their expected types.", SchemaTypeUtil.BOOLEAN_TYPE).defaultValue(Boolean.FALSE.toString()));

CliOption dateLibraryOption = new CliOption(DATE_LIBRARY, DATE_LIBRARY_DESC).defaultValue(this.getDateLibrary());
Map<String, String> dateOptions = new HashMap<>();
dateOptions.put(DATE_LIBRARY_DATE, "Native Date. `format: date` and `format: date-time` are both mapped to Date and (de)serialized by the runtime.");
dateOptions.put(DATE_LIBRARY_STRING, "Plain string. Values are passed through untouched, leaving date handling to the consumer.");
dateLibraryOption.setEnum(dateOptions);
this.cliOptions.add(dateLibraryOption);
this.cliOptions.add(new CliOption(SAGAS_AND_RECORDS, "Setting this property to true will generate additional files for use with redux-saga and immutablejs.", SchemaTypeUtil.BOOLEAN_TYPE).defaultValue(Boolean.FALSE.toString()));
this.cliOptions.add(new CliOption(STRING_ENUMS, STRING_ENUMS_DESC, SchemaTypeUtil.BOOLEAN_TYPE).defaultValue(Boolean.FALSE.toString()));
this.cliOptions.add(new CliOption(IMPORT_FILE_EXTENSION_SWITCH, IMPORT_FILE_EXTENSION_SWITCH_DESC).defaultValue(""));
Expand Down Expand Up @@ -192,6 +209,14 @@ public void setWithoutRuntimeChecks(Boolean withoutRuntimeChecks) {
this.withoutRuntimeChecks = withoutRuntimeChecks;
}

public String getDateLibrary() {
return this.dateLibrary;
}

public void setDateLibrary(String dateLibrary) {
this.dateLibrary = dateLibrary;
}

public Boolean getStringEnums() {
return this.stringEnums;
}
Expand Down Expand Up @@ -306,11 +331,34 @@ public void processOpts() {
this.setFileNaming(additionalProperties.get(FILE_NAMING).toString());
}

if (additionalProperties.containsKey(DATE_LIBRARY)) {
this.setDateLibrary(additionalProperties.get(DATE_LIBRARY).toString());
}

if (!withoutRuntimeChecks) {
this.modelTemplateFiles.put("models.mustache", ".ts");
}

// `date` needs the model (de)serialization to convert with, which
// withoutRuntimeChecks removes: the raw string would just be cast to Date.
if (withoutRuntimeChecks && DATE_LIBRARY_DATE.equals(this.dateLibrary)) {
if (additionalProperties.containsKey(DATE_LIBRARY)) {
LOGGER.warn("{}={} is not compatible with {}=true; falling back to {}={}.",
DATE_LIBRARY, DATE_LIBRARY_DATE, WITHOUT_RUNTIME_CHECKS, DATE_LIBRARY, DATE_LIBRARY_STRING);
}
this.dateLibrary = DATE_LIBRARY_STRING;
}

if (DATE_LIBRARY_DATE.equals(this.dateLibrary)) {
typeMapping.put("date", "Date");
typeMapping.put("DateTime", "Date");
} else {
typeMapping.put("date", "string");
typeMapping.put("DateTime", "string");
}
additionalProperties.put(DATE_LIBRARY, this.dateLibrary);
// Mustache cannot compare strings, so expose the selected library as a flag.
additionalProperties.put("isDateLibraryDate", DATE_LIBRARY_DATE.equals(this.dateLibrary));

if (additionalProperties.containsKey(SAGAS_AND_RECORDS)) {
this.setSagasAndRecords(convertPropertyToBoolean(SAGAS_AND_RECORDS));
Expand Down Expand Up @@ -407,6 +455,12 @@ public ModelsMap postProcessModels(ModelsMap objs) {
ExtendedCodegenModel cm = (ExtendedCodegenModel) mo.getModel();
cm.imports = new TreeSet<>(cm.imports);
this.processCodeGenModel(cm);
// Mirrors the branches in modelGeneric.mustache that call the date helpers, so a
// model without dates does not import them.
cm.vendorExtensions.put(X_HAS_DATE_VARS, cm.vars.stream()
.filter(ExtendedCodegenProperty.class::isInstance)
.map(ExtendedCodegenProperty.class::cast)
.anyMatch(v -> v.isPrimitiveType && !v.isArray && (v.isDateType() || v.isDateTimeType())));
}

// Add supporting file only if we plan to generate files in /models
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -272,9 +272,13 @@ export class {{classname}} extends runtime.BaseAPI {
{{^isArray}}
if (requestParameters['{{paramName}}'] != null) {
{{#isDateTimeType}}
formParams.append('{{baseName}}', (requestParameters['{{paramName}}'] as any).toISOString());
formParams.append('{{baseName}}', runtime.serializeDateTime(requestParameters['{{paramName}}'] as any));
{{/isDateTimeType}}
{{^isDateTimeType}}
{{#isDateType}}
formParams.append('{{baseName}}', runtime.serializeDate(requestParameters['{{paramName}}'] as any));
{{/isDateType}}
{{^isDateType}}
{{#isPrimitiveType}}
formParams.append('{{baseName}}', requestParameters['{{paramName}}'] as any);
{{/isPrimitiveType}}
Expand All @@ -295,6 +299,7 @@ export class {{classname}} extends runtime.BaseAPI {
{{/withoutRuntimeChecks}}
{{/isEnumRef}}
{{/isPrimitiveType}}
{{/isDateType}}
{{/isDateTimeType}}
}

Expand All @@ -306,15 +311,15 @@ export class {{classname}} extends runtime.BaseAPI {
{{#pathParams}}
{{#isDateTimeType}}
if (requestParameters['{{paramName}}'] instanceof Date) {
urlPath = urlPath.replace({{=<< >>=}}'{<<baseName>>}'<<={{ }}=>>, encodeURIComponent(requestParameters['{{paramName}}'].toISOString()));
urlPath = urlPath.replace({{=<< >>=}}'{<<baseName>>}'<<={{ }}=>>, encodeURIComponent(runtime.serializeDateTime(requestParameters['{{paramName}}'])));
} else {
urlPath = urlPath.replace({{=<< >>=}}'{<<baseName>>}'<<={{ }}=>>, encodeURIComponent(String(requestParameters['{{paramName}}'])));
}
{{/isDateTimeType}}
{{^isDateTimeType}}
{{#isDateType}}
if (requestParameters['{{paramName}}'] instanceof Date) {
urlPath = urlPath.replace({{=<< >>=}}'{<<baseName>>}'<<={{ }}=>>, encodeURIComponent(requestParameters['{{paramName}}'].toISOString().substring(0,10)));
urlPath = urlPath.replace({{=<< >>=}}'{<<baseName>>}'<<={{ }}=>>, encodeURIComponent(runtime.serializeDate(requestParameters['{{paramName}}'])));
} else {
urlPath = urlPath.replace({{=<< >>=}}'{<<baseName>>}'<<={{ }}=>>, encodeURIComponent(String(requestParameters['{{paramName}}'])));
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
{{! Assign query parameters based on their type }}
{{#isDateTimeType}}
queryParameters['{{baseName}}'] = (requestParameters['{{paramName}}'] as any).toISOString();
queryParameters['{{baseName}}'] = runtime.serializeDateTime(requestParameters['{{paramName}}'] as any);
{{/isDateTimeType}}
{{^isDateTimeType}}
{{#isDateType}}
queryParameters['{{baseName}}'] = (requestParameters['{{paramName}}'] as any).toISOString().substring(0,10);
queryParameters['{{baseName}}'] = runtime.serializeDate(requestParameters['{{paramName}}'] as any);
{{/isDateType}}
{{^isDateType}}
queryParameters['{{baseName}}'] = requestParameters['{{paramName}}'];
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { mapValues } from '../runtime{{importFileExtension}}';
import { mapValues{{#isDateLibraryDate}}{{#vendorExtensions.x-hasDateVars}}, parseDate, parseDateTime, serializeDate, serializeDateTime{{/vendorExtensions.x-hasDateVars}}{{/isDateLibraryDate}} } from '../runtime{{importFileExtension}}';
{{#hasImports}}
{{#tsImports}}
import type { {{{classname}}} } from './{{filename}}{{importFileExtension}}';
Expand Down Expand Up @@ -98,10 +98,10 @@ export function {{classname}}FromJSONTyped(json: any, ignoreDiscriminator: boole
{{/isArray}}
{{^isArray}}
{{#isDateType}}
'{{name}}': {{^required}}{{#isNullable}}json['{{baseName}}'] === undefined ? undefined : json['{{baseName}}'] === null ? null : {{/isNullable}}{{^isNullable}}json['{{baseName}}'] == null ? undefined : {{/isNullable}}{{/required}}({{#required}}{{#isNullable}}json['{{baseName}}'] == null ? null : {{/isNullable}}{{/required}}new Date(json['{{baseName}}'])),
'{{name}}': {{^required}}{{#isNullable}}json['{{baseName}}'] === undefined ? undefined : json['{{baseName}}'] === null ? null : {{/isNullable}}{{^isNullable}}json['{{baseName}}'] == null ? undefined : {{/isNullable}}{{/required}}({{#required}}{{#isNullable}}json['{{baseName}}'] == null ? null : {{/isNullable}}{{/required}}parseDate(json['{{baseName}}'])),
{{/isDateType}}
{{#isDateTimeType}}
'{{name}}': {{^required}}{{#isNullable}}json['{{baseName}}'] === undefined ? undefined : json['{{baseName}}'] === null ? null : {{/isNullable}}{{^isNullable}}json['{{baseName}}'] == null ? undefined : {{/isNullable}}{{/required}}({{#required}}{{#isNullable}}json['{{baseName}}'] == null ? null : {{/isNullable}}{{/required}}new Date(json['{{baseName}}'])),
'{{name}}': {{^required}}{{#isNullable}}json['{{baseName}}'] === undefined ? undefined : json['{{baseName}}'] === null ? null : {{/isNullable}}{{^isNullable}}json['{{baseName}}'] == null ? undefined : {{/isNullable}}{{/required}}({{#required}}{{#isNullable}}json['{{baseName}}'] == null ? null : {{/isNullable}}{{/required}}parseDateTime(json['{{baseName}}'])),
{{/isDateTimeType}}
{{^isDateType}}
{{^isDateTimeType}}
Expand Down Expand Up @@ -173,10 +173,10 @@ export function {{classname}}ToJSONTyped(value?: {{#hasReadOnly}}Omit<{{classnam
{{^isReadOnly}}
{{#isPrimitiveType}}
{{#isDateType}}
'{{baseName}}': {{^required}}value['{{name}}'] == null ? value['{{name}}'] : {{/required}}{{#isNullable}}{{#required}}value['{{name}}'] == null ? value['{{name}}'] : {{/required}}{{/isNullable}}value['{{name}}'].toISOString().substring(0,10),
'{{baseName}}': {{^required}}value['{{name}}'] == null ? value['{{name}}'] : {{/required}}{{#isNullable}}{{#required}}value['{{name}}'] == null ? value['{{name}}'] : {{/required}}{{/isNullable}}serializeDate(value['{{name}}']),
{{/isDateType}}
{{#isDateTimeType}}
'{{baseName}}': {{^required}}value['{{name}}'] == null ? value['{{name}}'] : {{/required}}{{#isNullable}}{{#required}}value['{{name}}'] == null ? value['{{name}}'] : {{/required}}{{/isNullable}}value['{{name}}'].toISOString(),
'{{baseName}}': {{^required}}value['{{name}}'] == null ? value['{{name}}'] : {{/required}}{{#isNullable}}{{#required}}value['{{name}}'] == null ? value['{{name}}'] : {{/required}}{{/isNullable}}serializeDateTime(value['{{name}}']),
{{/isDateTimeType}}
{{#isArray}}
'{{baseName}}': {{#uniqueItems}}{{^required}}value['{{name}}'] == null ? undefined : {{/required}}{{#required}}{{#isNullable}}value['{{name}}'] == null ? null : {{/isNullable}}{{/required}}Array.from(value['{{name}}'] as Set<any>){{/uniqueItems}}{{^uniqueItems}}value['{{name}}']{{/uniqueItems}},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
{{#isDateLibraryDate}}
import { parseDate, parseDateTime, serializeDate, serializeDateTime } from '../runtime{{importFileExtension}}';
{{/isDateLibraryDate}}
{{#hasImports}}
{{#oneOfArrays}}
import type { {{{.}}} } from './{{.}}{{importFileExtension}}';
Expand Down Expand Up @@ -70,14 +73,14 @@ export function {{classname}}FromJSONTyped(json: any, ignoreDiscriminator: boole
{{#isDateType}}
if (Array.isArray(json)) {
if (json.every(item => !(isNaN(new Date(item).getTime())))) {
return json.map(value => new Date(value));
return json.map(value => parseDate(value));
}
}
{{/isDateType}}
{{#isDateTimeType}}
if (Array.isArray(json)) {
if (json.every(item => !(isNaN(new Date(item).getTime())))) {
return json.map(value => new Date(value));
return json.map(value => parseDateTime(value));
}
}
{{/isDateTimeType}}
Expand Down Expand Up @@ -116,13 +119,13 @@ export function {{classname}}FromJSONTyped(json: any, ignoreDiscriminator: boole
{{^isArray}}
{{#isDateType}}
if (!(isNaN(new Date(json).getTime()))) {
return {{^required}}json == null ? undefined : {{/required}}({{#required}}{{#isNullable}}json == null ? null : {{/isNullable}}{{/required}}new Date(json));
return {{^required}}json == null ? undefined : {{/required}}({{#required}}{{#isNullable}}json == null ? null : {{/isNullable}}{{/required}}parseDate(json));
}
{{/isDateType}}
{{^isDateType}}
{{#isDateTimeType}}
if (!(isNaN(new Date(json).getTime()))) {
return {{^required}}json == null ? undefined : {{/required}}({{#required}}{{#isNullable}}json == null ? null : {{/isNullable}}{{/required}}new Date(json));
return {{^required}}json == null ? undefined : {{/required}}({{#required}}{{#isNullable}}json == null ? null : {{/isNullable}}{{/required}}parseDateTime(json));
}
{{/isDateTimeType}}
{{/isDateType}}
Expand Down Expand Up @@ -195,14 +198,14 @@ export function {{classname}}ToJSONTyped(value?: {{classname}} | null, ignoreDis
{{#isDateType}}
if (Array.isArray(value)) {
if (value.every(item => item instanceof Date)) {
return value.map(value => value.toISOString().substring(0,10));
return value.map(value => serializeDate(value));
}
}
{{/isDateType}}
{{#isDateTimeType}}
if (Array.isArray(value)) {
if (value.every(item => item instanceof Date)) {
return value.map(item => item.toISOString());
return value.map(item => serializeDateTime(item));
}
}
{{/isDateTimeType}}
Expand Down Expand Up @@ -241,12 +244,12 @@ export function {{classname}}ToJSONTyped(value?: {{classname}} | null, ignoreDis
{{^isArray}}
{{#isDateType}}
if (value instanceof Date) {
return ((value{{#isNullable}} as any{{/isNullable}}){{^required}}{{#isNullable}}?{{/isNullable}}{{/required}}.toISOString().substring(0,10));
return (serializeDate(value{{#isNullable}} as any{{/isNullable}}));
}
{{/isDateType}}
{{#isDateTimeType}}
if (value instanceof Date) {
return {{^required}}{{#isNullable}}value === null ? null : {{/isNullable}}{{^isNullable}}value == null ? undefined : {{/isNullable}}{{/required}}((value{{#isNullable}} as any{{/isNullable}}){{^required}}{{#isNullable}}?{{/isNullable}}{{/required}}.toISOString());
return {{^required}}{{#isNullable}}value === null ? null : {{/isNullable}}{{^isNullable}}value == null ? undefined : {{/isNullable}}{{/required}}(serializeDateTime(value{{#isNullable}} as any{{/isNullable}}));
}
{{/isDateTimeType}}
{{#isNumeric}}
Expand Down
Loading