Skip to content

Commit f791cea

Browse files
committed
For body(Path file) throw IllegalArgumentException
Going with the API assumption that file existence and file permission is checked in application side prior to setting as body content. In that sense it is nicer if the API does not force the user to again handle FileNotFoundException.
1 parent e05a1a4 commit f791cea

File tree

11 files changed

+262
-15
lines changed

11 files changed

+262
-15
lines changed

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

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
import java.net.http.HttpResponse;
1212
import java.nio.charset.StandardCharsets;
1313
import java.nio.file.Path;
14-
import java.time.*;
14+
import java.time.Duration;
1515
import java.util.*;
1616
import java.util.concurrent.CompletableFuture;
1717
import java.util.function.Supplier;
@@ -212,9 +212,13 @@ public HttpClientRequest body(Supplier<? extends InputStream> streamSupplier) {
212212
}
213213

214214
@Override
215-
public HttpClientRequest body(Path file) throws FileNotFoundException {
216-
this.body = HttpRequest.BodyPublishers.ofFile(file);
217-
return this;
215+
public HttpClientRequest body(Path file) {
216+
try {
217+
this.body = HttpRequest.BodyPublishers.ofFile(file);
218+
return this;
219+
} catch (FileNotFoundException e) {
220+
throw new IllegalArgumentException("File not found " + file, e);
221+
}
218222
}
219223

220224
@Override

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,7 @@ public interface HttpClientRequest {
236236
* @param file The file to send as body content
237237
* @return The request being built
238238
*/
239-
HttpClientRequest body(Path file) throws FileNotFoundException;
239+
HttpClientRequest body(Path file);
240240

241241
/**
242242
* Set the body content using http BodyPublisher.

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,11 @@ void suppressLogging_listenerEvent_expect_suppressedPayloadContent() {
2525
void skipAuthToken_listenerEvent_expect_suppressedPayloadContent() {
2626

2727
final DHttpClientRequest request = new DHttpClientRequest(mock(DHttpClientContext.class), Duration.ZERO);
28+
assertThat(request.isSkipAuthToken()).isFalse();
2829

2930
request.skipAuthToken();
31+
assertThat(request.isSkipAuthToken()).isTrue();
32+
3033
final RequestListener.Event event = request.listenerEvent();
3134

3235
assertThat(event.requestBody()).isEqualTo("<suppressed request body>");

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

Lines changed: 84 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,16 @@
44
import org.example.webserver.HelloDto;
55
import org.junit.jupiter.api.Test;
66

7+
import java.io.*;
78
import java.net.http.HttpResponse;
9+
import java.nio.file.Path;
10+
import java.time.Duration;
11+
import java.time.LocalDate;
12+
import java.time.ZoneId;
813
import java.util.ArrayList;
914
import java.util.List;
1015
import java.util.Map;
16+
import java.util.UUID;
1117
import java.util.concurrent.CompletableFuture;
1218
import java.util.concurrent.CompletionException;
1319
import java.util.concurrent.ExecutionException;
@@ -24,6 +30,56 @@ class HelloControllerTest extends BaseWebTest {
2430

2531
final HttpClientContext clientContext = client();
2632

33+
@Test
34+
void asLines() {
35+
final HttpResponse<Stream<String>> hres =
36+
clientContext.request()
37+
.path("hello").path("stream")
38+
.GET()
39+
.asLines();
40+
41+
assertThat(hres.statusCode()).isEqualTo(200);
42+
final List<String> lines = hres.body().collect(Collectors.toList());
43+
44+
assertThat(lines).hasSize(4);
45+
assertThat(lines.get(0)).contains("{\"id\":1, \"name\":\"one\"}");
46+
}
47+
48+
@Test
49+
void asInputStream() throws IOException {
50+
final HttpResponse<InputStream> hres =
51+
clientContext.request()
52+
.path("hello").path("stream")
53+
.GET()
54+
.asInputStream();
55+
56+
assertThat(hres.statusCode()).isEqualTo(200);
57+
final LineNumberReader reader = new LineNumberReader(new InputStreamReader(hres.body()));
58+
59+
List<String> allLines = new ArrayList<>();
60+
String line;
61+
while ((line = reader.readLine()) != null) {
62+
allLines.add(line);
63+
}
64+
assertThat(allLines).hasSize(4);
65+
assertThat(allLines.get(0)).contains("{\"id\":1, \"name\":\"one\"}");
66+
}
67+
68+
@Test
69+
void asFile() throws IOException {
70+
final Path path = File.createTempFile("test", "dump").toPath();
71+
final HttpResponse<Path> hres =
72+
clientContext.request()
73+
.path("hello").path("stream")
74+
.GET()
75+
.asFile(path);
76+
77+
assertThat(hres.statusCode()).isEqualTo(200);
78+
assertThat(path.toFile().length()).isGreaterThan(90L);
79+
80+
assertTrue(path.toFile().delete());
81+
}
82+
2783
@Test
2884
void get_stream() {
2985

@@ -160,6 +216,23 @@ void get_helloMessage() {
160216
assertThat(hres.statusCode()).isEqualTo(200);
161217
}
162218

219+
@Test
220+
void get_notFound() {
221+
UUID nullUUID = null;
222+
final HttpClientRequest request = clientContext.request()
223+
.path("hello").path(UUID.randomUUID()).queryParam("zone", ZoneId.of("UTC"))
224+
.header("X-Zone", ZoneId.of("UTC"))
225+
.header("X-Foo", nullUUID);
226+
227+
assertThat(request.header("X-Zone")).containsExactly("UTC");
228+
assertThat(request.header("X-Foo")).isEmpty();
229+
230+
final HttpResponse<String> hres = request.GET().asString();
231+
232+
assertThat(hres.statusCode()).isEqualTo(404);
233+
assertThat(hres.body()).contains("Not found");
234+
}
235+
163236
@Test
164237
void callString() {
165238
final HttpResponse<String> hres = clientContext.request()
@@ -499,12 +572,16 @@ void post_bean_returningVoid() {
499572
@Test
500573
void postForm() {
501574

575+
UUID nullUUID = null;
576+
502577
final HttpResponse<Void> res = clientContext.request()
503578
.path("hello/saveform")
504579
.formParam("name", "Bazz")
505580
.formParam("email", "user@foo.com")
506581
.formParam("url", "http://foo.com")
507-
.formParam("startDate", "2030-12-03")
582+
.formParam("startDate", LocalDate.of(2030, 12, 03))
583+
.formParam("setToNull", null)
584+
.formParam("nullUid", nullUUID)
508585
.POST()
509586
.asDiscarding();
510587

@@ -741,7 +818,7 @@ void callAsVoid_async() throws ExecutionException, InterruptedException {
741818
void callAsDiscarding() {
742819
final HttpResponse<Void> res =
743820
clientContext.request()
744-
.path("hello/52")
821+
.path("hello").path(52)
745822
.DELETE()
746823
.call().asDiscarding().execute();
747824

@@ -751,7 +828,7 @@ void callAsDiscarding() {
751828
@Test
752829
void callAsDiscardingAsync() throws ExecutionException, InterruptedException {
753830
final CompletableFuture<HttpResponse<Void>> future = clientContext.request()
754-
.path("hello/52")
831+
.path("hello").path(52L)
755832
.DELETE()
756833
.call().asDiscarding().async();
757834

@@ -761,18 +838,20 @@ void callAsDiscardingAsync() throws ExecutionException, InterruptedException {
761838

762839
@Test
763840
void get_withMatrixParam() {
764-
765841
final HttpResponse<String> httpRes = clientContext.request()
842+
.requestTimeout(Duration.ofSeconds(40))
766843
.path("hello/withMatrix/2011")
767844
.matrixParam("author", "rob")
768845
.matrixParam("country", "nz")
846+
.matrixParam("zone", ZoneId.of("UTC"))
769847
.path("foo")
770848
.queryParam("extra", "banana")
849+
.matrixParam("setToNull", null)
771850
.GET()
772851
.asString();
773852

774853
assertEquals(200, httpRes.statusCode());
775-
assertEquals("yr:2011 au:rob co:nz other:foo extra:banana", httpRes.body());
854+
assertEquals("yr:2011 au:rob co:nz zone:UTC other:foo extra:banana", httpRes.body());
776855
}
777856

778857
public static class SimpleData {
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
package io.avaje.http.client;
2+
3+
import com.fasterxml.jackson.databind.ObjectMapper;
4+
import org.example.webserver.HelloDto;
5+
import org.junit.jupiter.api.Test;
6+
7+
import java.net.http.HttpResponse;
8+
9+
import static org.assertj.core.api.Assertions.assertThat;
10+
11+
class RequestListenerTest extends BaseWebTest {
12+
13+
static class TDRequestListener implements RequestListener {
14+
final boolean hasBody;
15+
16+
TDRequestListener(boolean hasBody) {
17+
this.hasBody = hasBody;
18+
}
19+
20+
@Override
21+
public void response(Event event) {
22+
if (hasBody) {
23+
assertThat(event.responseBody()).isEqualTo("post");
24+
assertThat(event.uri().toString()).isEqualTo("http://localhost:8887/post");
25+
assertThat(event.requestBody()).isEqualTo("post-request-body");
26+
} else {
27+
assertThat(event.responseBody()).isEqualTo("hello world");
28+
assertThat(event.uri().toString()).isEqualTo("http://localhost:8887/hello/message");
29+
assertThat(event.requestBody()).isNull();
30+
}
31+
assertThat(event.responseTimeNanos()).isGreaterThan(1L);
32+
assertThat(event.response().statusCode()).isEqualTo(200);
33+
assertThat(event.request()).isEqualTo(event.response().request());
34+
}
35+
}
36+
37+
private HttpClientContext createClient(TDRequestListener tdRequestListener) {
38+
return HttpClientContext.newBuilder()
39+
.withBaseUrl(baseUrl)
40+
.withRequestListener(new RequestLogger())
41+
.withBodyAdapter(new JacksonBodyAdapter(new ObjectMapper()))
42+
.withRequestListener(tdRequestListener)
43+
.build();
44+
}
45+
46+
@Test
47+
void get_no_request_body() {
48+
final TDRequestListener tdRequestListener = new TDRequestListener(false);
49+
final HttpClientContext client = createClient(tdRequestListener);
50+
51+
final HttpResponse<String> hres = client.request()
52+
.path("hello").path("message")
53+
.GET().asString();
54+
55+
assertThat(hres.body()).contains("hello world");
56+
assertThat(hres.statusCode()).isEqualTo(200);
57+
}
58+
59+
@Test
60+
void post() {
61+
final TDRequestListener tdRequestListener = new TDRequestListener(true);
62+
final HttpClientContext client = createClient(tdRequestListener);
63+
64+
final HttpResponse<String> hres = client.request()
65+
.path("post")
66+
.body("post-request-body")
67+
.POST().asString();
68+
69+
assertThat(hres.body()).contains("post");
70+
assertThat(hres.statusCode()).isEqualTo(200);
71+
}
72+
73+
}

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,15 @@ void matrixParam() {
4343
assertThat(url).isEqualTo("https://foo/bar;a=one;b=two");
4444
}
4545

46+
@Test
47+
void matrixParam_object() {
48+
UUID uid = UUID.randomUUID();
49+
final String url = foo().path("bar").matrixParam("a", uid)
50+
.build();
51+
52+
assertThat(url).isEqualTo("https://foo/bar;a=" + uid);
53+
}
54+
4655
@Test
4756
void matrixParam_null() {
4857
final String url = foo().path("bar").matrixParam("a", null).matrixParam("b", "two")

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

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,19 @@
33
import org.junit.jupiter.api.Disabled;
44
import org.junit.jupiter.api.Test;
55

6+
import java.io.InputStream;
7+
import java.net.URI;
8+
import java.net.URISyntaxException;
9+
import java.net.URL;
10+
import java.net.http.HttpRequest;
611
import java.net.http.HttpResponse;
12+
import java.nio.charset.StandardCharsets;
13+
import java.nio.file.Path;
14+
import java.util.function.Supplier;
715

816
import static org.assertj.core.api.Assertions.assertThat;
17+
import static org.junit.jupiter.api.Assertions.assertThrows;
18+
import static org.junit.jupiter.api.Assertions.fail;
919

1020
public class VerbTest extends BaseWebTest {
1121

@@ -89,6 +99,62 @@ void delete_with_body() {
8999
assertThat(res.body()).isEqualTo("delete body[dummy]");
90100
}
91101

102+
@Test
103+
void delete_with_body_bytes() {
104+
105+
HttpResponse<String> res = clientContext.request()
106+
.path("delete")
107+
.body("dummyBytes".getBytes(StandardCharsets.UTF_8))
108+
.DELETE().asString();
109+
110+
assertThat(res.body()).isEqualTo("delete body[dummyBytes]");
111+
}
112+
113+
@Test
114+
void delete_with_body_BodyPublishers() {
115+
116+
HttpResponse<String> res = clientContext.request()
117+
.path("delete")
118+
.body(HttpRequest.BodyPublishers.ofString("dummyBodyPublishers"))
119+
.DELETE().asString();
120+
121+
assertThat(res.body()).isEqualTo("delete body[dummyBodyPublishers]");
122+
}
123+
124+
@Test
125+
void delete_with_body_Path() throws URISyntaxException {
126+
127+
final URL resource = getClass().getResource("/dummy.txt");
128+
HttpResponse<String> res = clientContext.request()
129+
.path("delete")
130+
.body(Path.of(resource.toURI()))
131+
.DELETE().asString();
132+
133+
assertThat(res.body()).isEqualTo("delete body[dummyFileContent]");
134+
}
135+
136+
@Test
137+
void delete_with_body_Path_NotFound_expects_IllegalArgumentException() {
138+
139+
final IllegalArgumentException e = assertThrows(IllegalArgumentException.class, () ->
140+
clientContext.request()
141+
.path("delete")
142+
.body(Path.of(URI.create("file:///file-does-not-exist.txt")))
143+
.DELETE().asString());
144+
145+
assertThat(e.getMessage()).startsWith("File not found");
146+
}
147+
148+
@Test
149+
void delete_with_body_InputStream() {
150+
HttpResponse<String> res = clientContext.request()
151+
.path("delete")
152+
.body(() -> getClass().getResourceAsStream("/dummy.txt"))
153+
.DELETE().asString();
154+
155+
assertThat(res.body()).isEqualTo("delete body[dummyFileContent]");
156+
}
157+
92158
@Test
93159
void head() {
94160

@@ -112,4 +178,14 @@ void get() {
112178
assertThat(res.body()).isEqualTo("get");
113179
}
114180

181+
@Test
182+
void get_BodyHandler_null_expect() {
183+
184+
HttpResponse<String> res = clientContext.request()
185+
.path("post")
186+
.body((HttpRequest.BodyPublisher)null)
187+
.POST().asString();
188+
189+
assertThat(res.body()).isEqualTo("post");
190+
}
115191
}

0 commit comments

Comments
 (0)