Skip to content

[http-client] ENH: Add response as() methods that return HttpResponse wrapping bean, list and stream #132

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

Merged
merged 1 commit into from
Jan 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ default <T> BodyReader<T> beanReader(ParameterizedType type) {
throw new UnsupportedOperationException("Parameterized types not supported for this adapter");
}


/**
* Return a BodyReader to read response content and convert to a list of beans.
*
Expand Down
71 changes: 50 additions & 21 deletions http-client/src/main/java/io/avaje/http/client/DHttpAsync.java
Original file line number Diff line number Diff line change
Expand Up @@ -61,43 +61,72 @@ public CompletableFuture<HttpResponse<InputStream>> asInputStream() {

@Override
public <E> CompletableFuture<E> bean(Class<E> type) {
return request
.performSendAsync(true, HttpResponse.BodyHandlers.ofByteArray())
.thenApply(httpResponse -> request.asyncBean(type, httpResponse));
return as(type).thenApply(HttpResponse::body);
}

@Override
public <E> CompletableFuture<List<E>> list(Class<E> type) {
return request
.performSendAsync(true, HttpResponse.BodyHandlers.ofByteArray())
.thenApply(httpResponse -> request.asyncList(type, httpResponse));
public <E> CompletableFuture<E> bean(ParameterizedType type) {
final CompletableFuture<HttpResponse<E>> future = as(type);
return future.thenApply(HttpResponse::body);
}

@Override
public <E> CompletableFuture<Stream<E>> stream(Class<E> type) {
return request
.performSendAsync(false, HttpResponse.BodyHandlers.ofLines())
.thenApply(httpResponse -> request.asyncStream(type, httpResponse));
public <E> CompletableFuture<HttpResponse<E>> as(Class<E> type) {
return asyncAsBytes().thenApply(httpResponse -> request.asyncBean(type, httpResponse));
}

@Override
public <E> CompletableFuture<E> bean(ParameterizedType type) {
return request
.performSendAsync(true, HttpResponse.BodyHandlers.ofByteArray())
.thenApply(httpResponse -> request.asyncBean(type, httpResponse));
public <E> CompletableFuture<HttpResponse<E>> as(ParameterizedType type) {
return asyncAsBytes().thenApply(httpResponse -> request.asyncBean(type, httpResponse));
}

@Override
public <E> CompletableFuture<List<E>> list(Class<E> type) {
return asList(type).thenApply(HttpResponse::body);
}

@Override
public <E> CompletableFuture<List<E>> list(ParameterizedType type) {
return request
.performSendAsync(true, HttpResponse.BodyHandlers.ofByteArray())
.thenApply(httpResponse -> request.asyncList(type, httpResponse));
final CompletableFuture<HttpResponse<List<E>>> future = asList(type);
return future.thenApply(HttpResponse::body);
}

@Override
public <E> CompletableFuture<HttpResponse<List<E>>> asList(Class<E> type) {
return asyncAsBytes().thenApply(httpResponse -> request.asyncList(type, httpResponse));
}

@Override
public <E> CompletableFuture<HttpResponse<List<E>>> asList(ParameterizedType type) {
return asyncAsBytes().thenApply(httpResponse -> request.asyncList(type, httpResponse));
}

@Override
public <E> CompletableFuture<Stream<E>> stream(Class<E> type) {
return asStream(type).thenApply(HttpResponse::body);
}

@Override
public <E> CompletableFuture<Stream<E>> stream(ParameterizedType type) {
return request
.performSendAsync(false, HttpResponse.BodyHandlers.ofLines())
.thenApply(httpResponse -> request.asyncStream(type, httpResponse));
final CompletableFuture<HttpResponse<Stream<E>>> future = asStream(type);
return future.thenApply(HttpResponse::body);
}

@Override
public <E> CompletableFuture<HttpResponse<Stream<E>>> asStream(Class<E> type) {
return asyncAsLines().thenApply(httpResponse -> request.asyncStream(type, httpResponse));
}

@Override
public <E> CompletableFuture<HttpResponse<Stream<E>>> asStream(ParameterizedType type) {
return asyncAsLines().thenApply(httpResponse -> request.asyncStream(type, httpResponse));
}

private CompletableFuture<HttpResponse<byte[]>> asyncAsBytes() {
return request.performSendAsync(true, HttpResponse.BodyHandlers.ofByteArray());
}

private CompletableFuture<HttpResponse<Stream<String>>> asyncAsLines() {
return request.performSendAsync(false, HttpResponse.BodyHandlers.ofLines());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -269,29 +269,30 @@ <T> BodyContent write(T bean, String contentType) {
return bodyAdapter.beanWriter(bean.getClass()).write(bean, contentType);
}

<T> BodyReader<T> beanReader(Class<T> cls) {
return bodyAdapter.beanReader(cls);
<T> BodyReader<T> beanReader(Class<T> type) {
return bodyAdapter.beanReader(type);
}

<T> BodyReader<T> beanReader(ParameterizedType cls) {
return bodyAdapter.beanReader(cls);
<T> BodyReader<T> beanReader(ParameterizedType type) {
return bodyAdapter.beanReader(type);
}

<T> T readBean(Class<T> cls, BodyContent content) {
return bodyAdapter.beanReader(cls).read(content);
<T> T readBean(Class<T> type, BodyContent content) {
return bodyAdapter.beanReader(type).read(content);
}

<T> List<T> readList(Class<T> cls, BodyContent content) {
return bodyAdapter.listReader(cls).read(content);
<T> List<T> readList(Class<T> type, BodyContent content) {
return bodyAdapter.listReader(type).read(content);
}

@SuppressWarnings("unchecked")
<T> T readBean(ParameterizedType cls, BodyContent content) {
return (T) bodyAdapter.beanReader(cls).read(content);
<T> T readBean(ParameterizedType type, BodyContent content) {
return (T) bodyAdapter.beanReader(type).read(content);
}

<T> List<T> readList(ParameterizedType cls, BodyContent content) {
return (List<T>) bodyAdapter.listReader(cls).read(content);
@SuppressWarnings("unchecked")
<T> List<T> readList(ParameterizedType type, BodyContent content) {
return (List<T>) bodyAdapter.listReader(type).read(content);
}

void afterResponse(DHttpClientRequest request) {
Expand Down
122 changes: 78 additions & 44 deletions http-client/src/main/java/io/avaje/http/client/DHttpClientRequest.java
Original file line number Diff line number Diff line change
Expand Up @@ -414,7 +414,7 @@ private void readResponseContent() {
@Override
public HttpResponse<Void> asVoid() {
readResponseContent();
return new HttpVoidResponse(httpResponse);
return new HttpWrapperResponse<>(httpResponse);
}

@Override
Expand All @@ -424,50 +424,75 @@ public <T> T read(BodyReader<T> reader) {
}

@Override
public <T> T bean(Class<T> cls) {
public <T> HttpResponse<T> as(Class<T> type) {
return new HttpWrapperResponse<>(bean(type), httpResponse);
}

@Override
public <T> HttpResponse<T> as(ParameterizedType type) {
return new HttpWrapperResponse<>(bean(type), httpResponse);
}

@Override
public <T> T bean(Class<T> type) {
readResponseContent();
return context.readBean(cls, encodedResponseBody);
return context.readBean(type, encodedResponseBody);
}

@Override
public <T> List<T> list(Class<T> cls) {
public <T> T bean(ParameterizedType type) {
readResponseContent();
return context.readList(cls, encodedResponseBody);
return context.readBean(type, encodedResponseBody);
}

@Override
public <T> Stream<T> stream(Class<T> cls) {
final HttpResponse<Stream<String>> res = handler(HttpResponse.BodyHandlers.ofLines());
this.httpResponse = res;
if (res.statusCode() >= 300) {
throw new HttpException(res, context);
}
final BodyReader<T> bodyReader = context.beanReader(cls);
return res.body().map(bodyReader::readBody);
public <T> HttpResponse<List<T>> asList(Class<T> type) {
return new HttpWrapperResponse<>(list(type), httpResponse);
}

@Override
public <T> HttpResponse<List<T>> asList(ParameterizedType type) {
return new HttpWrapperResponse<>(list(type), httpResponse);
}

@Override
public <T> T bean(ParameterizedType cls) {
public <T> List<T> list(Class<T> type) {
readResponseContent();
return context.readBean(cls, encodedResponseBody);
return context.readList(type, encodedResponseBody);
}

@Override
public <T> List<T> list(ParameterizedType cls) {
public <T> List<T> list(ParameterizedType type) {
readResponseContent();
return context.readList(cls, encodedResponseBody);
return context.readList(type, encodedResponseBody);
}

@Override
public <T> HttpResponse<Stream<T>> asStream(Class<T> type) {
return new HttpWrapperResponse<>(stream(type), httpResponse);
}

@Override
public <T> HttpResponse<Stream<T>> asStream(ParameterizedType type) {
return new HttpWrapperResponse<>(stream(type), httpResponse);
}

@Override
public <T> Stream<T> stream(Class<T> type) {
return stream(context.beanReader(type));
}

@Override
public <T> Stream<T> stream(ParameterizedType cls) {
public <T> Stream<T> stream(ParameterizedType type) {
return stream(context.beanReader(type));
}

private <T> Stream<T> stream(BodyReader<T> bodyReader) {
final HttpResponse<Stream<String>> res = handler(HttpResponse.BodyHandlers.ofLines());
this.httpResponse = res;
if (res.statusCode() >= 300) {
throw new HttpException(res, context);
}
final BodyReader<T> bodyReader = context.beanReader(cls);
return res.body().map(bodyReader::readBody);
}

Expand All @@ -484,7 +509,7 @@ public <T> HttpResponse<T> handler(HttpResponse.BodyHandler<T> responseHandler)
private <T> HttpResponse<T> sendWith(HttpResponse.BodyHandler<T> responseHandler) {
context.beforeRequest(this);
addHeaders();
HttpResponse<T> response = performSend(responseHandler);
final HttpResponse<T> response = performSend(responseHandler);
httpResponse = response;
return response;
}
Expand All @@ -508,49 +533,49 @@ protected <T> CompletableFuture<HttpResponse<T>> performSendAsync(boolean loggab

protected HttpResponse<Void> asyncVoid(HttpResponse<byte[]> response) {
afterAsyncEncoded(response);
return new HttpVoidResponse(response);
return new HttpWrapperResponse<>(response);
}

protected <E> E asyncBean(Class<E> type, HttpResponse<byte[]> response) {
protected <E> HttpResponse<E> asyncBean(Class<E> type, HttpResponse<byte[]> response) {
afterAsyncEncoded(response);
return new HttpWrapperResponse<>(context.readBean(type, encodedResponseBody), httpResponse);
}

protected <E> E asyncBean(ParameterizedType type, HttpResponse<byte[]> response) {
afterAsyncEncoded(response);
return context.readBean(type, encodedResponseBody);
}

protected <E> List<E> asyncList(Class<E> type, HttpResponse<byte[]> response) {
protected <E> HttpResponse<List<E>> asyncList(Class<E> type, HttpResponse<byte[]> response) {
afterAsyncEncoded(response);
return context.readList(type, encodedResponseBody);
return new HttpWrapperResponse<>(context.readList(type, encodedResponseBody), httpResponse);
}

protected <E> HttpResponse<List<E>> asyncList(ParameterizedType type, HttpResponse<byte[]> response) {
afterAsyncEncoded(response);
return new HttpWrapperResponse<>(context.readList(type, encodedResponseBody), httpResponse);
}

protected <E> Stream<E> asyncStream(Class<E> type, HttpResponse<Stream<String>> response) {
protected <E> HttpResponse<Stream<E>> asyncStream(Class<E> type, HttpResponse<Stream<String>> response) {
responseTimeNanos = System.nanoTime() - startAsyncNanos;
httpResponse = response;
context.afterResponse(this);
if (response.statusCode() >= 300) {
throw new HttpException(response, context);
}
final BodyReader<E> bodyReader = context.beanReader(type);
return response.body().map(bodyReader::readBody);
return new HttpWrapperResponse<>(response.body().map(bodyReader::readBody), httpResponse);
}

protected <E> E asyncBean(ParameterizedType type, HttpResponse<byte[]> response) {
afterAsyncEncoded(response);
return context.readBean(type, encodedResponseBody);
}

protected <E> List<E> asyncList(ParameterizedType type, HttpResponse<byte[]> response) {
afterAsyncEncoded(response);
return context.readList(type, encodedResponseBody);
}

protected <E> Stream<E> asyncStream(ParameterizedType type, HttpResponse<Stream<String>> response) {
protected <E> HttpResponse<Stream<E>> asyncStream(ParameterizedType type, HttpResponse<Stream<String>> response) {
responseTimeNanos = System.nanoTime() - startAsyncNanos;
httpResponse = response;
context.afterResponse(this);
if (response.statusCode() >= 300) {
throw new HttpException(response, context);
}
final BodyReader<E> bodyReader = context.beanReader(type);
return response.body().map(bodyReader::readBody);
return new HttpWrapperResponse<>(response.body().map(bodyReader::readBody), httpResponse);
}

private void afterAsyncEncoded(HttpResponse<byte[]> response) {
Expand Down Expand Up @@ -728,13 +753,20 @@ public String responseBody() {
}
}

static class HttpVoidResponse implements HttpResponse<Void> {
static final class HttpWrapperResponse<B> implements HttpResponse<B> {

private final HttpResponse<?> orig;
private final B body;

HttpWrapperResponse(HttpResponse<?> orig) {
this.orig = orig;
this.body = null;
}

@SuppressWarnings({"raw"})
HttpVoidResponse(HttpResponse<?> orig) {
HttpWrapperResponse(B body, HttpResponse<?> orig) {
this.orig = orig;
this.body = body;
}

@Override
Expand All @@ -747,9 +779,11 @@ public HttpRequest request() {
return orig.request();
}

@SuppressWarnings("unchecked")
@Override
public Optional<HttpResponse<Void>> previousResponse() {
return Optional.empty();
public Optional<HttpResponse<B>> previousResponse() {
final Optional<? extends HttpResponse<?>> previous = orig.previousResponse();
return (Optional<HttpResponse<B>>)previous;
}

@Override
Expand All @@ -758,8 +792,8 @@ public HttpHeaders headers() {
}

@Override
public Void body() {
return null;
public B body() {
return body;
}

@Override
Expand Down
Loading