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

Feat: Response header(Fix #770) #857

Merged
merged 16 commits into from Jan 16, 2021
Merged

Conversation

jackey8616
Copy link
Collaborator

All Submissions:

  • Have you followed the guidelines in our Contributing document?
  • Have you checked to ensure there aren't other open Pull Requests for the same update/change?
  • Have you written unit tests?
  • Have you written unit tests that cover the negative cases (i.e.: if bad data is submitted, does the library respond properly)?
  • This PR is associated with an existing issue?

Closing issues

Closes #770 .

If this is a new feature submission:

  • Has the issue had a maintainer respond to the issue and clarify that the feature is something that aligns with the goals and philosophy of the project?

Potential Problems With The Approach

This RP introduce SuccessResponse and Response decorator with a generic type to define header struture.

SuccessResponse wasn't follow with any generic type, so in this PR, is not a big deal.
but Response is already provide a generic type T which specify the example structure,
usage of Response before this PR is @Response<ExampleClass>:
In some situation like need header class but not example class, the decorator define will be @Response<null, HeaderClass>.

Test plan

Test spec is coming out at path.[status code].headers field.

@WoH
Copy link
Collaborator

WoH commented Dec 21, 2020

Can you extend the parsing to the third argument of @Res() response: TsoaResponse<Status, Body, Headers>. I added the argument for type checking and anticipation of a PR adding this.

E: See here and here.

Add statements to support headers field.
Add controller for tests and test cases.
@jackey8616
Copy link
Collaborator Author

@WoH I've done the support for TsoaResponse,
Please review. thank you.

tests/unit/swagger/schemaDetails3.spec.ts Outdated Show resolved Hide resolved
tests/unit/swagger/schemaDetails3.spec.ts Outdated Show resolved Hide resolved
tests/unit/swagger/schemaDetails.spec.ts Show resolved Hide resolved
packages/cli/src/swagger/specGenerator3.ts Show resolved Hide resolved
Add missing common response header with header object instead of header class.
Edit some HeaderClass's description more readable.
Replace all exsitence examine with content examine in test cases.
Add optional field in all header class / object at test controllers.
FIx incorrect statement with undefined result
 at schemaDetail#L197 & shcmeaDetail3#L444.
 Add field exsitence examine at OAI2.0 & required field in OAI3.0.
Add else statement to throw error with invalid header type.
Add incorrect response header controller for test cases.
Add test case in both OAI2.0 & 3.0.
Remove optional overrloading for SuccessResponse & Response type.
Edit SuccessResponse & Response type's generic type more readable.
@WoH
Copy link
Collaborator

WoH commented Dec 29, 2020

3 more general thoughts:

  • We may want to add type aliases in the future, currently, that will fail
  • This relates to both Use code from SuccessResponse decorator as default status. #850 and Support for response headers in generated OpenAPI spec #770 which initially suggested the Header docs API: Currently, this PR extends the @SuccessResponse decorator arguments in a descriptive fashion. This means, there's no guarantee that the response headers actually match the documentation. Use code from SuccessResponse decorator as default status. #850 changes that for the response status, I'd argue there's a lot of times, where the header could be set using @SuccessHeader({'Cache-Control', 'none'}). What do you think? Would also be interested in @devnev 's take here. While I don't think we should do that in this PR, if we anticipate that functionality, we should change the descriptive API to not add an Argument to @SuccessResponse and instead add @SuccessHeader. @jackey8616 was there a specific reason why there's a deviation from the API suggested in Support for response headers in generated OpenAPI spec #770?
  • I think we should type the header property of the Tsoa.Response more strictly, since it can only contain a refObject/nestedObjectLiteral. Then we can catch other types when getting the header type (here and here) and use the GenerateMetadataError early to catch issues and print the offending node.

@WoH
Copy link
Collaborator

WoH commented Dec 30, 2020

@jackey8616 I'd love to add some commits that address some of the things I laid out myself if you don't mind. Would you like to give me commit access to the PR or should I send you diffs?

@jackey8616
Copy link
Collaborator Author

@WoH I turned it on!

@WoH
Copy link
Collaborator

WoH commented Dec 30, 2020

Remaining TODOs:

  • OAS3: explode and style, OAS2: collectionFormat (?)
  • Clean up tests: Both OAS2 and OAS3 specs should test all the new features, there are discrepancies rn
  • Discuss @SuccessResponse<Header>() vs. SuccessHeader<Header>() (?)
  • Support aliases, unions, intersections (?)

This ensures we never try to generate a spec based on invalid metadata.
This enables us to point out the issue more precisely and link the
offending node.
@jackey8616
Copy link
Collaborator Author

@WoH

@jackey8616 was there a specific reason why there's a deviation from the API suggested in #770?

IMHO, header must be used along with a response in swagger, thus, they should be good if we let it be optional parameter at Response, SuccessResponse or TsoaResponse.

Since SuccessResponse and SuccessHeader decorator is implicitly contains a 2xx status.
When programmer not using default status as it decorator gives, these two decorator may let the usage logic become fuzzy.

@WoH
Copy link
Collaborator

WoH commented Dec 31, 2020

Since SuccessResponse and SuccessHeader decorator is implicitly contains a 2xx status.
When programmer not using default status as it decorator gives, these two decorator may let the usage logic become fuzzy.

I don't think I can follow. Can you give me an example?

@jackey8616
Copy link
Collaborator Author

Since SuccessResponse and SuccessHeader decorator is implicitly contains a 2xx status.
When programmer not using default status as it decorator gives, these two decorator may let the usage logic become fuzzy.

I don't think I can follow. Can you give me an example?

If we want to let success status code to be 204,
we need to set both in SuccessResponse & SuccessHeader.
and it seems a little bit bother to set 204 code twice.

@SuccessResponse(204, 'some descriptions doesn\'t want be omit.')
@SuccessHeader<HeaderClass>(204, {
  linkA: 'http://google.com',
})
public async handler(): Promise<void> {
  return;
}

@WoH
Copy link
Collaborator

WoH commented Jan 1, 2021

Since SuccessResponse and SuccessHeader decorator is implicitly contains a 2xx status.
When programmer not using default status as it decorator gives, these two decorator may let the usage logic become fuzzy.

I don't think I can follow. Can you give me an example?

If we want to let success status code to be 204,
we need to set both in SuccessResponse & SuccessHeader.
and it seems a little bit bother to set 204 code twice.

@SuccessResponse(204, 'some descriptions doesn\'t want be omit.')
@SuccessHeader<HeaderClass>(204, {
  linkA: 'http://google.com',
})
public async handler(): Promise<void> {
  return;
}

But do we? What's the problem with

​@​SuccessResponse(204,​ ​'some descriptions doesn\'t want be omit.')​
@​SuccessHeader<Header>({
  ​linkA​: ​'http://google.com',​
​})​
​public​ ​async​ ​handler()​: ​Promise<void>​ ​{​
  ​return;​
​}

@jackey8616
Copy link
Collaborator Author

But do we? What's the problem with

​@​SuccessResponse(204,​ ​'some descriptions doesn\'t want be omit.')​
@​SuccessHeader<Header>({
  ​linkA​: ​'http://google.com',​
​})​
​public​ ​async​ ​handler()​: ​Promise<void>​ ​{​
  ​return;​
​}

I think its no problem expect it runs different logic compares with SuccessResponse, which requires pre-check exists response defined by it, further more, it also effect by existence of SuccessResponse decorator, which may cause complex logic of SuccessHeader IMHO.

I've surveyed #723, and I don't see any actual reason to introduce SuccessHeader.

@WoH
Copy link
Collaborator

WoH commented Jan 3, 2021

I think its no problem expect it runs different logic compares with SuccessResponse, which requires pre-check exists response defined by it, further more, it also effect by existence of SuccessResponse decorator, which may cause complex logic of SuccessHeader IMHO.

I've surveyed #723, and I don't see any actual reason to introduce SuccessHeader.

I hope I get where you're coming from now. For this PR, the goal is only to document the headers on the default / success response. Admittedly, you can do that both with the API you implemented and @SuccessHeaders<Header>()>.
However, pulling out the header portion into it's own decorator allows us to extend the usage more easily in the future. That requires 2 additional steps, one of which will be already done in #850, because it allows tsoa to know what the default/success status code is. Based on this, we are able to infer when a default response is returned and are able to append the headers as provided in an argument to @SuccessHeaders({}).

I'm looking specifically at this:

@​SuccessHeader<Header>({linkA​: ​'http://google.com',​
​})​
​public​ ​async​ ​handler()​: ​Promise<void>​ ​{​
  ​return;​
​}

Here, Header is the type used for documentation, the actual argument will be set as headers automatically.
If we use the @SuccessResponse, my worry was that this would be something like:

@​SuccessResponse<Header>(undefined, undefined, {linkA​: ​'http://google.com',​
​})​
​public​ ​async​ ​handler()​: ​Promise<void>​ ​{​
  ​return;​
​}

However, we can obviously do type/arg checks so we can do:

@​SuccessResponse<Header>({linkA​: ​'http://google.com',​
​})​
​public​ ​async​ ​handler()​: ​Promise<void>​ ​{​
  ​return;​
​}

So it seems to come down to preference. For my personal taste setting the headers as a separate annotation feels more intuitive, but if I can't really point a finger as to why, your approach is probably equally fine.

@devnev
Copy link
Contributor

devnev commented Jan 7, 2021

Should the solution for success headers generalise to headers with dynamic values?

@WoH
Copy link
Collaborator

WoH commented Jan 9, 2021

@jackey8616 I rebased and this lgtm now. Can you take a look and confirm?

@WoH
Copy link
Collaborator

WoH commented Jan 10, 2021

Should the solution for success headers generalise to headers with dynamic values?

You'd still have to set them using setHeaders, but you'd be able to declare them (as optional properties on the Header interface).

@WoH WoH merged commit 0579f62 into lukeautry:master Jan 16, 2021
@jackey8616 jackey8616 deleted the FeatResponseHeader branch January 28, 2024 16:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Support for response headers in generated OpenAPI spec
3 participants