Skip to content

Commit fa3aee6

Browse files
author
adriancole
committed
issue OpenFeign#55: support iterable query params
1 parent 328fdb5 commit fa3aee6

4 files changed

Lines changed: 63 additions & 22 deletions

File tree

CHANGES.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
### Version 4.4
22
* Support overriding default HostnameVerifier
33
* Support GZIP content encoding for request bodies
4+
* Support Iterable args for query parameters
45

56
### Version 4.3
67
* Add ability to configure zero or more RequestInterceptors.

core/src/main/java/feign/RequestTemplate.java

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -84,11 +84,11 @@ public RequestTemplate(RequestTemplate toCopy) {
8484
* just the URL
8585
*/
8686
public RequestTemplate resolve(Map<String, ?> unencoded) {
87+
replaceQueryValues(unencoded);
8788
Map<String, String> encoded = new LinkedHashMap<String, String>();
8889
for (Entry<String, ?> entry : unencoded.entrySet()) {
8990
encoded.put(entry.getKey(), urlEncode(String.valueOf(entry.getValue())));
9091
}
91-
replaceQueryValues(encoded);
9292
String resolvedUrl = expand(url.toString(), encoded).replace("%2F", "/");
9393
url = new StringBuilder(resolvedUrl);
9494

@@ -509,8 +509,15 @@ public void replaceQueryValues(Map<String, ?> unencoded) {
509509
if (value.indexOf('{') == 0 && value.indexOf('}') == value.length() - 1) {
510510
Object variableValue = unencoded.get(value.substring(1, value.length() - 1));
511511
// only add non-null expressions
512-
if (variableValue != null) {
513-
values.add(String.valueOf(variableValue));
512+
if (variableValue == null) {
513+
continue;
514+
}
515+
if (variableValue instanceof Iterable) {
516+
for (Object val : Iterable.class.cast(variableValue)) {
517+
values.add(urlEncode(String.valueOf(val)));
518+
}
519+
} else {
520+
values.add(urlEncode(String.valueOf(variableValue)));
514521
}
515522
} else {
516523
values.add(value);

core/src/test/java/feign/FeignTest.java

Lines changed: 36 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,8 @@ void login(
9191

9292
@RequestLine("GET /{1}/{2}") Response uriParam(@Named("1") String one, URI endpoint, @Named("2") String two);
9393

94+
@RequestLine("GET /?1={1}&2={2}") Response queryParams(@Named("1") String one, @Named("2") Iterable<String> twos);
95+
9496
@RequestLine("POST /") Observable<Void> observableVoid();
9597

9698
@RequestLine("POST /") Observable<String> observableString();
@@ -114,14 +116,29 @@ static class Module {
114116
}
115117
};
116118
}
119+
}
120+
}
121+
122+
@Test
123+
public void iterableQueryParams() throws IOException, InterruptedException {
124+
final MockWebServer server = new MockWebServer();
125+
server.enqueue(new MockResponse().setBody("foo"));
126+
server.play();
127+
128+
try {
129+
TestInterface api = Feign.create(TestInterface.class, "http://localhost:" + server.getPort(), new TestInterface.Module());
117130

131+
api.queryParams("user", Arrays.asList("apple", "pear"));
132+
assertEquals(server.takeRequest().getRequestLine(), "GET /?1=user&2=apple&2=pear HTTP/1.1");
133+
} finally {
134+
server.shutdown();
118135
}
119136
}
120137

121138
@Test
122139
public void observableVoid() throws IOException, InterruptedException {
123140
final MockWebServer server = new MockWebServer();
124-
server.enqueue(new MockResponse().setResponseCode(200).setBody("foo"));
141+
server.enqueue(new MockResponse().setBody("foo"));
125142
server.play();
126143

127144
try {
@@ -156,7 +173,7 @@ public void observableVoid() throws IOException, InterruptedException {
156173
@Test
157174
public void observableResponse() throws IOException, InterruptedException {
158175
final MockWebServer server = new MockWebServer();
159-
server.enqueue(new MockResponse().setResponseCode(200).setBody("foo"));
176+
server.enqueue(new MockResponse().setBody("foo"));
160177
server.play();
161178

162179
try {
@@ -202,7 +219,7 @@ static class RunSynchronous {
202219
@Test
203220
public void incrementString() throws IOException, InterruptedException {
204221
final MockWebServer server = new MockWebServer();
205-
server.enqueue(new MockResponse().setResponseCode(200).setBody("foo"));
222+
server.enqueue(new MockResponse().setBody("foo"));
206223
server.play();
207224

208225
try {
@@ -237,8 +254,8 @@ public void incrementString() throws IOException, InterruptedException {
237254
@Test
238255
public void multipleObservers() throws IOException, InterruptedException {
239256
final MockWebServer server = new MockWebServer();
240-
server.enqueue(new MockResponse().setResponseCode(200).setBody("foo"));
241-
server.enqueue(new MockResponse().setResponseCode(200).setBody("foo"));
257+
server.enqueue(new MockResponse().setBody("foo"));
258+
server.enqueue(new MockResponse().setBody("foo"));
242259
server.play();
243260

244261
try {
@@ -275,7 +292,7 @@ public void multipleObservers() throws IOException, InterruptedException {
275292
@Test
276293
public void postTemplateParamsResolve() throws IOException, InterruptedException {
277294
final MockWebServer server = new MockWebServer();
278-
server.enqueue(new MockResponse().setResponseCode(200).setBody("foo"));
295+
server.enqueue(new MockResponse().setBody("foo"));
279296
server.play();
280297

281298
try {
@@ -292,7 +309,7 @@ public void postTemplateParamsResolve() throws IOException, InterruptedException
292309
@Test
293310
public void postFormParams() throws IOException, InterruptedException {
294311
final MockWebServer server = new MockWebServer();
295-
server.enqueue(new MockResponse().setResponseCode(200).setBody("foo"));
312+
server.enqueue(new MockResponse().setBody("foo"));
296313
server.play();
297314

298315
try {
@@ -309,7 +326,7 @@ public void postFormParams() throws IOException, InterruptedException {
309326
@Test
310327
public void postBodyParam() throws IOException, InterruptedException {
311328
final MockWebServer server = new MockWebServer();
312-
server.enqueue(new MockResponse().setResponseCode(200).setBody("foo"));
329+
server.enqueue(new MockResponse().setBody("foo"));
313330
server.play();
314331

315332
try {
@@ -327,7 +344,7 @@ public void postBodyParam() throws IOException, InterruptedException {
327344
@Test
328345
public void postGZIPEncodedBodyParam() throws IOException, InterruptedException {
329346
final MockWebServer server = new MockWebServer();
330-
server.enqueue(new MockResponse().setResponseCode(200).setBody("foo"));
347+
server.enqueue(new MockResponse().setBody("foo"));
331348
server.play();
332349

333350
try {
@@ -359,7 +376,7 @@ static class ForwardedForInterceptor implements RequestInterceptor {
359376
@Test
360377
public void singleInterceptor() throws IOException, InterruptedException {
361378
final MockWebServer server = new MockWebServer();
362-
server.enqueue(new MockResponse().setResponseCode(200).setBody("foo"));
379+
server.enqueue(new MockResponse().setBody("foo"));
363380
server.play();
364381

365382
try {
@@ -387,7 +404,7 @@ static class UserAgentInterceptor implements RequestInterceptor {
387404
@Test
388405
public void multipleInterceptor() throws IOException, InterruptedException {
389406
final MockWebServer server = new MockWebServer();
390-
server.enqueue(new MockResponse().setResponseCode(200).setBody("foo"));
407+
server.enqueue(new MockResponse().setBody("foo"));
391408
server.play();
392409

393410
try {
@@ -445,7 +462,7 @@ public void canOverrideErrorDecoder() throws IOException, InterruptedException {
445462
@Test public void retriesLostConnectionBeforeRead() throws IOException, InterruptedException {
446463
MockWebServer server = new MockWebServer();
447464
server.enqueue(new MockResponse().setSocketPolicy(SocketPolicy.DISCONNECT_AT_START));
448-
server.enqueue(new MockResponse().setResponseCode(200).setBody("success!".getBytes()));
465+
server.enqueue(new MockResponse().setBody("success!".getBytes()));
449466
server.play();
450467

451468
try {
@@ -474,7 +491,7 @@ public String decode(Reader reader, Type type) throws IOException {
474491

475492
public void overrideTypeSpecificDecoder() throws IOException, InterruptedException {
476493
MockWebServer server = new MockWebServer();
477-
server.enqueue(new MockResponse().setResponseCode(200).setBody("success!".getBytes()));
494+
server.enqueue(new MockResponse().setBody("success!".getBytes()));
478495
server.play();
479496

480497
try {
@@ -508,8 +525,8 @@ public String decode(Reader reader, Type type) throws RetryableException, IOExce
508525
*/
509526
public void retryableExceptionInDecoder() throws IOException, InterruptedException {
510527
MockWebServer server = new MockWebServer();
511-
server.enqueue(new MockResponse().setResponseCode(200).setBody("retry!".getBytes()));
512-
server.enqueue(new MockResponse().setResponseCode(200).setBody("success!".getBytes()));
528+
server.enqueue(new MockResponse().setBody("retry!".getBytes()));
529+
server.enqueue(new MockResponse().setBody("success!".getBytes()));
513530
server.play();
514531

515532
try {
@@ -538,7 +555,7 @@ public String decode(Reader reader, Type type) throws IOException {
538555
@Test(expectedExceptions = FeignException.class, expectedExceptionsMessageRegExp = "error reading response POST http://.*")
539556
public void doesntRetryAfterResponseIsSent() throws IOException, InterruptedException {
540557
MockWebServer server = new MockWebServer();
541-
server.enqueue(new MockResponse().setResponseCode(200).setBody("success!".getBytes()));
558+
server.enqueue(new MockResponse().setBody("success!".getBytes()));
542559
server.play();
543560

544561
try {
@@ -562,7 +579,7 @@ static class TrustSSLSockets {
562579
@Test public void canOverrideSSLSocketFactory() throws IOException, InterruptedException {
563580
MockWebServer server = new MockWebServer();
564581
server.useHttps(TrustingSSLSocketFactory.get("localhost"), false);
565-
server.enqueue(new MockResponse().setResponseCode(200).setBody("success!".getBytes()));
582+
server.enqueue(new MockResponse().setBody("success!".getBytes()));
566583
server.play();
567584

568585
try {
@@ -584,7 +601,7 @@ static class DisableHostnameVerification {
584601
@Test public void canOverrideHostnameVerifier() throws IOException, InterruptedException {
585602
MockWebServer server = new MockWebServer();
586603
server.useHttps(TrustingSSLSocketFactory.get("bad.example.com"), false);
587-
server.enqueue(new MockResponse().setResponseCode(200).setBody("success!".getBytes()));
604+
server.enqueue(new MockResponse().setBody("success!".getBytes()));
588605
server.play();
589606

590607
try {
@@ -600,7 +617,7 @@ static class DisableHostnameVerification {
600617
MockWebServer server = new MockWebServer();
601618
server.useHttps(TrustingSSLSocketFactory.get("localhost"), false);
602619
server.enqueue(new MockResponse().setSocketPolicy(SocketPolicy.FAIL_HANDSHAKE));
603-
server.enqueue(new MockResponse().setResponseCode(200).setBody("success!".getBytes()));
620+
server.enqueue(new MockResponse().setBody("success!".getBytes()));
604621
server.play();
605622

606623
try {

core/src/test/java/feign/RequestTemplateTest.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
import com.google.common.collect.ImmutableMap;
2121
import org.testng.annotations.Test;
2222

23+
import java.util.Arrays;
24+
2325
import static feign.RequestTemplate.expand;
2426
import static org.testng.Assert.assertEquals;
2527

@@ -83,6 +85,20 @@ public class RequestTemplateTest {
8385
+ "GET https://iam.amazonaws.com/?Action=DescribeRegions&RegionName.1=eu-west-1 HTTP/1.1\n");
8486
}
8587

88+
@Test public void resolveTemplateWithBaseAndParameterizedIterableQuery() {
89+
RequestTemplate template = new RequestTemplate().method("GET")
90+
.append("/?Query=one").query("Queries", "{queries}");
91+
92+
template.resolve(ImmutableMap.of("queries", Arrays.asList("us-east-1", "eu-west-1")));
93+
assertEquals(template.queries(),
94+
ImmutableListMultimap.<String, String> builder()
95+
.put("Query", "one")
96+
.putAll("Queries", "us-east-1", "eu-west-1")
97+
.build().asMap());
98+
99+
assertEquals(template.toString(), "GET /?Query=one&Queries=us-east-1&Queries=eu-west-1 HTTP/1.1\n");
100+
}
101+
86102
@Test public void resolveTemplateWithMixedRequestLineParams() throws Exception {
87103
RequestTemplate template = new RequestTemplate().method("GET")//
88104
.append("/domains/{domainId}/records")//

0 commit comments

Comments
 (0)