Skip to content

Commit e05a1a4

Browse files
committed
Add call().asVoid() option
1 parent 766753a commit e05a1a4

File tree

4 files changed

+145
-9
lines changed

4 files changed

+145
-9
lines changed

client/src/main/java/io/avaje/http/client/DHttpCall.java

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,11 @@ class DHttpCall implements HttpCallResponse {
1313
this.request = request;
1414
}
1515

16+
@Override
17+
public HttpCall<HttpResponse<Void>> asVoid() {
18+
return new CallVoid();
19+
}
20+
1621
@Override
1722
public HttpCall<HttpResponse<Void>> asDiscarding() {
1823
return new CallDiscarding();
@@ -43,12 +48,23 @@ public <E> HttpCall<Stream<E>> stream(Class<E> type) {
4348
return new CallStream<>(type);
4449
}
4550

46-
private class CallDiscarding implements HttpCall<HttpResponse<Void>> {
51+
private class CallVoid implements HttpCall<HttpResponse<Void>> {
4752
@Override
4853
public HttpResponse<Void> execute() {
4954
return request.asVoid();
5055
}
5156
@Override
57+
public CompletableFuture<HttpResponse<Void>> async() {
58+
return request.async().asVoid();
59+
}
60+
}
61+
62+
private class CallDiscarding implements HttpCall<HttpResponse<Void>> {
63+
@Override
64+
public HttpResponse<Void> execute() {
65+
return request.asDiscarding();
66+
}
67+
@Override
5268
public CompletableFuture<HttpResponse<Void>> async() {
5369
return request.async().asDiscarding();
5470
}

client/src/main/java/io/avaje/http/client/HttpCallResponse.java

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,44 @@
1010
*/
1111
public interface HttpCallResponse {
1212

13+
/**
14+
* Process the response with check for 200 range status code
15+
* returning as {@literal HttpResponse<Void>}.
16+
* <p>
17+
* Unlike {@link #asDiscarding()} this request will read any response
18+
* content as bytes with the view that the response content can be
19+
* an error message that could be read via for example
20+
* {@link HttpException#bean(Class)}.
21+
* <p>
22+
* Will throw an HttpException if the status code is in the
23+
* error range allowing the caller to access the error message
24+
* body via for example {@link HttpException#bean(Class)}
25+
* <p>
26+
* This is intended to be used for POST, PUT, DELETE requests
27+
* where the caller is only interested in the response body
28+
* when an error occurs (status code not in 200 range).
29+
*
30+
* <pre>{@code
31+
*
32+
* HttpCall<HttpResponse<Void>> call =
33+
* clientContext.request()
34+
* .path("hello/world")
35+
* .GET()
36+
* .call().asVoid();
37+
*
38+
* }</pre>
39+
*
40+
* @return The HttpCall to allow sync or async execution
41+
*/
42+
HttpCall<HttpResponse<Void>> asVoid();
43+
1344
/**
1445
* Process discarding response body as {@literal HttpResponse<Void>}.
46+
* <p>
47+
* Unlike {@link #asVoid()} this will discard any response body including
48+
* any error response body. We should instead use {@link #asVoid()} if we
49+
* might get an error response body that we want to read via
50+
* for example {@link HttpException#bean(Class)}.
1551
*
1652
* <pre>{@code
1753
*

client/src/main/java/io/avaje/http/client/HttpClientResponse.java

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -98,18 +98,21 @@ public interface HttpClientResponse {
9898
*/
9999
HttpResponse<Void> asVoid();
100100

101-
/**
102-
* Return the content as string.
103-
*/
104-
HttpResponse<String> asString();
105-
106101
/**
107102
* Return the response discarding the response content.
108103
* <p>
109-
* Unlike {@link #asVoid()} this does not check the response status code.
104+
* Unlike {@link #asVoid()} this will discard any response body including
105+
* any error response body. We should instead use {@link #asVoid()} if we
106+
* might get an error response body that we want to read via
107+
* for example {@link HttpException#bean(Class)}.
110108
*/
111109
HttpResponse<Void> asDiscarding();
112110

111+
/**
112+
* Return the content as string.
113+
*/
114+
HttpResponse<String> asString();
115+
113116
/**
114117
* Return the content as InputStream.
115118
*/

client/src/test/java/io/avaje/http/client/HelloControllerTest.java

Lines changed: 83 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -613,6 +613,66 @@ void asyncAsVoid_extractError() throws InterruptedException {
613613
}
614614
}
615615

616+
@Test
617+
void callAsVoid_async_extractError() throws InterruptedException {
618+
AtomicReference<HttpException> ref = new AtomicReference<>();
619+
620+
final CompletableFuture<HttpResponse<Void>> future =
621+
clientContext.request()
622+
.path("hello/saveform")
623+
.formParam("email", "user@foo.com")
624+
.formParam("url", "notAValidUrl")
625+
.POST()
626+
.call()
627+
.asVoid()
628+
.async()
629+
.whenComplete((hres, throwable) -> {
630+
631+
final HttpException cause = (HttpException) throwable.getCause();
632+
ref.set(cause);
633+
634+
final HttpResponse<?> httpResponse = cause.getHttpResponse();
635+
assertNotNull(httpResponse);
636+
assertEquals(422, httpResponse.statusCode());
637+
638+
final ErrorResponse errorResponse = cause.bean(ErrorResponse.class);
639+
final Map<String, String> errorMap = errorResponse.getErrors();
640+
assertThat(errorMap.get("url")).isEqualTo("must be a valid URL");
641+
assertThat(errorMap.get("name")).isEqualTo("must not be null");
642+
});
643+
644+
try {
645+
future.get();
646+
} catch (ExecutionException e) {
647+
assertThat(ref.get()).isNotNull();
648+
}
649+
}
650+
651+
@Test
652+
void callAsVoid_extractError() {
653+
try {
654+
clientContext.request()
655+
.path("hello/saveform")
656+
.formParam("email", "user@foo.com")
657+
.formParam("url", "notAValidUrl")
658+
.POST()
659+
.call()
660+
.asVoid()
661+
.execute();
662+
663+
fail();
664+
} catch (HttpException e) {
665+
final HttpResponse<?> httpResponse = e.getHttpResponse();
666+
assertNotNull(httpResponse);
667+
assertEquals(422, httpResponse.statusCode());
668+
669+
final ErrorResponse errorResponse = e.bean(ErrorResponse.class);
670+
final Map<String, String> errorMap = errorResponse.getErrors();
671+
assertThat(errorMap.get("url")).isEqualTo("must be a valid URL");
672+
assertThat(errorMap.get("name")).isEqualTo("must not be null");
673+
}
674+
}
675+
616676
@Test
617677
void postForm_asBytes_validation_expect_badRequest_extractError() {
618678
try {
@@ -655,16 +715,37 @@ void delete() {
655715
assertThat(res.statusCode()).isEqualTo(204);
656716
}
657717

718+
@Test
719+
void callAsVoid() {
720+
final HttpResponse<Void> res =
721+
clientContext.request()
722+
.path("hello/52")
723+
.DELETE()
724+
.call().asVoid().execute();
725+
726+
assertThat(res.statusCode()).isEqualTo(204);
727+
}
728+
729+
@Test
730+
void callAsVoid_async() throws ExecutionException, InterruptedException {
731+
final HttpResponse<Void> res =
732+
clientContext.request()
733+
.path("hello/52")
734+
.DELETE()
735+
.call().asVoid().async().get();
736+
737+
assertThat(res.statusCode()).isEqualTo(204);
738+
}
658739

659740
@Test
660741
void callAsDiscarding() {
661-
final HttpResponse<Void> res2 =
742+
final HttpResponse<Void> res =
662743
clientContext.request()
663744
.path("hello/52")
664745
.DELETE()
665746
.call().asDiscarding().execute();
666747

667-
assertThat(res2.statusCode()).isEqualTo(204);
748+
assertThat(res.statusCode()).isEqualTo(204);
668749
}
669750

670751
@Test

0 commit comments

Comments
 (0)