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

Support ResponseEntity in annotated service instead of HttpResult #5586

Merged
merged 19 commits into from
Apr 22, 2024

Conversation

moromin
Copy link
Contributor

@moromin moromin commented Apr 9, 2024

Motivation:

We have HttpResult<T> and ResponseEntity<T>, both of them support automatic JSON deserialization.
However, we can't use HttpResult for clients because it is located in com.linecorp.armeria.server.annotation.

So, we should support ResponseEntity in that package so that users are not confused as to which one to use, and so that it can be used commonly between server and client.

Modifications:

  • Add logic to check if result type is ResponseEntity.
  • Create ResponseEntityUtil to build response headers as well as HttpResult.
  • Deprecate HttpResult.
  • Tests
    • Add ResponseEntity cases for the cases that verify HttpResult behavior itself.
    • Replace for the cases that use HttpResult to verify other behaviors.

Result:

Copy link

codecov bot commented Apr 9, 2024

Codecov Report

Attention: Patch coverage is 96.55172% with 1 lines in your changes are missing coverage. Please review.

Project coverage is 74.13%. Comparing base (b8eb810) to head (4094889).
Report is 160 commits behind head on main.

❗ Current head 4094889 differs from pull request most recent head b1a5c37. Consider uploading reports for the commit b1a5c37 to get more accurate results

Files Patch % Lines
...annotation/CompositeResponseConverterFunction.java 83.33% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##               main    #5586      +/-   ##
============================================
+ Coverage     73.95%   74.13%   +0.18%     
- Complexity    20115    21020     +905     
============================================
  Files          1730     1820      +90     
  Lines         74161    77443    +3282     
  Branches       9465     9901     +436     
============================================
+ Hits          54847    57415    +2568     
- Misses        14837    15378     +541     
- Partials       4477     4650     +173     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

Copy link
Member

@trustin trustin left a comment

Choose a reason for hiding this comment

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

Very nice work, @moromin! Left some minor comments 🙇

Comment on lines +422 to +428
@Get("/expect-unauthorized")
public ResponseEntity<HttpResponse> expectUnauthorized() {
// Will send '401 Unauthorized' because the content of ResponseEntity is HttpResponse.
return ResponseEntity.of(
ResponseHeaders.of(HttpStatus.OK),
HttpResponse.of(HttpStatus.UNAUTHORIZED));
}
Copy link
Member

Choose a reason for hiding this comment

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

Should we raise an exception to avoid this confusing behavior? What do you think?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

There is also warning, but I think the exception is more kind.
If we do, I think we could throw the exception here with the warning.

if (HttpResponse.class.isAssignableFrom(clazz) ||
AggregatedHttpResponse.class.isAssignableFrom(clazz)) {
logger.warn("{} in the return type '{}' may take precedence over {}.",
clazz.getSimpleName(), returnType, HttpResult.class.getSimpleName());

Copy link
Member

Choose a reason for hiding this comment

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

Ahh, I see. We already log a warning message. Let's leave it as it is for now given that it may break an existing application. 🤔

Copy link
Member

@trustin trustin left a comment

Choose a reason for hiding this comment

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

One last comment 😄

Copy link
Member

@trustin trustin left a comment

Choose a reason for hiding this comment

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

👏👏👏

@ikhoon
Copy link
Contributor

ikhoon commented Apr 11, 2024

Regarding the test failure of build-self-hosted-unsafe-jdk-17-leak, the test was skipped but the test report had running logs for the test class.
https://ge.armeria.dev/s/3apwtzpy5yhzu/tests/overview?class=com.linecorp.armeria.client.kubernetes.ArmeriaHttpInterceptorTest&task-path=:kubernetes:shadedTest
image

Maybe Gradle's build result had not been properly cleared somehow.

Copy link
Contributor

@ikhoon ikhoon left a comment

Choose a reason for hiding this comment

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

Looks great! I hope to see you often even after Armeria Sprint. 😆

Comment on lines 33 to 40
final ResponseHeadersBuilder builder = result.headers().toBuilder();

if (builder.status().isContentAlwaysEmpty()) {
return builder.build();
}
if (builder.contentType() != null) {
return builder.build();
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Micro optimization) Should we lazily create ResponseHeadersBuilder?

ResponseHeaders headers = result.headers();

if (builder.status().isContentAlwaysEmpty()) {
    return headers;
}
if (builder.contentType() != null) {
    return headers;
}
....

@moromin moromin force-pushed the support_response_entity branch from 338849a to cf0e9ca Compare April 11, 2024 10:25
Copy link
Contributor

@jrhee17 jrhee17 left a comment

Choose a reason for hiding this comment

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

Left one nit but looks good overall! 👍

/**
* Utilities for {@link ResponseEntity}.
*/
public final class ResponseEntityUtil {
Copy link
Contributor

Choose a reason for hiding this comment

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

nit; if this class is moved to com.linecorp.armeria.internal.server.annotation, it can be package private

Suggested change
public final class ResponseEntityUtil {
final class ResponseEntityUtil {

/**
* Build {@link ResponseHeaders} from the given {@link ServiceRequestContext} and {@link ResponseEntity}.
*/
public static ResponseHeaders buildResponseHeaders(ServiceRequestContext ctx, ResponseEntity<?> result) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
public static ResponseHeaders buildResponseHeaders(ServiceRequestContext ctx, ResponseEntity<?> result) {
static ResponseHeaders buildResponseHeaders(ServiceRequestContext ctx, ResponseEntity<?> result) {

@jrhee17 jrhee17 added this to the 1.29.0 milestone Apr 16, 2024
Copy link
Contributor

@jrhee17 jrhee17 left a comment

Choose a reason for hiding this comment

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

👍 👍 👍

Copy link
Contributor

@minwoox minwoox left a comment

Choose a reason for hiding this comment

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

Thanks, @moromin! 👍

@ikhoon ikhoon merged commit a8dea21 into line:main Apr 22, 2024
16 checks passed
clayburn added a commit to clayburn/armeria that referenced this pull request Apr 24, 2024
* upstream/main:
  Allow `{Service,Server}ErrorHandler.renderStatus()` to access `ServiceRequestContext` (line#5634)
  Bump com.gradle.enterprise from 3.16.2 to 3.17 (line#5559)
  Bump com.gradle.common-custom-user-data-gradle-plugin from 2.0 to 2.0.1 (line#5618)
  Add SchemeAndAuthority class which is in charge of authority normalization (line#5561)
  [Issue-5581] Change into transitive dependency(netty.transport.native.unix.common) (line#5623)
  Make @description annotation at super class/interface work (line#5562)
  Support `ResponseEntity` in annotated service instead of `HttpResult` (line#5586)
  Update public suffix list (line#5625)
  Wait until buffers are released in `ExceedingServiceMaxContentLengthTest.maxContentLength` (line#5600)
  Add the release note for 1.28.2 (line#5615)
  Fixed bug where a request timeout is scheduled after receiving the entire body (line#5614)
  Support custom json marshaller for unframed gRPC error (line#5555)
  Update public suffix list (line#5612)
Dogacel pushed a commit to Dogacel/armeria that referenced this pull request Jun 8, 2024
…line#5586)

Motivation:

We have `HttpResult<T>` and `ResponseEntity<T>`, both of them support
automatic JSON deserialization.
However, we can't use `HttpResult` for clients because it is located in
`com.linecorp.armeria.server.annotation`.

So, we should support `ResponseEntity` in that package so that users are
not confused as to which one to use, and so that it can be used commonly
between server and client.

Modifications:

- Add logic to check if result type is `ResponseEntity`.
- Create `ResponseEntityUtil` to build response headers as well as
`HttpResult`.
- Deprecate `HttpResult`.
- Tests
- Add `ResponseEntity` cases for the cases that verify `HttpResult`
behavior itself.
- Replace for the cases that use `HttpResult` to verify other behaviors.

Result:

- Closes line#4910
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.

Support ResponseEntity in annotated services.
5 participants