Skip to content

Commit 06497d5

Browse files
pilakPierre Lakrebvelo
authored
Add @QueryMap mapEncoder attribute (#2098)
* use `mapEncoder` attribute at method level for what encoder to use * still use builder `QueryMapEncoder` if no attribute specified Co-authored-by: Pierre Lakreb <[email protected]> Co-authored-by: Marvin Froeder <[email protected]>
1 parent 44c1968 commit 06497d5

File tree

7 files changed

+86
-7
lines changed

7 files changed

+86
-7
lines changed

core/src/main/java/feign/BaseBuilder.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
import feign.codec.Decoder;
2222
import feign.codec.Encoder;
2323
import feign.codec.ErrorDecoder;
24-
import feign.querymap.FieldQueryMapEncoder;
2524
import java.lang.reflect.Field;
2625
import java.lang.reflect.ParameterizedType;
2726
import java.lang.reflect.Type;
@@ -44,7 +43,7 @@ public abstract class BaseBuilder<B extends BaseBuilder<B, T>, T> implements Clo
4443
protected Decoder decoder = new Decoder.Default();
4544
protected boolean closeAfterDecode = true;
4645
protected boolean decodeVoid = false;
47-
protected QueryMapEncoder queryMapEncoder = new FieldQueryMapEncoder();
46+
protected QueryMapEncoder queryMapEncoder = QueryMap.MapEncoder.FIELD.instance();
4847
protected ErrorDecoder errorDecoder = new ErrorDecoder.Default();
4948
protected Options options = new Options();
5049
protected InvocationHandlerFactory invocationHandlerFactory =

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,7 @@ public Default() {
341341
data.queryMapIndex() == null,
342342
"QueryMap annotation was present on multiple parameters.");
343343
data.queryMapIndex(paramIndex);
344+
data.queryMapEncoder(queryMap.mapEncoder().instance());
344345
});
345346
super.registerParameterAnnotation(
346347
HeaderMap.class,

core/src/main/java/feign/MethodMetadata.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ public final class MethodMetadata implements Serializable {
2929
private Integer bodyIndex;
3030
private Integer headerMapIndex;
3131
private Integer queryMapIndex;
32+
private QueryMapEncoder queryMapEncoder;
3233
private boolean alwaysEncodeBody;
3334
private transient Type bodyType;
3435
private final RequestTemplate template = new RequestTemplate();
@@ -109,6 +110,15 @@ public MethodMetadata queryMapIndex(Integer queryMapIndex) {
109110
return this;
110111
}
111112

113+
public QueryMapEncoder queryMapEncoder() {
114+
return queryMapEncoder;
115+
}
116+
117+
public MethodMetadata queryMapEncoder(QueryMapEncoder queryMapEncoder) {
118+
this.queryMapEncoder = queryMapEncoder;
119+
return this;
120+
}
121+
112122
@Experimental
113123
public boolean alwaysEncodeBody() {
114124
return alwaysEncodeBody;

core/src/main/java/feign/QueryMap.java

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
import static java.lang.annotation.ElementType.PARAMETER;
1717
import static java.lang.annotation.RetentionPolicy.RUNTIME;
1818

19+
import feign.querymap.BeanQueryMapEncoder;
20+
import feign.querymap.FieldQueryMapEncoder;
1921
import java.lang.annotation.Retention;
2022
import java.util.List;
2123
import java.util.Map;
@@ -74,4 +76,28 @@
7476
* @deprecated
7577
*/
7678
boolean encoded() default false;
79+
80+
/**
81+
* Specifies the QueryMapEncoder implementation to use to transform DTO into query map.
82+
*
83+
* @return the enum containing the instance of QueryMapEncoder
84+
*/
85+
MapEncoder mapEncoder() default MapEncoder.DEFAULT;
86+
87+
public enum MapEncoder {
88+
// the latter DEFAULT will use BaseBuilder instance
89+
BEAN(new BeanQueryMapEncoder()),
90+
FIELD(new FieldQueryMapEncoder()),
91+
DEFAULT(null);
92+
93+
private QueryMapEncoder mapEncoder;
94+
95+
private MapEncoder(QueryMapEncoder mapEncoder) {
96+
this.mapEncoder = mapEncoder;
97+
}
98+
99+
public QueryMapEncoder instance() {
100+
return mapEncoder;
101+
}
102+
}
77103
}

core/src/main/java/feign/RequestTemplateFactoryResolver.java

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -107,26 +107,29 @@ public RequestTemplate create(Object[] argv) {
107107
// add query map parameters after initial resolve so that they take
108108
// precedence over any predefined values
109109
Object value = argv[metadata.queryMapIndex()];
110-
Map<String, Object> queryMap = toQueryMap(value);
110+
Map<String, Object> queryMap = toQueryMap(value, metadata.queryMapEncoder());
111111
template = addQueryMapQueryParameters(queryMap, template);
112112
}
113113

114114
if (metadata.headerMapIndex() != null) {
115115
// add header map parameters for a resolution of the user pojo object
116116
Object value = argv[metadata.headerMapIndex()];
117-
Map<String, Object> headerMap = toQueryMap(value);
117+
Map<String, Object> headerMap = toQueryMap(value, metadata.queryMapEncoder());
118118
template = addHeaderMapHeaders(headerMap, template);
119119
}
120120

121121
return template;
122122
}
123123

124-
private Map<String, Object> toQueryMap(Object value) {
124+
private Map<String, Object> toQueryMap(Object value, QueryMapEncoder queryMapEncoder) {
125125
if (value instanceof Map) {
126126
return (Map<String, Object>) value;
127127
}
128128
try {
129-
return queryMapEncoder.encode(value);
129+
// encode with @QueryMap annotation if exists otherwise with the one from this resolver
130+
return queryMapEncoder != null
131+
? queryMapEncoder.encode(value)
132+
: this.queryMapEncoder.encode(value);
130133
} catch (EncodeException e) {
131134
throw new IllegalStateException(e);
132135
}

core/src/test/java/feign/ChildPojo.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,15 @@
1616
class ParentPojo {
1717
public String parentPublicProperty;
1818
protected String parentProtectedProperty;
19+
private String parentPrivatePropertyAlteredByGetter;
20+
21+
public String getParentPrivatePropertyAlteredByGetter() {
22+
return parentPrivatePropertyAlteredByGetter + "FromGetter";
23+
}
24+
25+
public void setParentPrivatePropertyAlteredByGetter(String parentPrivatePropertyAlteredByGetter) {
26+
this.parentPrivatePropertyAlteredByGetter = parentPrivatePropertyAlteredByGetter;
27+
}
1928

2029
public String getParentPublicProperty() {
2130
return parentPublicProperty;

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

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import com.google.gson.Gson;
2727
import com.google.gson.reflect.TypeToken;
2828
import feign.Feign.ResponseMappingDecoder;
29+
import feign.QueryMap.MapEncoder;
2930
import feign.Request.HttpMethod;
3031
import feign.Target.HardCodedTarget;
3132
import feign.codec.DecodeException;
@@ -962,14 +963,40 @@ public void queryMap_with_child_pojo() throws Exception {
962963
childPojo.setChildPrivateProperty("first");
963964
childPojo.setParentProtectedProperty("second");
964965
childPojo.setParentPublicProperty("third");
966+
childPojo.setParentPrivatePropertyAlteredByGetter("fourth");
965967

966968
server.enqueue(new MockResponse());
967969
api.queryMapPropertyInheritence(childPojo);
968970
assertThat(server.takeRequest())
969971
.hasQueryParams(
970972
"parentPublicProperty=third",
971973
"parentProtectedProperty=second",
972-
"childPrivateProperty=first");
974+
"childPrivateProperty=first",
975+
"parentPrivatePropertyAlteredByGetter=fourth");
976+
}
977+
978+
@Test
979+
public void queryMap_with_child_pojo_altered_by_getter_while_using_overriding_encoder()
980+
throws Exception {
981+
TestInterface api =
982+
new TestInterfaceBuilder()
983+
.queryMapEncoder(new FieldQueryMapEncoder())
984+
.target("http://localhost:" + server.getPort());
985+
986+
ChildPojo childPojo = new ChildPojo();
987+
childPojo.setChildPrivateProperty("first");
988+
childPojo.setParentProtectedProperty("second");
989+
childPojo.setParentPublicProperty("third");
990+
childPojo.setParentPrivatePropertyAlteredByGetter("fourth");
991+
992+
server.enqueue(new MockResponse());
993+
api.queryMapPropertyInheritenceWithBeanMapEncoder(childPojo);
994+
assertThat(server.takeRequest())
995+
.hasQueryParams(
996+
"parentPublicProperty=third",
997+
"parentProtectedProperty=second",
998+
"childPrivateProperty=first",
999+
"parentPrivatePropertyAlteredByGetter=fourthFromGetter");
9731000
}
9741001

9751002
@Test
@@ -1268,6 +1295,10 @@ void queryMapWithQueryParams(
12681295
@RequestLine("GET /")
12691296
void queryMapPropertyPojo(@QueryMap PropertyPojo object);
12701297

1298+
@RequestLine("GET /")
1299+
void queryMapPropertyInheritenceWithBeanMapEncoder(
1300+
@QueryMap(mapEncoder = MapEncoder.BEAN) ChildPojo object);
1301+
12711302
@RequestLine("GET /")
12721303
void queryMapPropertyInheritence(@QueryMap ChildPojo object);
12731304

0 commit comments

Comments
 (0)