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

[Typescript][Fetch] client refactoring #569

Merged
merged 46 commits into from Nov 18, 2018

Conversation

Place1
Copy link
Contributor

@Place1 Place1 commented Jul 15, 2018

PR checklist

  • Read the contribution guidelines.
  • Ran the shell script under ./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\.
  • Filed the PR against the correct branch: master, 3.1.x, 4.0.x. Default: master.
  • Copied the technical committee to review the pull request if your PR is targeting a particular programming language.

Description of the PR

This PR applies a fork of typescript-fetch that I originally created here: https://github.com/place1/swagger-codegen-typescript-browser

The notable differences to the original typescript-fetch generator are outlines in the README.md

The changes in this fork also address the following open issues on this repo:

TODO:

Confirm the this PR fixes the following known issues:

Add support for:

General improvements:

  • Find a better implementation for modelPropertyNaming.
    The current runtime conversion has known edge cases. Only a perfect implementation (i.e. based on the spec itself) should be acceptable for an option like modelPropertyNaming to exist.
  • improve middleware api interface. use a single options object rather than many positional arguments

@Place1
Copy link
Contributor Author

Place1 commented Jul 15, 2018

Sorry for messing up the first PR. I use gitlab all day at work and they have the source/target selection the other way around 🤣

@Place1
Copy link
Contributor Author

Place1 commented Jul 15, 2018

@TiFu (2017/07) @taxpon (2017/07) @sebastianhaas (2017/07) @kenisteward (2017/07) @Vrolijkx (2017/09) @macjohnny (2018/01)

TiFu
TiFu previously requested changes Jul 16, 2018
Copy link
Contributor

@TiFu TiFu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh :D Worked out in the end. Thanks for the PR!

From what I saw in the code, this change mostly removes files and changes comments, but does not fix any issues? (correct me if i'm wrong). Is this PR missing some changes you did on your fork?

e.g. package.json, tsconfig.json etc.

* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* NOTE: This class is auto generated by the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen.git
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you please revert this change?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sure thing

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed in ccfd874

export enum {{classname}} {
{{#allowableValues}}
{{#enumVars}}
{{{name}}} = <any> {{{value}}}{{^-last}},{{/-last}}
{{{name}}} = {{{value}}}{{^-last}},{{/-last}} {{#description}}// {{description}}{{/description}}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason why <any> was removed?

This was not removed in modelGeneric.mustache (see below)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No I think I must have just sweept it away when changeing things. I can't see a reason for it though, enum values should be true to what they really are without type erasure.

@@ -1,32 +0,0 @@
{
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should still have a package.json and tsconfig.json

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@TiFu I'm happy to put these back in if required. I removed them because i've found that there's a general level of simplicity gained from just having a single file output. But perhaps with typescript project references coming in TS 3.0 it might be worth while to have.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

package.json just makes it simpler to include in packages just as a dependency.

tsconfig.json should definitely be included, e.g. specify EcmaScritp version.

* {{{description}}}
* @export
* @interface {{classname}}
*/
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this comment section should remain instead of the new one.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sure thing

* {{{description}}}
* @type {{=<% %>=}}{<%&datatype%>}<%={{ }}=%>
* @memberof {{classname}}
*/
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See above - i'd prefer this comment over the new one

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sure thing

const localVarHeaderParameter = {} as any;
const localVarQueryParameter = {} as any;
{{#hasFormParams}}
const localVarFormParams = new url.URLSearchParams();
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@TiFu my fork does away with the dependency on the URL module and uses browser native APIs such as FormData and encodeURIComponent.

This solves issues such as swagger-api/swagger-codegen#6403

From memory some users encountered issues with @types definitions for isomorphic fetch as well, but I can't find the bug reports :/

{{#returnType}}
responseType: this.getResponseType('{{{returnType}}}'),
{{/returnType}}
modelPropertyNaming: '{{modelPropertyNaming}}',
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@TiFu We use the modelPropertyNaming flag to do response model property renaming. Currently it uses a runtime method to do the conversion but a better implementation would modify this to use a property name map.

The existing codegen, using the default camelCase for model property naming just causes broken code for clients. This is a major barrier for new users IMO when their first experience is a generator results in a broken default.

see: swagger-api/swagger-codegen#3818

body: formData,
{{/hasFormParams}}
{{#returnType}}
responseType: this.getResponseType('{{{returnType}}}'),
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@TiFu this fork uses the response type of the request to support handling "string" response types. The original generator just calls response.json() which crashes when the response is plain text.

I'm sure this issue has been recoded in the original swagger codegen issue tracker but someone picked it up on my fork as well: Place1/swagger-codegen-typescript-browser#2

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@TiFu This change also allows the client to support HTTP 204 no-content responses. The original generator will call response.json() which throws a SyntaxError when the response has no content because of JSON.parse("")

this.middleware = configuration.middleware;
}

withMiddleware<T extends BaseAPI>(this: T, ...middlewares: Middleware[]) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@TiFu our generator supports middleware. This is a very useful feature and something that most HTTP clients contain. My colleagues have been able to make good use of this feature across different projects to implement logging, automatic request retries, timeouts, circuit breakers, automatic token refresh logic, automatic logout logic, complex authentication/authorization challenges that are custom or out-of-scope for the built in security definitions, etc.

{{#operations}}
{{#operation}}
{{#allParams.0}}
export interface {{operationIdCamelCase}}Request {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@TiFu this fork also generates interfaces for all API parameters. This is very helpful as a consumer because it allows you to reference them in your own methods/variables. The original generator uses inline type signatures which aren't reusable.

{{/isOAuth}}
{{/authMethods}}
{{#hasFormParams}}
const formData = new FormData();
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@TiFu
Copy link
Contributor

TiFu commented Jul 16, 2018

Oh thanks for the write up! No idea how I missed these changes earlier. Gonna do an in-depth review tonight.

@Place1
Copy link
Contributor Author

Place1 commented Jul 16, 2018

haha no worries, it's hard to see anything with a diff like this one 🤓

@macjohnny
Copy link
Member

@Place1 could you have a look at the changes of @JFCote in #145 to avoid conflicts?

@Place1
Copy link
Contributor Author

Place1 commented Jul 17, 2018

@macjohnny hey i've had a look at that PR, my changes here implement all the changes over there so I think it's covered (hopefully I didn't miss anything).

@macjohnny
Copy link
Member

@Place1 great, so it should work out

@Place1
Copy link
Contributor Author

Place1 commented Jul 17, 2018

Something I think we should try to add to this PR is to change the modelPropertyNaming implementation.

https://github.com/OpenAPITools/openapi-generator/pull/569/files#diff-1509e7682d968c989ff9ff50e26052edR61

Currently it's a runtime method that generally works, but will fail in certain cases such as properties with a leading underscore.

I'm not familiar with how other generators (outside of typescript/js) implement this feature. We should probably copy the approach so we can make sure this part of the generator is bullet proof.

@TiFu
Copy link
Contributor

TiFu commented Jul 17, 2018

Alright the PR looks good to me (and I realized why I missed all those changes - api.mustache was hidden by default and I overlooked it). Seems to need some fixing so that the CI tests pass (see below).

Shippable complains that the samples aren't up to date:

 Perform git status
HEAD detached from FETCH_HEAD
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

	modified:   samples/client/petstore/typescript-fetch/builds/default/api.ts
	modified:   samples/client/petstore/typescript-fetch/builds/es6-target/api.ts
	modified:   samples/client/petstore/typescript-fetch/builds/with-interfaces/api.ts
	modified:   samples/client/petstore/typescript-fetch/builds/with-npm-version/api.ts

no changes added to commit (use "git add" and/or "git commit -a")
Please run 'bin/utils/ensure-up-to-date' locally and commit changes (UNCOMMITTED CHANGES ERROR)

Appveyor seems to be missing a README.mustache?

[main] WARN  o.o.codegen.utils.ModelUtils - Multiple schemas found, returning only the first one
[main] WARN  o.o.codegen.utils.ModelUtils - Multiple schemas found, returning only the first one
[main] WARN  o.o.codegen.utils.ModelUtils - Multiple schemas found, returning only the first one
[main] WARN  o.o.codegen.utils.ModelUtils - Multiple schemas found, returning only the first one
[main] INFO  o.o.codegen.AbstractGenerator - writing file C:\projects\openapi-generator\samples\client\petstore\typescript-fetch\builds\es6-target\api.ts
[main] ERROR o.o.codegen.AbstractGenerator - typescript-fetch\README.mustache (The system cannot find the path specified)
[main] ERROR o.o.codegen.AbstractGenerator - typescript-fetch\README.mustache (The system cannot find the path specified)
[main] ERROR o.o.codegen.AbstractGenerator - can't load template typescript-fetch\README.mustache
[main] ERROR o.o.codegen.AbstractGenerator - can't load template typescript-fetch\README.mustache
Exception in thread "main" java.lang.RuntimeException: Could not generate supporting file 'SupportingFile:
	templateFile: README.mustache
	folder: 
	destinationFilename: README.md
'
	at org.openapitools.codegen.DefaultGenerator.generateSupportingFiles(DefaultGenerator.java:656)
	at org.openapitools.codegen.DefaultGenerator.generate(DefaultGenerator.java:797)
	at org.openapitools.codegen.cmd.Generate.run(Generate.java:315)
	at org.openapitools.codegen.OpenAPIGenerator.main(OpenAPIGenerator.java:59)
Caused by: java.lang.RuntimeException: can't load template typescript-fetch\README.mustache
	at org.openapitools.codegen.AbstractGenerator.readTemplate(AbstractGenerator.java:68)
	at org.openapitools.codegen.DefaultGenerator.generateSupportingFiles(DefaultGenerator.java:623)
	... 3 more
Java HotSpot(TM) 64-Bit Server VM warning: ignoring option MaxPermSize=2g; support was removed in 8.0

travis complains that the code doesn't compile

> @swagger/typescript-fetch-petstore@1.0.0 build /home/travis/build/OpenAPITools/openapi-generator/samples/client/petstore/typescript-fetch/builds/with-npm-version
> tsc --outDir dist/
index.ts(16,1): error TS2308: Module "./api" has already exported a member named 'Configuration'. Consider explicitly re-exporting to resolve the ambiguity.
index.ts(16,1): error TS2308: Module "./api" has already exported a member named 'ConfigurationParameters'. Consider explicitly re-exporting to resolve the ambiguity.
npm ERR! code ELIFECYCLE

@Place1
Copy link
Contributor Author

Place1 commented Jul 17, 2018

@TiFu thanks for the breakdown. I'll try to fix these CI issues in the next few days.

I'll also confirm that my fork supports:

Other things i'd like to address if people are still onboard is:

Perhaps we could adopt an implementation similar to the typescript-angular generator which uses method overloading to let the user request the API's json or the native Response (from which they can get headers etc.)

@macjohnny
Copy link
Member

@Place1 could you please update this PR and resolve the conflicts due to #145 ?

@macjohnny
Copy link
Member

@emreavsar the additional headers in you are asking for in swagger-api/swagger-codegen#8444 could be solved by using this changed typescript-fetch generator and provide an appropriate middleware, i.e. an interceptor that sets your headers.

@Place1
Copy link
Contributor Author

Place1 commented Jul 19, 2018

@macjohnny done. I'll be away for a few days do i'll have to get to the other action items later. I'll add them as a check list in this PR

@Place1
Copy link
Contributor Author

Place1 commented Jul 19, 2018

If anyone else has time i'd be great to hear some feedback for the API around middleware. One concern I have is that there's too many positional arguments:

export interface Middleware {
    pre?(fetch: FetchAPI, url: string, init: RequestInit): Promise<FetchParams | undefined | void>;
    post?(fetch: FetchAPI, url: string, init: RequestInit, response: Response): Promise<Response | undefined | void>;
}

Does anyone have an opinion on a better API?

@TiFu
Copy link
Contributor

TiFu commented Jul 20, 2018

Maybe move the positional arguments into a separate class or interface?
e.g. for pre

// (i'm not very happy with this interface name, maybe you can think of a better one?)
interface Request {
      fetch: FetchAPI;
      url: string;
      init: RequestInit;
}

and then the same thing for post.

@Place1
Copy link
Contributor Author

Place1 commented Oct 16, 2018

Ok CI is now passing. I'm working on a commit for the last check list item swagger-api/swagger-codegen#7828 add method overload to allow caller to receive native Response. It should be done by tonight. Tomorrow i'll give it all 1 last big test on a few major projects and then i'm happy for it to be merged (assuming no other changes are required) :D

…equest bodies. added support for blob data type for files/binary.
@Place1
Copy link
Contributor Author

Place1 commented Oct 27, 2018

@wing328 any updates?

@wing328
Copy link
Member

wing328 commented Nov 15, 2018

If no further question/feedback from anyone, I'll merge this PR into 4.0.x branch (breaking changes without fallback) this coming weekend.

@wing328
Copy link
Member

wing328 commented Jan 2, 2019

@Place1 thanks for the contribution, which has been included in the 4.0.0-beta release: https://twitter.com/oas_generator/status/1079727020374806529.

Happy New Year and looking forward to more collaboration and contributions in 2019!

A-Joshi pushed a commit to ihsmarkitoss/openapi-generator that referenced this pull request Feb 27, 2019
* added my fork from https://github.com/Place1/swagger-codegen-typescript-browser

* ran bin/typescript-fetch-petstore-all.sh

* use FormData.append rather than .set for IE11 compat

* reverted change to licenseInfo.mustache

* reverted some comments

* added package.json and tsconfig.json back to the generator

* added support for blob (application/octet-stream) responses

* models and apis are now in folders

* added support for modelPropertyNaming based on the spec

* bug fix

* updated samples

* Restore pom.xml for typescript project

* Restore samples/client/petstore/typescript-fetch/tests/default/package.json
≈

* added support for response type Date conversion

* updated samples

* Rework pom in "samples.ci"

* Restore "samples/client/petstore/typescript-fetch/tests/default"

* updated configuration class to use property getters to allow clients to implement configuration values as getters

* added {{datatype}}ToJSON functions to handle serialization and naming conversions

* fixed missing import

* fixed compilation error. updated samples

* 1 character change to get CI to run again

* updated samples

* added support for array type request body

* updated tests

* support for optional request bodies

* updated models json converters to handle undefined inputs (to simplify usage in optional contexts like optional request bodies)

* updated samples

* updated tests

* changed to typescript version 2.4

* updated samples

* support for optional properties being null, undefined or omitted

* updated samples

* bug fix

* bug fix

* updated samples

* ran npm install in test project

* patch to get tests running

* added support for retrieving raw response. added support for binary request bodies. added support for blob data type for files/binary.
@JFCote
Copy link
Member

JFCote commented Jul 9, 2019

@Place1 @TiFu @wing328 I'm not sure I understand this new fetch generator. It is breaking my entire code and I might not be able to upgrade to the latest version of OpenApi Generator because of this.

It seems all the signature have changed.

For exemple, before this, my function addAffiliate would simply needs a AffiliateCore object in the body. It is defined that way in the spec.

With the new generator, I need to pass a AddAffiliateRequest object type.

The new interface is defined that way:

export interface AddAffiliateRequest {
    newAffiliate: AffiliateCore;
}

It looks to me like a wrapper. But by doing this, we do not respect the API spec and new object type needs to be passed. Objects that the user of the API / Client might not even know.

I suggest we rollback this merge completely or that we remove the part that create new object type out of thin air. Unless there is a way or an option in the generator to use the real method signature but it doesn't seems that way in the generator code.

@mhidalgop
Copy link

@emreavsar the additional headers in you are asking for in swagger-api/swagger-codegen#8444 could be solved by using this changed typescript-fetch generator and provide an appropriate middleware, i.e. an interceptor that sets your headers.

How could I do this??? I need to send the version API into the header for all request.

@macjohnny
Copy link
Member

@mhidalgop see e.g.

withPreMiddleware<T extends BaseAPI>(this: T, ...preMiddlewares: Array<Middleware['pre']>) {
const middlewares = preMiddlewares.map((pre) => ({ pre }));
return this.withMiddleware<T>(...middlewares);
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

8 participants