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

Sync with upstream master #2

Merged
merged 10 commits into from
Jan 28, 2020
Prev Previous commit
Next Next commit
[typescript-rxjs] performance improvements, bugfix for falsy paramete…
…rs (OpenAPITools#4250)

* perf(typescript-rxjs): remove redundant check when building auth header

* feat(typescript-rxjs): destructure request parameters, add throwIfNullOrUndefined helper, build query object more efficently

* fix(typescript-rxjs): change form checks back from null to undefined

* feat(typescript-rxjs): regenerate samples

* feat(typescript-rxjs): add hasRequiredQueryParams flag for improved query object generation

* feat(typescript-rxjs): remove trailing comma in param destructuring, improve formatting via hasOptionalQueryParams flag

* feat(typescript-rxjs): remove useless generics in BaseAPI

* feat(typescript-rxjs): regenerate samples

* feat(typescript-rxjs): extend CodegenParameter by output.paramNameAlternative and output.paramNameOrAlternative

* refactor(typescript-rxjs): remove obsolete reservedWords RequiredError and exists

* feat(typescript-rxjs): add reservedParamNames list with headers, query and formData, extend param processing

* feat(typescript-rxjs): use paramNameOrAlternative in api template

* refactor(typescript-rxjs): replace paramNameOrAlternative prop with mustache partial

* refactor(typescript-rxjs): reduce branching in configuration's apiKey() and accessToken()

* refactor(typescript-rxjs): remove unused ModelPropertyNaming

* feat(typescript-rxjs): regenerate samples

* feat(typescript-rxjs): remove CodegenParamter's paramNameAlternative, use vendorExtensions instead

* docs(typescript-rxjs): regenerate readme
  • Loading branch information
denyo authored and macjohnny committed Jan 27, 2020
commit 45f26fe0bde2f134c89ca566381f279dae775e7c
3 changes: 0 additions & 3 deletions docs/generators/typescript-rxjs.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,10 +67,8 @@ sidebar_label: typescript-rxjs
<li>HttpMethod</li>
<li>HttpQuery</li>
<li>Middleware</li>
<li>ModelPropertyNaming</li>
<li>RequestArgs</li>
<li>RequestOpts</li>
<li>RequiredError</li>
<li>ResponseArgs</li>
<li>abstract</li>
<li>await</li>
Expand All @@ -90,7 +88,6 @@ sidebar_label: typescript-rxjs
<li>double</li>
<li>else</li>
<li>enum</li>
<li>exists</li>
<li>export</li>
<li>extends</li>
<li>false</li>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,19 +23,20 @@
import org.openapitools.codegen.*;
import org.openapitools.codegen.meta.features.DocumentationFeature;
import org.openapitools.codegen.utils.ModelUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.util.TreeSet;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.util.*;

public class TypeScriptRxjsClientCodegen extends AbstractTypeScriptClientCodegen {
private static final Logger LOGGER = LoggerFactory.getLogger(AbstractTypeScriptClientCodegen.class);

public static final String NPM_REPOSITORY = "npmRepository";
public static final String WITH_INTERFACES = "withInterfaces";

protected String npmRepository = null;
protected Set<String> reservedParamNames = new HashSet<>();

public TypeScriptRxjsClientCodegen() {
super();
Expand All @@ -58,6 +59,11 @@ public TypeScriptRxjsClientCodegen() {

this.cliOptions.add(new CliOption(NPM_REPOSITORY, "Use this property to set an url your private npmRepo in the package.json"));
this.cliOptions.add(new CliOption(WITH_INTERFACES, "Setting this property to true will generate interfaces next to the default class implementations.", SchemaTypeUtil.BOOLEAN_TYPE).defaultValue(Boolean.FALSE.toString()));

// these are used in the api template for more efficient destructuring
this.reservedParamNames.add("headers");
this.reservedParamNames.add("query");
this.reservedParamNames.add("formData");
}

@Override
Expand Down Expand Up @@ -252,38 +258,65 @@ private void updateOperationParameterEnumInformation(Map<String, Object> operati
operations.put("hasEnums", hasEnums);
}

private void setParamNameAlternative(CodegenParameter param, String paramName, String paramNameAlternative) {
if (param.paramName.equals(paramName)) {
param.vendorExtensions.put("paramNameAlternative", paramNameAlternative);
}
}

private void addConditionalImportInformation(Map<String, Object> operations) {
// This method will determine if there are required parameters and if there are list containers
Map<String, Object> _operations = (Map<String, Object>) operations.get("operations");
List<ExtendedCodegenOperation> operationList = (List<ExtendedCodegenOperation>) _operations.get("operation");

boolean hasRequiredParameters = false;
boolean hasRequiredParams = false;
boolean hasListContainers = false;
boolean hasHttpHeaders = false;
boolean hasQueryParams = false;
boolean hasPathParams = false;

for (ExtendedCodegenOperation op : operationList) {
if (op.getHasRequiredParams()) {
hasRequiredParameters = true;
hasRequiredParams = true;
}

for (CodegenParameter param : op.headerParams) {
if (param.isListContainer) {
hasListContainers = true;
break;

for (CodegenParameter p: op.allParams) {
String paramNameAlternative = null;

if(this.reservedParamNames.contains(p.paramName)){
paramNameAlternative = p.paramName + "Alias";
LOGGER.info("param: "+p.paramName+" isReserved ––> "+paramNameAlternative);
}
}
for (CodegenParameter param : op.queryParams) {
if (param.isListContainer && !param.isCollectionFormatMulti) {
hasListContainers = true;
break;
setParamNameAlternative(p, p.paramName, paramNameAlternative);

for (CodegenParameter param : op.headerParams) {
if (param.isListContainer) {
hasListContainers = true;
}
setParamNameAlternative(param, p.paramName, paramNameAlternative);
}
}
for (CodegenParameter param : op.formParams) {
if (param.isListContainer && !param.isCollectionFormatMulti) {
hasListContainers = true;
break;

for (CodegenParameter param : op.queryParams) {
if (param.isListContainer && !param.isCollectionFormatMulti) {
hasListContainers = true;
}
if (param.required) {
op.hasRequiredQueryParams = true;
} else {
op.hasOptionalQueryParams = true;
}
setParamNameAlternative(param, p.paramName, paramNameAlternative);
}

for (CodegenParameter param : op.formParams) {
if (param.isListContainer && !param.isCollectionFormatMulti) {
hasListContainers = true;
}
setParamNameAlternative(param, p.paramName, paramNameAlternative);
}

for (CodegenParameter param : op.pathParams) {
setParamNameAlternative(param, p.paramName, paramNameAlternative);
}
}

Expand All @@ -296,13 +329,9 @@ private void addConditionalImportInformation(Map<String, Object> operations) {
if (op.getHasPathParams()) {
hasPathParams = true;
}

if(hasRequiredParameters && hasListContainers && hasHttpHeaders && hasQueryParams && hasPathParams){
break;
}
}

operations.put("hasRequiredParameters", hasRequiredParameters);
operations.put("hasRequiredParams", hasRequiredParams);
operations.put("hasListContainers", hasListContainers);
operations.put("hasHttpHeaders", hasHttpHeaders);
operations.put("hasQueryParams", hasQueryParams);
Expand All @@ -312,26 +341,25 @@ private void addConditionalImportInformation(Map<String, Object> operations) {
private void addExtraReservedWords() {
this.reservedWords.add("BASE_PATH");
this.reservedWords.add("BaseAPI");
this.reservedWords.add("RequiredError");
this.reservedWords.add("COLLECTION_FORMATS");
this.reservedWords.add("ConfigurationParameters");
this.reservedWords.add("Configuration");
this.reservedWords.add("HttpMethod");
this.reservedWords.add("HttpHeaders");
this.reservedWords.add("HttpQuery");
this.reservedWords.add("HttpBody");
this.reservedWords.add("ModelPropertyNaming");
this.reservedWords.add("RequestArgs");
this.reservedWords.add("RequestOpts");
this.reservedWords.add("ResponseArgs");
this.reservedWords.add("exists");
this.reservedWords.add("Middleware");
this.reservedWords.add("AjaxRequest");
this.reservedWords.add("AjaxResponse");
}

class ExtendedCodegenOperation extends CodegenOperation {
public boolean hasHttpHeaders;
public boolean hasRequiredQueryParams;
public boolean hasOptionalQueryParams;

public ExtendedCodegenOperation(CodegenOperation o) {
super();
Expand Down Expand Up @@ -405,6 +433,8 @@ public ExtendedCodegenOperation(CodegenOperation o) {

// new fields
this.hasHttpHeaders = o.getHasHeaderParams() || o.getHasBodyParam() || o.hasAuthMethods;
this.hasRequiredQueryParams = false; // will be updated within addConditionalImportInformation
this.hasOptionalQueryParams = false; // will be updated within addConditionalImportInformation
}
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// tslint:disable
{{>licenseInfo}}
import { Observable } from 'rxjs';
import { BaseAPI{{#hasHttpHeaders}}, HttpHeaders{{/hasHttpHeaders}}{{#hasQueryParams}}, HttpQuery{{/hasQueryParams}}{{#hasRequiredParameters}}, throwIfRequired{{/hasRequiredParameters}}{{#hasPathParams}}, encodeURI{{/hasPathParams}}{{#hasListContainers}}, COLLECTION_FORMATS{{/hasListContainers}} } from '../runtime';
import { BaseAPI{{#hasHttpHeaders}}, HttpHeaders{{/hasHttpHeaders}}{{#hasQueryParams}}, HttpQuery{{/hasQueryParams}}{{#hasRequiredParams}}, throwIfNullOrUndefined{{/hasRequiredParams}}{{#hasPathParams}}, encodeURI{{/hasPathParams}}{{#hasListContainers}}, COLLECTION_FORMATS{{/hasListContainers}} } from '../runtime';
{{#imports.0}}
import {
{{#imports}}
Expand Down Expand Up @@ -37,11 +37,11 @@ export class {{classname}} extends BaseAPI {
* {{&summary}}
{{/summary}}
*/
{{nickname}} = ({{#allParams.0}}requestParameters: {{operationIdCamelCase}}Request{{/allParams.0}}): Observable<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/returnType}}> => {
{{nickname}} = ({{#allParams.0}}{ {{#allParams}}{{paramName}}{{#vendorExtensions.paramNameAlternative}}: {{vendorExtensions.paramNameAlternative}}{{/vendorExtensions.paramNameAlternative}}{{^-last}}, {{/-last}}{{/allParams}} }: {{operationIdCamelCase}}Request{{/allParams.0}}): Observable<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/returnType}}> => {
{{#hasParams}}
{{#allParams}}
{{#required}}
throwIfRequired(requestParameters, '{{paramName}}', '{{nickname}}');
throwIfNullOrUndefined({{> paramNamePartial}}, '{{nickname}}');
{{/required}}
{{/allParams}}

Expand All @@ -58,15 +58,15 @@ export class {{classname}} extends BaseAPI {
{{/bodyParam}}
{{#headerParams}}
{{#isListContainer}}
...(requestParameters.{{paramName}} && { '{{baseName}}': requestParameters.{{paramName}}.join(COLLECTION_FORMATS['{{collectionFormat}}'])) }),
...({{> paramNamePartial}} != null ? { '{{baseName}}': {{> paramNamePartial}}.join(COLLECTION_FORMATS['{{collectionFormat}}'])) } : undefined),
{{/isListContainer}}
{{^isListContainer}}
...(requestParameters.{{paramName}} && { '{{baseName}}': String(requestParameters.{{paramName}}) }),
...({{> paramNamePartial}} != null ? { '{{baseName}}': String({{> paramNamePartial}}) } : undefined),
{{/isListContainer}}
{{/headerParams}}
{{#authMethods}}
{{#isBasic}}
...(this.configuration.username && this.configuration.password && { Authorization: `Basic ${btoa(this.configuration.username + ':' + this.configuration.password)}` }),
...(this.configuration.username != null && this.configuration.password != null ? { Authorization: `Basic ${btoa(this.configuration.username + ':' + this.configuration.password)}` } : undefined),
{{/isBasic}}
{{#isApiKey}}
{{#isKeyInHeader}}
Expand All @@ -75,77 +75,109 @@ export class {{classname}} extends BaseAPI {
{{/isApiKey}}
{{#isOAuth}}
// oauth required
...(this.configuration.accessToken && {
Authorization: this.configuration.accessToken && (typeof this.configuration.accessToken === 'function'
...(this.configuration.accessToken != null
? { Authorization: typeof this.configuration.accessToken === 'function'
? this.configuration.accessToken('{{name}}', [{{#scopes}}'{{{scope}}}'{{^-last}}, {{/-last}}{{/scopes}}])
: this.configuration.accessToken)
}),
: this.configuration.accessToken }
: undefined
),
{{/isOAuth}}
{{/authMethods}}
};

{{/hasHttpHeaders}}
{{#hasQueryParams}}
const query: HttpQuery = {
{{^hasRequiredQueryParams}}
const query: HttpQuery = {};
{{/hasRequiredQueryParams}}
{{#hasRequiredQueryParams}}
const query: HttpQuery = { // required parameters are used directly since they are already checked by throwIfNullOrUndefined
{{#queryParams}}
{{#required}}
{{#isListContainer}}
{{#isCollectionFormatMulti}}
...(requestParameters.{{paramName}} && { '{{baseName}}': requestParameters.{{paramName}} }),
'{{baseName}}': {{> paramNamePartial}},
{{/isCollectionFormatMulti}}
{{^isCollectionFormatMulti}}
...(requestParameters.{{paramName}} && { '{{baseName}}': requestParameters.{{paramName}}.join(COLLECTION_FORMATS['{{collectionFormat}}']) }),
'{{baseName}}': {{> paramNamePartial}}.join(COLLECTION_FORMATS['{{collectionFormat}}']),
{{/isCollectionFormatMulti}}
{{/isListContainer}}
{{^isListContainer}}
{{#isDateTime}}
...(requestParameters.{{paramName}} && { '{{baseName}}': (requestParameters.{{paramName}} as any).toISOString() }),
'{{baseName}}': ({{> paramNamePartial}} as any).toISOString(),
{{/isDateTime}}
{{^isDateTime}}
{{#isDate}}
...(requestParameters.{{paramName}} && { '{{baseName}}': (requestParameters.{{paramName}} as any).toISOString() }),
'{{baseName}}': ({{> paramNamePartial}} as any).toISOString(),
{{/isDate}}
{{^isDate}}
...(requestParameters.{{paramName}} && { '{{baseName}}': requestParameters.{{paramName}} }),
'{{baseName}}': {{> paramNamePartial}},
{{/isDate}}
{{/isDateTime}}
{{/isListContainer}}
{{/required}}
{{/queryParams}}
{{#authMethods}}
{{#isApiKey}}
{{#isKeyInQuery}}
...(this.configuration.apiKey && { '{{keyParamName}}': this.configuration.apiKey && this.configuration.apiKey('{{keyParamName}}') }), // {{name}} authentication
{{/isKeyInQuery}}
{{/isApiKey}}
{{/authMethods}}
};
{{/hasRequiredQueryParams}}
{{#hasOptionalQueryParams}}

{{#queryParams}}
{{^required}}
{{#isListContainer}}
{{#isCollectionFormatMulti}}
if ({{> paramNamePartial}} != null) { query['{{baseName}}'] = {{> paramNamePartial}}; }
{{/isCollectionFormatMulti}}
{{^isCollectionFormatMulti}}
if ({{> paramNamePartial}} != null) { query['{{baseName}}'] = {{> paramNamePartial}}.join(COLLECTION_FORMATS['{{collectionFormat}}']); }
{{/isCollectionFormatMulti}}
{{/isListContainer}}
{{^isListContainer}}
{{#isDateTime}}
if ({{> paramNamePartial}} != null) { query['{{baseName}}'] = ({{> paramNamePartial}} as any).toISOString(); }
{{/isDateTime}}
{{^isDateTime}}
{{#isDate}}
if ({{> paramNamePartial}} != null) { query['{{baseName}}'] = ({{> paramNamePartial}} as any).toISOString(); }
{{/isDate}}
{{^isDate}}
if ({{> paramNamePartial}} != null) { query['{{baseName}}'] = {{> paramNamePartial}}; }
{{/isDate}}
{{/isDateTime}}
{{/isListContainer}}
{{/required}}
{{/queryParams}}
{{/hasOptionalQueryParams}}
{{#authMethods}}
{{#isApiKey}}
{{#isKeyInQuery}}
if (this.configuration.apiKey != null) { query['{{keyParamName}}'] = this.configuration.apiKey('{{keyParamName}}'); } // {{name}} authentication
{{/isKeyInQuery}}
{{/isApiKey}}
{{/authMethods}}

{{/hasQueryParams}}
{{#hasFormParams}}
const formData = new FormData();
{{/hasFormParams}}
{{#formParams}}
{{#isListContainer}}
if (requestParameters.{{paramName}}) {
if ({{> paramNamePartial}} !== undefined) {
{{#isCollectionFormatMulti}}
requestParameters.{{paramName}}.forEach((element) => {
formData.append('{{baseName}}', element as any);
})
{{> paramNamePartial}}.forEach((element) => formData.append('{{baseName}}', element as any))
{{/isCollectionFormatMulti}}
{{^isCollectionFormatMulti}}
formData.append('{{baseName}}', requestParameters.{{paramName}}.join(COLLECTION_FORMATS['{{collectionFormat}}']));
formData.append('{{baseName}}', {{> paramNamePartial}}.join(COLLECTION_FORMATS['{{collectionFormat}}']));
{{/isCollectionFormatMulti}}
}

{{/isListContainer}}
{{^isListContainer}}
if (requestParameters.{{paramName}} !== undefined) {
formData.append('{{baseName}}', requestParameters.{{paramName}} as any);
}

if ({{> paramNamePartial}} !== undefined) { formData.append('{{baseName}}', {{> paramNamePartial}} as any); }
{{/isListContainer}}
{{/formParams}}

{{/hasFormParams}}
return this.request<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/returnType}}>({
path: '{{{path}}}'{{#pathParams}}.replace({{=<% %>=}}'{<%baseName%>}'<%={{ }}=%>, encodeURI(requestParameters.{{paramName}})){{/pathParams}},
path: '{{{path}}}'{{#pathParams}}.replace({{=<% %>=}}'{<%baseName%>}'<%={{ }}=%>, encodeURI({{> paramNamePartial}})){{/pathParams}},
method: '{{httpMethod}}',
{{#hasHttpHeaders}}
headers,
Expand All @@ -156,14 +188,14 @@ export class {{classname}} extends BaseAPI {
{{#hasBodyParam}}
{{#bodyParam}}
{{#isContainer}}
body: requestParameters.{{paramName}},
body: {{paramName}},
{{/isContainer}}
{{^isContainer}}
{{^isPrimitiveType}}
body: requestParameters.{{paramName}},
body: {{paramName}},
{{/isPrimitiveType}}
{{#isPrimitiveType}}
body: requestParameters.{{paramName}} as any,
body: {{paramName}} as any,
{{/isPrimitiveType}}
{{/isContainer}}
{{/bodyParam}}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
{{! helper to output the alias of a parameter if provided and to not clutter the main template }}
{{#vendorExtensions.paramNameAlternative}}{{vendorExtensions.paramNameAlternative}}{{/vendorExtensions.paramNameAlternative}}{{^vendorExtensions.paramNameAlternative}}{{paramName}}{{/vendorExtensions.paramNameAlternative}}
Loading