Skip to content

Commit 0439021

Browse files
authored
Merge pull request #140 from avaje/feature/client-codegen-as-methods
[http-client] Add code generation for the as() methods returning HttpResponse<E> etc
2 parents 84f6705 + a170be2 commit 0439021

File tree

5 files changed

+222
-12
lines changed

5 files changed

+222
-12
lines changed

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

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,36 @@ public <E> HttpCall<E> bean(Class<E> type) {
5555
return new CallBean<>(type);
5656
}
5757

58+
@Override
59+
public <E> HttpCall<HttpResponse<E>> as(Class<E> type) {
60+
return new CallAs<>(type);
61+
}
62+
63+
@Override
64+
public <E> HttpCall<HttpResponse<E>> as(ParameterizedType type) {
65+
return new CallAs<>(type);
66+
}
67+
68+
@Override
69+
public <E> HttpCall<HttpResponse<List<E>>> asList(Class<E> type) {
70+
return new CallAsList<>(type);
71+
}
72+
73+
@Override
74+
public <E> HttpCall<HttpResponse<List<E>>> asList(ParameterizedType type) {
75+
return new CallAsList<>(type);
76+
}
77+
78+
@Override
79+
public <E> HttpCall<HttpResponse<Stream<E>>> asStream(Class<E> type) {
80+
return new CallAsStream<>(type);
81+
}
82+
83+
@Override
84+
public <E> HttpCall<HttpResponse<Stream<E>>> asStream(ParameterizedType type) {
85+
return new CallAsStream<>(type);
86+
}
87+
5888
@Override
5989
public <E> HttpCall<List<E>> list(Class<E> type) {
6090
return new CallList<>(type);
@@ -85,6 +115,7 @@ private class CallVoid implements HttpCall<HttpResponse<Void>> {
85115
public HttpResponse<Void> execute() {
86116
return request.asVoid();
87117
}
118+
88119
@Override
89120
public CompletableFuture<HttpResponse<Void>> async() {
90121
return request.async().asVoid();
@@ -96,6 +127,7 @@ private class CallDiscarding implements HttpCall<HttpResponse<Void>> {
96127
public HttpResponse<Void> execute() {
97128
return request.asDiscarding();
98129
}
130+
99131
@Override
100132
public CompletableFuture<HttpResponse<Void>> async() {
101133
return request.async().asDiscarding();
@@ -107,6 +139,7 @@ private class CallString implements HttpCall<HttpResponse<String>> {
107139
public HttpResponse<String> execute() {
108140
return request.asString();
109141
}
142+
110143
@Override
111144
public CompletableFuture<HttpResponse<String>> async() {
112145
return request.async().asString();
@@ -118,6 +151,7 @@ private class CallBytes implements HttpCall<HttpResponse<byte[]>> {
118151
public HttpResponse<byte[]> execute() {
119152
return request.asByteArray();
120153
}
154+
121155
@Override
122156
public CompletableFuture<HttpResponse<byte[]>> async() {
123157
return request.async().asByteArray();
@@ -129,6 +163,7 @@ private class CallLines implements HttpCall<HttpResponse<Stream<String>>> {
129163
public HttpResponse<Stream<String>> execute() {
130164
return request.asLines();
131165
}
166+
132167
@Override
133168
public CompletableFuture<HttpResponse<Stream<String>>> async() {
134169
return request.async().asLines();
@@ -140,12 +175,97 @@ private class CallInputStream implements HttpCall<HttpResponse<InputStream>> {
140175
public HttpResponse<InputStream> execute() {
141176
return request.asInputStream();
142177
}
178+
143179
@Override
144180
public CompletableFuture<HttpResponse<InputStream>> async() {
145181
return request.async().asInputStream();
146182
}
147183
}
148184

185+
private class CallAs<E> implements HttpCall<HttpResponse<E>> {
186+
private final Class<E> type;
187+
private final ParameterizedType genericType;
188+
private final boolean isGeneric;
189+
190+
CallAs(Class<E> type) {
191+
this.isGeneric = false;
192+
this.type = type;
193+
this.genericType = null;
194+
}
195+
196+
CallAs(ParameterizedType type) {
197+
this.isGeneric = true;
198+
this.type = null;
199+
this.genericType = type;
200+
}
201+
202+
@Override
203+
public HttpResponse<E> execute() {
204+
return isGeneric ? request.as(genericType) : request.as(type);
205+
}
206+
207+
@Override
208+
public CompletableFuture<HttpResponse<E>> async() {
209+
return isGeneric ? request.async().as(genericType) : request.async().as(type);
210+
}
211+
}
212+
213+
private class CallAsList<E> implements HttpCall<HttpResponse<List<E>>> {
214+
private final Class<E> type;
215+
private final ParameterizedType genericType;
216+
private final boolean isGeneric;
217+
218+
CallAsList(Class<E> type) {
219+
this.isGeneric = false;
220+
this.type = type;
221+
this.genericType = null;
222+
}
223+
224+
CallAsList(ParameterizedType type) {
225+
this.isGeneric = true;
226+
this.type = null;
227+
this.genericType = type;
228+
}
229+
230+
@Override
231+
public HttpResponse<List<E>> execute() {
232+
return isGeneric ? request.asList(genericType) : request.asList(type);
233+
}
234+
235+
@Override
236+
public CompletableFuture<HttpResponse<List<E>>> async() {
237+
return isGeneric ? request.async().asList(genericType) : request.async().asList(type);
238+
}
239+
}
240+
241+
private class CallAsStream<E> implements HttpCall<HttpResponse<Stream<E>>> {
242+
private final Class<E> type;
243+
private final ParameterizedType genericType;
244+
private final boolean isGeneric;
245+
246+
CallAsStream(Class<E> type) {
247+
this.isGeneric = false;
248+
this.type = type;
249+
this.genericType = null;
250+
}
251+
252+
CallAsStream(ParameterizedType type) {
253+
this.isGeneric = true;
254+
this.type = null;
255+
this.genericType = type;
256+
}
257+
258+
@Override
259+
public HttpResponse<Stream<E>> execute() {
260+
return isGeneric ? request.asStream(genericType) : request.asStream(type);
261+
}
262+
263+
@Override
264+
public CompletableFuture<HttpResponse<Stream<E>>> async() {
265+
return isGeneric ? request.async().asStream(genericType) : request.async().asStream(type);
266+
}
267+
}
268+
149269
private class CallBean<E> implements HttpCall<E> {
150270
private final Class<E> type;
151271
private final ParameterizedType genericType;
@@ -232,13 +352,16 @@ public CompletableFuture<Stream<E>> async() {
232352

233353
private class CallHandler<E> implements HttpCall<HttpResponse<E>> {
234354
private final HttpResponse.BodyHandler<E> handler;
355+
235356
CallHandler(HttpResponse.BodyHandler<E> handler) {
236357
this.handler = handler;
237358
}
359+
238360
@Override
239361
public HttpResponse<E> execute() {
240362
return request.handler(handler);
241363
}
364+
242365
@Override
243366
public CompletableFuture<HttpResponse<E>> async() {
244367
return request.async().handler(handler);

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

Lines changed: 49 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,52 @@ default <E> HttpCall<HttpResponse<E>> withHandler(HttpResponse.BodyHandler<E> bo
126126
return handler(bodyHandler);
127127
}
128128

129+
/**
130+
* A bean response to execute async or sync.
131+
* <p>
132+
* If the HTTP statusCode is not in the 2XX range a HttpException is throw which contains
133+
* the HttpResponse. This is the cause in the CompletionException.
134+
*
135+
* <pre>{@code
136+
*
137+
* HttpCall<HttpResponse<HelloDto>> call =
138+
* client.request()
139+
* ...
140+
* .POST()
141+
* .call().as(HelloDto.class);
142+
*
143+
* }</pre>
144+
*
145+
* @param type The bean type to convert the content to
146+
* @return The HttpCall to allow sync or async execution
147+
*/
148+
<E> HttpCall<HttpResponse<E>> as(Class<E> type);
149+
150+
/**
151+
* Same as {@link #as(Class)} but takes a generic parameterized type.
152+
*/
153+
<E> HttpCall<HttpResponse<E>> as(ParameterizedType type);
154+
155+
/**
156+
* Same as {@link #as(Class)} but returns {@code HttpResponse<List<E>>}.
157+
*/
158+
<E> HttpCall<HttpResponse<List<E>>> asList(Class<E> type);
159+
160+
/**
161+
* Same as {@link #as(Class)} but returns {@code HttpResponse<List<E>>}.
162+
*/
163+
<E> HttpCall<HttpResponse<List<E>>> asList(ParameterizedType type);
164+
165+
/**
166+
* Same as {@link #as(Class)} but returns {@code HttpResponse<Stream<E>>}.
167+
*/
168+
<E> HttpCall<HttpResponse<Stream<E>>> asStream(Class<E> type);
169+
170+
/**
171+
* Same as {@link #as(Class)} but returns {@code HttpResponse<Stream<E>>}.
172+
*/
173+
<E> HttpCall<HttpResponse<Stream<E>>> asStream(ParameterizedType type);
174+
129175
/**
130176
* A bean response to execute async or sync.
131177
* <p>
@@ -135,7 +181,7 @@ default <E> HttpCall<HttpResponse<E>> withHandler(HttpResponse.BodyHandler<E> bo
135181
* <pre>{@code
136182
*
137183
* HttpCall<HelloDto> call =
138-
* clientContext.request()
184+
* client.request()
139185
* ...
140186
* .POST()
141187
* .call().bean(HelloDto.class);
@@ -156,7 +202,7 @@ default <E> HttpCall<HttpResponse<E>> withHandler(HttpResponse.BodyHandler<E> bo
156202
* <pre>{@code
157203
*
158204
* HttpCall<List<HelloDto>> call =
159-
* clientContext.request()
205+
* client.request()
160206
* ...
161207
* .GET()
162208
* .call().list(HelloDto.class);
@@ -176,7 +222,7 @@ default <E> HttpCall<HttpResponse<E>> withHandler(HttpResponse.BodyHandler<E> bo
176222
* <pre>{@code
177223
*
178224
* HttpCall<Stream<HelloDto>> call =
179-
* clientContext.request()
225+
* client.request()
180226
* ...
181227
* .GET()
182228
* .call().stream(HelloDto.class);

http-generator-client/src/main/java/io/avaje/http/generator/client/ClientMethodWriter.java

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,20 @@ private void writeResponse(UType type) {
137137
writer.append(".stream(");
138138
writeGeneric(param1);
139139
} else if (isHttpResponse(mainType)) {
140-
writeWithHandler();
140+
if (bodyHandlerParam == null) {
141+
UType paramType = type.paramRaw();
142+
if (paramType.mainType().equals("java.util.List")) {
143+
writer.append(".asList(");
144+
writeGeneric(paramType.paramRaw());
145+
} else if (paramType.mainType().equals("java.util.stream.Stream")) {
146+
writer.append(".asStream(");
147+
writeGeneric(paramType.paramRaw());
148+
} else {
149+
writer.append(".as(");
150+
writeGeneric(paramType);
151+
}
152+
} else {
153+
writer.append(".handler(%s);", bodyHandlerParam.name()).eol(); }
141154
} else {
142155
writer.append(".bean(");
143156
writeGeneric(type);
@@ -159,14 +172,6 @@ void writeGeneric(UType type) {
159172
writer.append(");").eol();
160173
}
161174

162-
private void writeWithHandler() {
163-
if (bodyHandlerParam != null) {
164-
writer.append(".handler(%s);", bodyHandlerParam.name()).eol();
165-
} else {
166-
writer.append(".handler(responseHandler);").eol(); // Better to barf here?
167-
}
168-
}
169-
170175
private void writeQueryParams(PathSegments pathSegments) {
171176
for (MethodParam param : method.params()) {
172177
ParamType paramType = param.paramType();

http-generator-core/src/main/java/io/avaje/http/generator/core/UType.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,7 @@ private String extractRawParam() {
165165
case "java.util.Set":
166166
case "java.util.List":
167167
case "java.util.stream.Stream":
168+
case "java.net.http.HttpResponse":
168169
case "java.util.concurrent.CompletableFuture":
169170
case "io.avaje.http.client.HttpCall":
170171
var first = rawType.indexOf("<") + 1;
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package org.example;
2+
3+
import io.avaje.http.api.Client;
4+
import io.avaje.http.api.Get;
5+
import io.avaje.http.client.HttpCall;
6+
7+
import java.net.http.HttpResponse;
8+
import java.util.List;
9+
import java.util.UUID;
10+
import java.util.stream.Stream;
11+
12+
@Client
13+
public interface WithAsResponseApi {
14+
15+
@Get("/{id}")
16+
Repo get(UUID id);
17+
18+
@Get("/as/{id}")
19+
HttpResponse<Repo> getAs(UUID id);
20+
21+
@Get("/as-list/{id}")
22+
HttpResponse<List<Repo>> getAsList(UUID id);
23+
24+
@Get("/as-stream/{id}")
25+
HttpResponse<Stream<Repo>> getAsStream(UUID id);
26+
27+
@Get("/call-as/{id}")
28+
HttpCall<HttpResponse<Repo>> getCallAs(UUID id);
29+
30+
@Get("/call-as-list/{id}")
31+
HttpCall<HttpResponse<List<Repo>>> getCallAsList(UUID id);
32+
33+
@Get("/call-as-stream/{id}")
34+
HttpCall<HttpResponse<Stream<Repo>>> getCallAsStream(UUID id);
35+
}

0 commit comments

Comments
 (0)