Browse Source

Added support for custom POJO query param encoding (#667)

* Added support for custom param encoding

* Added ability to inherit @CustomParam annotation

* Updated class cast style to match rest of code

* Updated to use QueryMap for custom pojo query parameters

* Clarification in README of QueryMap POJO usage

* Removed unused line

* Updated custom POJO QueryMap test to prove that private fields can be used

* Removed no-longer-valid test endpoint

* Renamed tests to more accurately reflect their contents

* More test cleanup

* Modified QueryMap POJO encoding to use specified QueryMapEncoder (default implementation provided)

* Corrected typo in README.md

* Fixed merge conflict and typo in test name
pull/689/head
Shadow Man 7 years ago committed by Marvin Froeder
parent
commit
5e2fdeed0d
  1. 29
      README.md
  2. 8
      core/src/main/java/feign/Contract.java
  3. 10
      core/src/main/java/feign/Feign.java
  4. 88
      core/src/main/java/feign/QueryMapEncoder.java
  5. 53
      core/src/main/java/feign/ReflectiveFeign.java
  6. 25
      core/src/test/java/feign/CustomPojo.java
  7. 44
      core/src/test/java/feign/DefaultContractTest.java
  8. 78
      core/src/test/java/feign/DefaultQueryMapEncoderTest.java
  9. 26
      core/src/test/java/feign/FeignBuilderTest.java
  10. 42
      core/src/test/java/feign/FeignTest.java
  11. 24
      core/src/test/java/feign/QueryMapEncoderObject.java
  12. 26
      core/src/test/java/feign/assertj/RecordedRequestAssert.java

29
README.md

@ -444,6 +444,35 @@ A Map parameter can be annotated with `QueryMap` to construct a query that uses @@ -444,6 +444,35 @@ A Map parameter can be annotated with `QueryMap` to construct a query that uses
V find(@QueryMap Map<String, Object> queryMap);
```
This may also be used to generate the query parameters from a POJO object using a `QueryMapEncoder`.
```java
@RequestLine("GET /find")
V find(@QueryMap CustomPojo customPojo);
```
When used in this manner, without specifying a custom `QueryMapEncoder`, the query map will be generated using member variable names as query parameter names. The following POJO will generate query params of "/find?name={name}&number={number}" (order of included query parameters not guaranteed, and as usual, if any value is null, it will be left out).
```java
public class CustomPojo {
private final String name;
private final int number;
public CustomPojo (String name, int number) {
this.name = name;
this.number = number;
}
}
```
To setup a custom `QueryMapEncoder`:
```java
MyApi myApi = Feign.builder()
.queryMapEncoder(new MyCustomQueryMapEncoder())
.target(MyApi.class, "https://api.hostname.com");
```
#### Static and Default Methods
Interfaces targeted by Feign may have static or default methods (if using Java 8+).
These allows Feign clients to contain logic that is not expressly defined by the underlying API.

8
core/src/main/java/feign/Contract.java

@ -123,7 +123,9 @@ public interface Contract { @@ -123,7 +123,9 @@ public interface Contract {
}
if (data.queryMapIndex() != null) {
checkMapString("QueryMap", parameterTypes[data.queryMapIndex()], genericParameterTypes[data.queryMapIndex()]);
if (Map.class.isAssignableFrom(parameterTypes[data.queryMapIndex()])) {
checkMapKeys("QueryMap", genericParameterTypes[data.queryMapIndex()]);
}
}
return data;
@ -132,6 +134,10 @@ public interface Contract { @@ -132,6 +134,10 @@ public interface Contract {
private static void checkMapString(String name, Class<?> type, Type genericType) {
checkState(Map.class.isAssignableFrom(type),
"%s parameter must be a Map: %s", name, type);
checkMapKeys(name, genericType);
}
private static void checkMapKeys(String name, Type genericType) {
Type[] parameterTypes = ((ParameterizedType) genericType).getActualTypeArguments();
Class<?> keyClass = (Class<?>) parameterTypes[0];
checkState(String.class.equals(keyClass),

10
core/src/main/java/feign/Feign.java

@ -101,6 +101,7 @@ public abstract class Feign { @@ -101,6 +101,7 @@ public abstract class Feign {
private Logger logger = new NoOpLogger();
private Encoder encoder = new Encoder.Default();
private Decoder decoder = new Decoder.Default();
private QueryMapEncoder queryMapEncoder = new QueryMapEncoder.Default();
private ErrorDecoder errorDecoder = new ErrorDecoder.Default();
private Options options = new Options();
private InvocationHandlerFactory invocationHandlerFactory =
@ -143,6 +144,11 @@ public abstract class Feign { @@ -143,6 +144,11 @@ public abstract class Feign {
return this;
}
public Builder queryMapEncoder(QueryMapEncoder queryMapEncoder) {
this.queryMapEncoder = queryMapEncoder;
return this;
}
/**
* Allows to map the response before passing it to the decoder.
*/
@ -241,9 +247,9 @@ public abstract class Feign { @@ -241,9 +247,9 @@ public abstract class Feign {
new SynchronousMethodHandler.Factory(client, retryer, requestInterceptors, logger,
logLevel, decode404, closeAfterDecode);
ParseHandlersByName handlersByName =
new ParseHandlersByName(contract, options, encoder, decoder,
new ParseHandlersByName(contract, options, encoder, decoder, queryMapEncoder,
errorDecoder, synchronousMethodHandlerFactory);
return new ReflectiveFeign(handlersByName, invocationHandlerFactory);
return new ReflectiveFeign(handlersByName, invocationHandlerFactory, queryMapEncoder);
}
}

88
core/src/main/java/feign/QueryMapEncoder.java

@ -0,0 +1,88 @@ @@ -0,0 +1,88 @@
/**
* Copyright 2012-2018 The Feign Authors
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package feign;
import feign.codec.EncodeException;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* A QueryMapEncoder encodes Objects into maps of query parameter names to values.
*/
public interface QueryMapEncoder {
/**
* Encodes the given object into a query map.
*
* @param object the object to encode
* @return the map represented by the object
*/
Map<String, Object> encode (Object object);
class Default implements QueryMapEncoder {
private final Map<Class<?>, ObjectParamMetadata> classToMetadata =
new HashMap<Class<?>, ObjectParamMetadata>();
@Override
public Map<String, Object> encode (Object object) throws EncodeException {
try {
ObjectParamMetadata metadata = getMetadata(object.getClass());
Map<String, Object> fieldNameToValue = new HashMap<String, Object>();
for (Field field : metadata.objectFields) {
Object value = field.get(object);
if (value != null && value != object) {
fieldNameToValue.put(field.getName(), value);
}
}
return fieldNameToValue;
} catch (IllegalAccessException e) {
throw new EncodeException("Failure encoding object into query map", e);
}
}
private ObjectParamMetadata getMetadata(Class<?> objectType) {
ObjectParamMetadata metadata = classToMetadata.get(objectType);
if (metadata == null) {
metadata = ObjectParamMetadata.parseObjectType(objectType);
classToMetadata.put(objectType, metadata);
}
return metadata;
}
private static class ObjectParamMetadata {
private final List<Field> objectFields;
private ObjectParamMetadata (List<Field> objectFields) {
this.objectFields = Collections.unmodifiableList(objectFields);
}
private static ObjectParamMetadata parseObjectType(Class<?> type) {
List<Field> fields = new ArrayList<Field>();
for (Field field : type.getDeclaredFields()) {
if (!field.isAccessible()) {
field.setAccessible(true);
}
fields.add(field);
}
return new ObjectParamMetadata(fields);
}
}
}
}

53
core/src/main/java/feign/ReflectiveFeign.java

@ -29,16 +29,17 @@ import feign.codec.ErrorDecoder; @@ -29,16 +29,17 @@ import feign.codec.ErrorDecoder;
import static feign.Util.checkArgument;
import static feign.Util.checkNotNull;
import static feign.Util.checkState;
public class ReflectiveFeign extends Feign {
private final ParseHandlersByName targetToHandlersByName;
private final InvocationHandlerFactory factory;
private final QueryMapEncoder queryMapEncoder;
ReflectiveFeign(ParseHandlersByName targetToHandlersByName, InvocationHandlerFactory factory) {
ReflectiveFeign(ParseHandlersByName targetToHandlersByName, InvocationHandlerFactory factory, QueryMapEncoder queryMapEncoder) {
this.targetToHandlersByName = targetToHandlersByName;
this.factory = factory;
this.queryMapEncoder = queryMapEncoder;
}
/**
@ -128,14 +129,22 @@ public class ReflectiveFeign extends Feign { @@ -128,14 +129,22 @@ public class ReflectiveFeign extends Feign {
private final Encoder encoder;
private final Decoder decoder;
private final ErrorDecoder errorDecoder;
private final QueryMapEncoder queryMapEncoder;
private final SynchronousMethodHandler.Factory factory;
ParseHandlersByName(Contract contract, Options options, Encoder encoder, Decoder decoder,
ErrorDecoder errorDecoder, SynchronousMethodHandler.Factory factory) {
ParseHandlersByName(
Contract contract,
Options options,
Encoder encoder,
Decoder decoder,
QueryMapEncoder queryMapEncoder,
ErrorDecoder errorDecoder,
SynchronousMethodHandler.Factory factory) {
this.contract = contract;
this.options = options;
this.factory = factory;
this.errorDecoder = errorDecoder;
this.queryMapEncoder = queryMapEncoder;
this.encoder = checkNotNull(encoder, "encoder");
this.decoder = checkNotNull(decoder, "decoder");
}
@ -146,11 +155,11 @@ public class ReflectiveFeign extends Feign { @@ -146,11 +155,11 @@ public class ReflectiveFeign extends Feign {
for (MethodMetadata md : metadata) {
BuildTemplateByResolvingArgs buildTemplate;
if (!md.formParams().isEmpty() && md.template().bodyTemplate() == null) {
buildTemplate = new BuildFormEncodedTemplateFromArgs(md, encoder);
buildTemplate = new BuildFormEncodedTemplateFromArgs(md, encoder, queryMapEncoder);
} else if (md.bodyIndex() != null) {
buildTemplate = new BuildEncodedTemplateFromArgs(md, encoder);
buildTemplate = new BuildEncodedTemplateFromArgs(md, encoder, queryMapEncoder);
} else {
buildTemplate = new BuildTemplateByResolvingArgs(md);
buildTemplate = new BuildTemplateByResolvingArgs(md, queryMapEncoder);
}
result.put(md.configKey(),
factory.create(key, md, buildTemplate, options, decoder, errorDecoder));
@ -161,11 +170,14 @@ public class ReflectiveFeign extends Feign { @@ -161,11 +170,14 @@ public class ReflectiveFeign extends Feign {
private static class BuildTemplateByResolvingArgs implements RequestTemplate.Factory {
private final QueryMapEncoder queryMapEncoder;
protected final MethodMetadata metadata;
private final Map<Integer, Expander> indexToExpander = new LinkedHashMap<Integer, Expander>();
private BuildTemplateByResolvingArgs(MethodMetadata metadata) {
private BuildTemplateByResolvingArgs(MethodMetadata metadata, QueryMapEncoder queryMapEncoder) {
this.metadata = metadata;
this.queryMapEncoder = queryMapEncoder;
if (metadata.indexToExpander() != null) {
indexToExpander.putAll(metadata.indexToExpander());
return;
@ -212,7 +224,9 @@ public class ReflectiveFeign extends Feign { @@ -212,7 +224,9 @@ public class ReflectiveFeign extends Feign {
if (metadata.queryMapIndex() != null) {
// add query map parameters after initial resolve so that they take
// precedence over any predefined values
template = addQueryMapQueryParameters((Map<String, Object>) argv[metadata.queryMapIndex()], template);
Object value = argv[metadata.queryMapIndex()];
Map<String, Object> queryMap = toQueryMap(value);
template = addQueryMapQueryParameters(queryMap, template);
}
if (metadata.headerMapIndex() != null) {
@ -222,6 +236,17 @@ public class ReflectiveFeign extends Feign { @@ -222,6 +236,17 @@ public class ReflectiveFeign extends Feign {
return template;
}
private Map<String, Object> toQueryMap (Object value) {
if (value instanceof Map) {
return (Map<String, Object>)value;
}
try {
return queryMapEncoder.encode(value);
} catch (EncodeException e) {
throw new IllegalStateException(e);
}
}
private Object expandElements(Expander expander, Object value) {
if (value instanceof Iterable) {
return expandIterable(expander, (Iterable) value);
@ -231,7 +256,7 @@ public class ReflectiveFeign extends Feign { @@ -231,7 +256,7 @@ public class ReflectiveFeign extends Feign {
private List<String> expandIterable(Expander expander, Iterable value) {
List<String> values = new ArrayList<String>();
for (Object element : (Iterable) value) {
for (Object element : value) {
if (element!=null) {
values.add(expander.expand(element));
}
@ -300,8 +325,8 @@ public class ReflectiveFeign extends Feign { @@ -300,8 +325,8 @@ public class ReflectiveFeign extends Feign {
private final Encoder encoder;
private BuildFormEncodedTemplateFromArgs(MethodMetadata metadata, Encoder encoder) {
super(metadata);
private BuildFormEncodedTemplateFromArgs(MethodMetadata metadata, Encoder encoder, QueryMapEncoder queryMapEncoder) {
super(metadata, queryMapEncoder);
this.encoder = encoder;
}
@ -329,8 +354,8 @@ public class ReflectiveFeign extends Feign { @@ -329,8 +354,8 @@ public class ReflectiveFeign extends Feign {
private final Encoder encoder;
private BuildEncodedTemplateFromArgs(MethodMetadata metadata, Encoder encoder) {
super(metadata);
private BuildEncodedTemplateFromArgs(MethodMetadata metadata, Encoder encoder, QueryMapEncoder queryMapEncoder) {
super(metadata, queryMapEncoder);
this.encoder = encoder;
}

25
core/src/test/java/feign/CustomPojo.java

@ -0,0 +1,25 @@ @@ -0,0 +1,25 @@
/**
* Copyright 2012-2018 The Feign Authors
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package feign;
public class CustomPojo {
private final String name;
private final Integer number;
CustomPojo(String name, Integer number) {
this.name = name;
this.number = number;
}
}

44
core/src/test/java/feign/DefaultContractTest.java

@ -316,16 +316,6 @@ public class DefaultContractTest { @@ -316,16 +316,6 @@ public class DefaultContractTest {
}
}
@Test
public void queryMapMustBeInstanceOfMap() throws Exception {
try {
parseAndValidateMetadata(QueryMapTestInterface.class, "nonMapQueryMap", String.class);
Fail.failBecauseExceptionWasNotThrown(IllegalStateException.class);
} catch (IllegalStateException ex) {
assertThat(ex).hasMessage("QueryMap parameter must be a Map: class java.lang.String");
}
}
@Test
public void queryMapKeysMustBeStrings() throws Exception {
try {
@ -336,6 +326,29 @@ public class DefaultContractTest { @@ -336,6 +326,29 @@ public class DefaultContractTest {
}
}
@Test
public void queryMapPojoObject() throws Exception {
MethodMetadata md = parseAndValidateMetadata(QueryMapTestInterface.class, "pojoObject", Object.class);
assertThat(md.queryMapIndex()).isEqualTo(0);
}
@Test
public void queryMapPojoObjectEncoded() throws Exception {
MethodMetadata md = parseAndValidateMetadata(QueryMapTestInterface.class, "pojoObjectEncoded", Object.class);
assertThat(md.queryMapIndex()).isEqualTo(0);
assertThat(md.queryMapEncoded()).isTrue();
}
@Test
public void queryMapPojoObjectNotEncoded() throws Exception {
MethodMetadata md = parseAndValidateMetadata(QueryMapTestInterface.class, "pojoObjectNotEncoded", Object.class);
assertThat(md.queryMapIndex()).isEqualTo(0);
assertThat(md.queryMapEncoded()).isFalse();
}
@Test
public void slashAreEncodedWhenNeeded() throws Exception {
MethodMetadata md = parseAndValidateMetadata(SlashNeedToBeEncoded.class,
@ -501,13 +514,18 @@ public class DefaultContractTest { @@ -501,13 +514,18 @@ public class DefaultContractTest {
@RequestLine("POST /")
void queryMapNotEncoded(@QueryMap(encoded = false) Map<String, String> queryMap);
// invalid
@RequestLine("POST /")
void multipleQueryMap(@QueryMap Map<String, String> mapOne, @QueryMap Map<String, String> mapTwo);
void pojoObject(@QueryMap Object object);
@RequestLine("POST /")
void pojoObjectEncoded(@QueryMap(encoded = true) Object object);
@RequestLine("POST /")
void pojoObjectNotEncoded(@QueryMap(encoded = false) Object object);
// invalid
@RequestLine("POST /")
void nonMapQueryMap(@QueryMap String notAMap);
void multipleQueryMap(@QueryMap Map<String, String> mapOne, @QueryMap Map<String, String> mapTwo);
// invalid
@RequestLine("POST /")

78
core/src/test/java/feign/DefaultQueryMapEncoderTest.java

@ -0,0 +1,78 @@ @@ -0,0 +1,78 @@
/**
* Copyright 2012-2018 The Feign Authors
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package feign;
import java.util.HashMap;
import java.util.Map;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
public class DefaultQueryMapEncoderTest {
@Rule
public final ExpectedException thrown = ExpectedException.none();
private final QueryMapEncoder encoder = new QueryMapEncoder.Default();
@Test
public void testEncodesObject_visibleFields() {
Map<String, Object> expected = new HashMap<>();
expected.put("foo", "fooz");
expected.put("bar", "barz");
expected.put("baz", "bazz");
VisibleFieldsObject object = new VisibleFieldsObject();
object.foo = "fooz";
object.bar = "barz";
object.baz = "bazz";
Map<String, Object> encodedMap = encoder.encode(object);
assertEquals("Unexpected encoded query map", expected, encodedMap);
}
@Test
public void testEncodesObject_visibleFields_emptyObject() {
VisibleFieldsObject object = new VisibleFieldsObject();
Map<String, Object> encodedMap = encoder.encode(object);
assertTrue("Non-empty map generated from null fields: " + encodedMap, encodedMap.isEmpty());
}
@Test
public void testEncodesObject_nonVisibleFields() {
Map<String, Object> expected = new HashMap<>();
expected.put("foo", "fooz");
expected.put("bar", "barz");
QueryMapEncoderObject object = new QueryMapEncoderObject("fooz", "barz");
Map<String, Object> encodedMap = encoder.encode(object);
assertEquals("Unexpected encoded query map", expected, encodedMap);
}
@Test
public void testEncodesObject_nonVisibleFields_emptyObject() {
QueryMapEncoderObject object = new QueryMapEncoderObject(null, null);
Map<String, Object> encodedMap = encoder.encode(object);
assertTrue("Non-empty map generated from null fields", encodedMap.isEmpty());
}
static class VisibleFieldsObject {
String foo;
String bar;
String baz;
}
}

26
core/src/test/java/feign/FeignBuilderTest.java

@ -13,6 +13,7 @@ @@ -13,6 +13,7 @@
*/
package feign;
import java.util.HashMap;
import okhttp3.mockwebserver.MockResponse;
import okhttp3.mockwebserver.MockWebServer;
@ -186,6 +187,28 @@ public class FeignBuilderTest { @@ -186,6 +187,28 @@ public class FeignBuilderTest {
assertEquals(1, server.getRequestCount());
}
@Test
public void testOverrideQueryMapEncoder() throws Exception {
server.enqueue(new MockResponse());
String url = "http://localhost:" + server.getPort();
QueryMapEncoder customMapEncoder = new QueryMapEncoder() {
@Override
public Map<String, Object> encode(Object ignored) {
Map<String, Object> queryMap = new HashMap<String, Object>();
queryMap.put("key1", "value1");
queryMap.put("key2", "value2");
return queryMap;
}
};
TestInterface api = Feign.builder().queryMapEncoder(customMapEncoder).target(TestInterface.class, url);
api.queryMapEncoded("ignored");
assertThat(server.takeRequest()).hasQueryParams(Arrays.asList("key1=value1", "key2=value2"));
assertEquals(1, server.getRequestCount());
}
@Test
public void testProvideRequestInterceptors() throws Exception {
server.enqueue(new MockResponse().setBody("response data"));
@ -333,6 +356,9 @@ public class FeignBuilderTest { @@ -333,6 +356,9 @@ public class FeignBuilderTest {
@RequestLine("GET api/thing")
Response getNoInitialSlashOnSlash();
@RequestLine(value = "GET /api/querymap/object")
String queryMapEncoded(@QueryMap Object object);
@RequestLine("POST /")
Response codecPost(String data);

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

@ -25,7 +25,6 @@ import java.util.Collections; @@ -25,7 +25,6 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import okio.Buffer;
import org.assertj.core.api.Fail;
import org.assertj.core.data.MapEntry;
import org.junit.Rule;
import org.junit.Test;
@ -384,6 +383,42 @@ public class FeignTest { @@ -384,6 +383,42 @@ public class FeignTest {
.hasPath("/?%7Bname=%7Balice");
}
@Test
public void queryMapPojoWithFullParams() throws Exception {
TestInterface api = new TestInterfaceBuilder().target("http://localhost:" + server.getPort());
CustomPojo customPojo = new CustomPojo("Name", 3);
server.enqueue(new MockResponse());
api.queryMapPojo(customPojo);
assertThat(server.takeRequest())
.hasQueryParams(Arrays.asList("name=Name", "number=3"));
}
@Test
public void queryMapPojoWithPartialParams() throws Exception {
TestInterface api = new TestInterfaceBuilder().target("http://localhost:" + server.getPort());
CustomPojo customPojo = new CustomPojo("Name", null);
server.enqueue(new MockResponse());
api.queryMapPojo(customPojo);
assertThat(server.takeRequest())
.hasPath("/?name=Name");
}
@Test
public void queryMapPojoWithEmptyParams() throws Exception {
TestInterface api = new TestInterfaceBuilder().target("http://localhost:" + server.getPort());
CustomPojo customPojo = new CustomPojo(null, null);
server.enqueue(new MockResponse());
api.queryMapPojo(customPojo);
assertThat(server.takeRequest())
.hasPath("/");
}
@Test
public void configKeyFormatsAsExpected() throws Exception {
assertEquals("TestInterface#post()",
@ -590,7 +625,7 @@ public class FeignTest { @@ -590,7 +625,7 @@ public class FeignTest {
.decode404()
.errorDecoder(new IllegalArgumentExceptionOn404())
.target("http://localhost:" + server.getPort());
api.queryMap(Collections.emptyMap());
api.queryMap(Collections.<String, Object>emptyMap());
}
@Test
@ -796,6 +831,9 @@ public class FeignTest { @@ -796,6 +831,9 @@ public class FeignTest {
@RequestLine("GET /?trim={trim}")
void encodedQueryParam(@Param(value = "trim", encoded = true) String trim);
@RequestLine("GET /")
void queryMapPojo(@QueryMap CustomPojo object);
class DateToMillis implements Param.Expander {
@Override

24
core/src/test/java/feign/QueryMapEncoderObject.java

@ -0,0 +1,24 @@ @@ -0,0 +1,24 @@
/**
* Copyright 2012-2018 The Feign Authors
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package feign;
class QueryMapEncoderObject {
private final String foo;
private final String bar;
QueryMapEncoderObject (String foo, String bar) {
this.foo = foo;
this.bar = bar;
}
}

26
core/src/test/java/feign/assertj/RecordedRequestAssert.java

@ -13,6 +13,9 @@ @@ -13,6 +13,9 @@
*/
package feign.assertj;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import okhttp3.Headers;
import okhttp3.mockwebserver.RecordedRequest;
@ -62,6 +65,29 @@ public final class RecordedRequestAssert @@ -62,6 +65,29 @@ public final class RecordedRequestAssert
return this;
}
public RecordedRequestAssert hasQueryParams(String... expectedParams) {
return hasQueryParams(Arrays.asList(expectedParams));
}
public RecordedRequestAssert hasQueryParams(Collection<String> expectedParams) {
isNotNull();
Collection<String> actualQueryParams = getQueryParams();
objects.assertEqual(info, expectedParams.size(), actualQueryParams.size());
for (String expectedParam : expectedParams) {
objects.assertIsIn(info, expectedParam, actualQueryParams);
}
return this;
}
private Collection<String> getQueryParams() {
String path = actual.getPath();
int queryStart = path.indexOf("?") + 1;
String[] queryParams = actual.getPath()
.substring(queryStart)
.split("&");
return Arrays.asList(queryParams);
}
public RecordedRequestAssert hasOneOfPath(String... expected) {
isNotNull();
objects.assertIsIn(info, actual.getPath(), expected);

Loading…
Cancel
Save