-
Notifications
You must be signed in to change notification settings - Fork 6k
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
Add typescript-inversify code generator #7885
Add typescript-inversify code generator #7885
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
in general, this looks very nice.
@@ -0,0 +1,31 @@ | |||
#!/bin/sh |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please add a .cmd version of this file for windows-users
@@ -0,0 +1,31 @@ | |||
#!/bin/sh |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please add a .cmd version of this file for windows-users
@@ -0,0 +1,62 @@ | |||
import IHttpClient from "./IHttpClient"; | |||
import * as Rx from "rx"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
wouldn't it be better to
import { Observable } from "rxjs/Observable";
?
if ({{paramName}}) { | ||
{{#isCollectionFormatMulti}} | ||
{{paramName}}.forEach((element) => { | ||
queryParameters.push("{{paramName}}="+String({{paramName}})); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you need to encodeURIComponent()
,
queryParameters.push("{{paramName}}="+encodeURIComponent(String({{paramName}})));
{{^isListContainer}} | ||
if ({{paramName}} !== undefined) { | ||
{{#isDateTime}} | ||
queryParameters.push("{{paramName}}="+<any>{{paramName}}.toISOString()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you need to encodeURIComponent()
,
queryParameters.push("{{paramName}}="+encodeURIComponent(<any>{{paramName}}.toISOString()));
{{/isKeyInHeader}} | ||
{{#isKeyInQuery}} | ||
if (this.APIConfiguration.apiKeys["{{keyParamName}}"]) { | ||
queryParameters.set('{{keyParamName}}', this.APIConfiguration.apiKeys["{{keyParamName}}"]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
how can queryParameters.set()
work on a string[]
?
should be
queryParameters.push("{{keyParamName}}="+encodeURIComponent(this.APIConfiguration.apiKeys["{{keyParamName}}"]));
|
||
{{/hasFormParams}} | ||
|
||
return this.httpClient.{{httpMethod}}(`${this.basePath}{{{path}}}{{#hasQueryParams}}?${encodeURIComponent(queryParameters.join('&'))}{{/hasQueryParams}}`{{#bodyParam}}, {{paramName}} {{/bodyParam}}{{#hasFormParams}}, body{{/hasFormParams}}, headers) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
encodeURIComponent(queryParameters.join('&'))`` should be
queryParameters.join('&'), otherwise the
=between the parameter name and the value will be converted to
%3D`
* {{notes}} | ||
{{#allParams}}* @param {{paramName}} {{description}} | ||
{{/allParams}}*/ | ||
{{nickname}}({{#allParams}}{{paramName}}{{^required}}?{{/required}}: {{{dataType}}}, {{/allParams}}extraHttpRequestParams?: any): Observable<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}{}{{/returnType}}>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the signature should correspond to the api service, i.e. should be
{{nickname}}({{#allParams}}{{paramName}}{{^required}}?{{/required}}: {{{dataType}}}, {{/allParams}}headers: Dictionary<string> = {}): Promise<{{#returnType}}{{{returnType}}}{{#isResponseTypeFile}}|undefined{{/isResponseTypeFile}}{{/returnType}}{{^returnType}}any{{/returnType}}>
"dependencies": { | ||
"inversify": "^4.3.0", | ||
"lodash": "^4.17.5", | ||
"rx": "~4.1.0", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
wouldn't you want to have rxjs
: "~5.5.2" ?
import mockit.Expectations; | ||
import mockit.Tested; | ||
|
||
public class TypeScriptInversifyClientOptionsTest extends AbstractOptionsTest { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
very nice
Hi, thanks for all yours suggestions. I should have solved all the problems listed. |
import { Dictionary } from "lodash"; | ||
|
||
interface IHttpClient { | ||
get(url:string, headers?:Dictionary<string>):Rx.Observable<HttpResponse> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do you really want to use a lodash dictionary? why not use a plain JavaScript object?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The lodash dictionary restrict the type of values and not permit nested Object. Also Angular's HttpClient allows only string as value (https://angular.io/api/http/Headers)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you can restrict the value of an object as follows:
let myObject: {[key: string]: string}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You have reason, i'm removing lodash.
headers['Accept'] = 'application/xml'; | ||
|
||
|
||
return this.httpClient.get(`${this.basePath}/pet/findByStatus?${encodeURIComponent(queryParameters.join('&'))}`, headers) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it looks like you need to re-generate the file, since the encodeURIComponent
should be removed here
headers['Content-Type'] = 'application/json'; | ||
|
||
|
||
return this.httpClient.post(`${this.basePath}/store/order`, body , headers) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
are you sure you want to convert all response observables to promises?
returning them as observables would make it convenient to chain with RxJS
, similar to the Angular HttpClient
.
As a drawback, you always need to subscribe to the API call observable, otherwise the request is not performed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On this subject I was undecided, because the observable returned by the HttpClient complete immediately and returns only a value (as every Promise). But you have reason that returing a observable would make convenient to chain with RxJS
, but you can do it with the operator fromPromise if necessary... I'm very undecided about this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would suggest to adapt the interface of the Angular HttpClient with Observables.
Anyone else got an opinion on this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would add a configuration variable that allow the user to decide if use Promise or Observable. What about this solution?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@gualtierim this sounds like a great idea!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@macjohnny I have added the usePromise
config's variable.
in general, I would not use lodash here, as it is not needed for any functionality and it adds a dependency. moreover, not everyone wants to use lodash. |
*/ | ||
{{/description}} | ||
export interface {{classname}}Interface { | ||
[others: string]: any; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is this needed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you set the additional parameters withInterfaces
, the apiInterface
should be necessary.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was referring to [others: string]: any;
@@ -1,79 +0,0 @@ | |||
export interface ConfigurationParameters { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@gualtierim why did you remove the configuration template?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because i only use the IApiConfiguration
that you have to implement and bind inside the container.
@gualtierim your code looks great. |
@macjohnny i have added the possibility to receive all the httpResponse. I tried to implement it as in typescript-angular generator. |
/** | ||
* Add a new pet to the store | ||
* | ||
* @param body Pet object that needs to be added to the store | ||
|
||
*/ | ||
|
||
public addPet(body: Pet, headers: Headers = {}): Observable<any> { | ||
public addPet(body: Pet, observe?: 'body', headers?: Headers): Observable<any>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@gualtierim can you find out why the response is any
? it should be Pet
, shouldn't it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ah, it's because the addPet
endpoint does not return anything, so it is ok
return this.httpClient.{{httpMethod}}(`${this.basePath}{{{path}}}{{#hasQueryParams}}?${queryParameters.join('&')}{{/hasQueryParams}}`{{#bodyParam}}, {{paramName}} {{/bodyParam}}{{#hasFormParams}}, body{{/hasFormParams}}, headers) | ||
.map(httpResponse => <{{#returnType}}{{{returnType}}}{{#isResponseTypeFile}}|undefined{{/isResponseTypeFile}}{{/returnType}}{{^returnType}}any{{/returnType}}>(httpResponse.response)){{#usePromise}}.toPromise(){{/usePromise}}; | ||
const response: Observable<HttpResponse<{{#returnType}}{{{returnType}}}{{#isResponseTypeFile}}|undefined{{/isResponseTypeFile}}{{/returnType}}{{^returnType}}any{{/returnType}}>> = this.httpClient.{{httpMethod}}(`${this.basePath}{{{path}}}{{#hasQueryParams}}?${queryParameters.join('&')}{{/hasQueryParams}}`{{#bodyParam}}, {{paramName}} {{/bodyParam}}{{#hasFormParams}}, body{{/hasFormParams}}, headers); | ||
if(observe == 'body') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please wrap the if
block with curly braces {
and }
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I done it
@gualtierim looks great! |
@macjohnny thanks to your support |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I haven't tested it, but the code looks good
@gualtierim did you test the generated code in an actual project? |
Yes, i have tested it inside a project and it seems to work. |
I'll take another look and merge this weekend. Thanks for the new generator! |
It seems like there's an issue with as I got the following errors when running "mvn clean test " locally:
(which seems totally unrelated) |
PR checklist
./bin/
to update Petstore sample so that CIs can verify the change. (For instance, only need to run./bin/{LANG}-petstore.sh
and./bin/security/{LANG}-petstore.sh
if updating the {LANG} (e.g. php, ruby, python, etc) code generator or {LANG} client's mustache templates). Windows batch files can be found in.\bin\windows\
.3.0.0
branch for changes related to OpenAPI spec 3.0. Default:master
.Description of the PR
We added a code generator called
typescript-inversify
to support InversifyJS: a powerful and lightweight inversion of control container for JavaScript & Node.js apps powered by TypeScript. Inside the README is explained how to integrate services generated in an existing project.