From 12322d69f076b4b15f9c9e4494e4b5a75605e51a Mon Sep 17 00:00:00 2001 From: Olga Maciaszek-Sharma Date: Wed, 15 Jul 2020 16:43:11 +0200 Subject: [PATCH 1/3] Add CollectionFormat support. Fixes gh-146. --- .../main/asciidoc/spring-cloud-openfeign.adoc | 19 ++++ .../cloud/openfeign/CollectionFormat.java | 41 +++++++++ .../openfeign/support/SpringMvcContract.java | 7 ++ .../support/PageableSupportTest.java | 88 +++++++++++++++++++ .../support/SpringMvcContractTests.java | 18 ++++ 5 files changed, 173 insertions(+) create mode 100644 spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/CollectionFormat.java create mode 100644 spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/support/PageableSupportTest.java diff --git a/docs/src/main/asciidoc/spring-cloud-openfeign.adoc b/docs/src/main/asciidoc/spring-cloud-openfeign.adoc index 9fc54faf2..d48ba67ed 100644 --- a/docs/src/main/asciidoc/spring-cloud-openfeign.adoc +++ b/docs/src/main/asciidoc/spring-cloud-openfeign.adoc @@ -586,6 +586,25 @@ public interface DemoTemplate { } ---- +=== Feign `CollectionFormat` support +We support `feign.CollectionFormat` by providing the `@CollectionFormat` annotation. You can annotate a feign client method with it passing the desired `feign.CollectionFormat` as annotation value. + +For example, in the example below, the `CSV` format will be used instead of the default `EXPLODED` to process the method. + +[source,java,indent=0] +---- +@FeignClient(name = "demo") + protected interface PageableFeignClient { + + @CollectionFormat(feign.CollectionFormat.CSV) + @GetMapping(path = "/page") + ResponseEntity performRequest(Pageable page); + + } +---- + +TIP: It's suggested to set the `CSV` format while sending `Pageable` as a query parameter in order for it to be encoded correctly. + === Reactive Support As the https://github.com/OpenFeign/feign[OpenFeign project] does not currently support reactive clients, such as https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/reactive/function/client/WebClient.html[Spring WebClient], neither does Spring Cloud OpenFeign. We will add support for it here as soon as it becomes available in the core project. diff --git a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/CollectionFormat.java b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/CollectionFormat.java new file mode 100644 index 000000000..d40c4a4b1 --- /dev/null +++ b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/CollectionFormat.java @@ -0,0 +1,41 @@ +/* + * Copyright 2013-2020 the original author or 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 + * + * https://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 org.springframework.cloud.openfeign; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Indicates which collection format should be used while processing the annotated method. + * + * @author Olga Maciaszek-Sharma + * @see feign.CollectionFormat + */ +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface CollectionFormat { + + /** + * Allows setting the {@link feign.CollectionFormat} to be used while processing the + * annotated method. + * @return the {@link feign.CollectionFormat} to be used + */ + feign.CollectionFormat value(); + +} diff --git a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/support/SpringMvcContract.java b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/support/SpringMvcContract.java index 433c93358..ab7b5679f 100644 --- a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/support/SpringMvcContract.java +++ b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/support/SpringMvcContract.java @@ -37,6 +37,7 @@ import feign.Request; import org.springframework.cloud.openfeign.AnnotatedParameterProcessor; +import org.springframework.cloud.openfeign.CollectionFormat; import org.springframework.cloud.openfeign.annotation.MatrixVariableParameterProcessor; import org.springframework.cloud.openfeign.annotation.PathVariableParameterProcessor; import org.springframework.cloud.openfeign.annotation.QueryMapParameterProcessor; @@ -213,6 +214,12 @@ public MethodMetadata parseAndValidateMetadata(Class targetType, Method metho @Override protected void processAnnotationOnMethod(MethodMetadata data, Annotation methodAnnotation, Method method) { + if (CollectionFormat.class.isInstance(methodAnnotation)) { + CollectionFormat collectionFormat = findMergedAnnotation(method, + CollectionFormat.class); + data.template().collectionFormat(collectionFormat.value()); + } + if (!RequestMapping.class.isInstance(methodAnnotation) && !methodAnnotation .annotationType().isAnnotationPresent(RequestMapping.class)) { return; diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/support/PageableSupportTest.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/support/PageableSupportTest.java new file mode 100644 index 000000000..40fbc5e39 --- /dev/null +++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/support/PageableSupportTest.java @@ -0,0 +1,88 @@ +/* + * Copyright 2013-2020 the original author or 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 + * + * https://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 org.springframework.cloud.openfeign.support; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.openfeign.CollectionFormat; +import org.springframework.cloud.openfeign.EnableFeignClients; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.cloud.openfeign.test.NoSecurityConfiguration; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; +import org.springframework.util.SocketUtils; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Olga Maciaszek-Sharma + */ +@SpringBootTest(classes = PageableSupportTest.Config.class, + webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT) +public class PageableSupportTest { + + @Autowired + private PageableFeignClient feignClient; + + @BeforeAll + public static void beforeClass() { + System.setProperty("server.port", + String.valueOf(SocketUtils.findAvailableTcpPort())); + } + + @Test + void shouldProperlyFormatPageable() { + String direction = feignClient.performRequest( + PageRequest.of(1, 10, Sort.by(Sort.Order.desc("property")))); + + assertThat(direction).isEqualTo("DESC"); + } + + @FeignClient(name = "pageable", url = "http://localhost:${server.port}/") + protected interface PageableFeignClient { + + @CollectionFormat(feign.CollectionFormat.CSV) + @GetMapping(path = "/page") + String performRequest(Pageable page); + + } + + @SuppressWarnings("ConstantConditions") + @Configuration(proxyBeanMethods = false) + @EnableAutoConfiguration + @RestController + @EnableFeignClients(clients = PageableFeignClient.class) + @Import(NoSecurityConfiguration.class) + protected static class Config { + + @GetMapping(path = "/page") + String performRequest(Pageable page) { + return page.getSort().getOrderFor("property").getDirection().toString(); + } + + } + +} diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/support/SpringMvcContractTests.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/support/SpringMvcContractTests.java index a1a9f9d6d..77fb59c10 100644 --- a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/support/SpringMvcContractTests.java +++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/support/SpringMvcContractTests.java @@ -37,6 +37,7 @@ import org.junit.Test; import org.junit.runner.RunWith; +import org.springframework.cloud.openfeign.CollectionFormat; import org.springframework.cloud.openfeign.SpringQueryMap; import org.springframework.core.convert.ConversionService; import org.springframework.format.annotation.DateTimeFormat; @@ -62,6 +63,7 @@ import static com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility.ANY; import static com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility.NONE; +import static feign.CollectionFormat.SSV; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assume.assumeTrue; @@ -268,6 +270,18 @@ public void testProcessAnnotationsOnMethod_Advanced_UnknownAnnotation() // Don't throw an exception and this passes } + @Test + public void testProcessAnnotationsOnMethod_CollectionFormat() + throws NoSuchMethodException { + Method method = TestTemplate_Advanced.class + .getDeclaredMethod("getWithCollectionFormat"); + + MethodMetadata data = contract + .parseAndValidateMetadata(method.getDeclaringClass(), method); + + assertThat(data.template().collectionFormat()).isEqualTo(SSV); + } + @Test public void testProcessAnnotations_Advanced() throws Exception { Method method = TestTemplate_Advanced.class.getDeclaredMethod("getTest", @@ -774,6 +788,10 @@ public interface TestTemplate_MatrixVariable { @RequestMapping("/advanced") public interface TestTemplate_Advanced { + @CollectionFormat(SSV) + @GetMapping + ResponseEntity getWithCollectionFormat(); + @ExceptionHandler @RequestMapping(path = "/test/{id}", method = RequestMethod.PUT, produces = MediaType.APPLICATION_JSON_VALUE) From 1d17e1edd28cb9ad4c4e64899cf4d203f9fdd764 Mon Sep 17 00:00:00 2001 From: Olga Maciaszek-Sharma Date: Wed, 15 Jul 2020 16:51:09 +0200 Subject: [PATCH 2/3] Minor refactoring. --- .../openfeign/support/SpringMvcContract.java | 31 ++++---- .../support/SpringMvcContractTests.java | 78 +++++++++---------- 2 files changed, 54 insertions(+), 55 deletions(-) diff --git a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/support/SpringMvcContract.java b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/support/SpringMvcContract.java index ab7b5679f..4fb21c15b 100644 --- a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/support/SpringMvcContract.java +++ b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/support/SpringMvcContract.java @@ -122,9 +122,9 @@ public SpringMvcContract( List processors = getDefaultAnnotatedArgumentsProcessors(); processors.addAll(annotatedParameterProcessors); - this.annotatedArgumentProcessors = toAnnotatedArgumentProcessorMap(processors); + annotatedArgumentProcessors = toAnnotatedArgumentProcessorMap(processors); this.conversionService = conversionService; - this.convertingExpanderFactory = new ConvertingExpanderFactory(conversionService); + convertingExpanderFactory = new ConvertingExpanderFactory(conversionService); } private static TypeDescriptor createTypeDescriptor(Method method, int paramIndex) { @@ -188,7 +188,7 @@ protected void processAnnotationOnClass(MethodMetadata data, Class clz) { @Override public MethodMetadata parseAndValidateMetadata(Class targetType, Method method) { - this.processedMethods.put(Feign.configKey(targetType, method), method); + processedMethods.put(Feign.configKey(targetType, method), method); MethodMetadata md = super.parseAndValidateMetadata(targetType, method); RequestMapping classAnnotation = findMergedAnnotation(targetType, @@ -255,13 +255,13 @@ protected void processAnnotationOnMethod(MethodMetadata data, // headers parseHeaders(data, method, methodMapping); - data.indexToExpander(new LinkedHashMap()); + data.indexToExpander(new LinkedHashMap<>()); } private String resolve(String value) { if (StringUtils.hasText(value) - && this.resourceLoader instanceof ConfigurableApplicationContext) { - return ((ConfigurableApplicationContext) this.resourceLoader).getEnvironment() + && resourceLoader instanceof ConfigurableApplicationContext) { + return ((ConfigurableApplicationContext) resourceLoader).getEnvironment() .resolvePlaceholders(value); } return value; @@ -287,9 +287,9 @@ protected boolean processAnnotationsOnParameter(MethodMetadata data, AnnotatedParameterProcessor.AnnotatedParameterContext context = new SimpleAnnotatedParameterContext( data, paramIndex); - Method method = this.processedMethods.get(data.configKey()); + Method method = processedMethods.get(data.configKey()); for (Annotation parameterAnnotation : annotations) { - AnnotatedParameterProcessor processor = this.annotatedArgumentProcessors + AnnotatedParameterProcessor processor = annotatedArgumentProcessors .get(parameterAnnotation.annotationType()); if (processor != null) { Annotation processParameterAnnotation; @@ -305,9 +305,8 @@ protected boolean processAnnotationsOnParameter(MethodMetadata data, if (!isMultipartFormData(data) && isHttpAnnotation && data.indexToExpander().get(paramIndex) == null) { TypeDescriptor typeDescriptor = createTypeDescriptor(method, paramIndex); - if (this.conversionService.canConvert(typeDescriptor, - STRING_TYPE_DESCRIPTOR)) { - Param.Expander expander = this.convertingExpanderFactory + if (conversionService.canConvert(typeDescriptor, STRING_TYPE_DESCRIPTOR)) { + Param.Expander expander = convertingExpanderFactory .getExpander(typeDescriptor); if (expander != null) { data.indexToExpander().put(paramIndex, expander); @@ -426,7 +425,7 @@ public ConvertingExpander(ConversionService conversionService) { @Override public String expand(Object value) { - return this.conversionService.convert(value, String.class); + return conversionService.convert(value, String.class); } } @@ -441,7 +440,7 @@ private static class ConvertingExpanderFactory { Param.Expander getExpander(TypeDescriptor typeDescriptor) { return value -> { - Object converted = this.conversionService.convert(value, typeDescriptor, + Object converted = conversionService.convert(value, typeDescriptor, STRING_TYPE_DESCRIPTOR); return (String) converted; }; @@ -464,17 +463,17 @@ private class SimpleAnnotatedParameterContext @Override public MethodMetadata getMethodMetadata() { - return this.methodMetadata; + return methodMetadata; } @Override public int getParameterIndex() { - return this.parameterIndex; + return parameterIndex; } @Override public void setParameterName(String name) { - nameParam(this.methodMetadata, name, this.parameterIndex); + nameParam(methodMetadata, name, parameterIndex); } @Override diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/support/SpringMvcContractTests.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/support/SpringMvcContractTests.java index 77fb59c10..15886e57b 100644 --- a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/support/SpringMvcContractTests.java +++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/support/SpringMvcContractTests.java @@ -73,6 +73,7 @@ * @author Aram Peres * @author Aaron Whiteside * @author Artyom Romanenko + * @author Olga Maciaszek-Sharma */ @RunWith(JUnitParamsRunner.class) public class SpringMvcContractTests { @@ -125,14 +126,14 @@ public void setup() { conversionServiceFactoryBean.afterPropertiesSet(); ConversionService conversionService = conversionServiceFactoryBean.getObject(); - this.contract = new SpringMvcContract(Collections.emptyList(), conversionService); + contract = new SpringMvcContract(Collections.emptyList(), conversionService); } @Test public void testProcessAnnotationOnMethod_Simple() throws Exception { Method method = TestTemplate_Simple.class.getDeclaredMethod("getTest", String.class); - MethodMetadata data = this.contract + MethodMetadata data = contract .parseAndValidateMetadata(method.getDeclaringClass(), method); assertThat(data.template().url()).isEqualTo("/test/{id}"); @@ -145,7 +146,7 @@ public void testProcessAnnotationOnMethod_Simple() throws Exception { public void testProcessAnnotations_Simple() throws Exception { Method method = TestTemplate_Simple.class.getDeclaredMethod("getTest", String.class); - MethodMetadata data = this.contract + MethodMetadata data = contract .parseAndValidateMetadata(method.getDeclaringClass(), method); assertThat(data.template().url()).isEqualTo("/test/{id}"); @@ -160,7 +161,7 @@ public void testProcessAnnotations_Simple() throws Exception { public void testProcessAnnotations_SimpleGetMapping() throws Exception { Method method = TestTemplate_Simple.class.getDeclaredMethod("getMappingTest", String.class); - MethodMetadata data = this.contract + MethodMetadata data = contract .parseAndValidateMetadata(method.getDeclaringClass(), method); assertThat(data.template().url()).isEqualTo("/test/{id}"); @@ -176,7 +177,7 @@ public void testProcessAnnotations_Class_AnnotationsGetSpecificTest() throws Exception { Method method = TestTemplate_Class_Annotations.class .getDeclaredMethod("getSpecificTest", String.class, String.class); - MethodMetadata data = this.contract + MethodMetadata data = contract .parseAndValidateMetadata(method.getDeclaringClass(), method); assertThat(data.template().url()).isEqualTo("/prepend/{classId}/test/{testId}"); @@ -190,7 +191,7 @@ public void testProcessAnnotations_Class_AnnotationsGetSpecificTest() public void testProcessAnnotations_Class_AnnotationsGetAllTests() throws Exception { Method method = TestTemplate_Class_Annotations.class .getDeclaredMethod("getAllTests", String.class); - MethodMetadata data = this.contract + MethodMetadata data = contract .parseAndValidateMetadata(method.getDeclaringClass(), method); assertThat(data.template().url()).isEqualTo("/prepend/{classId}"); @@ -203,12 +204,12 @@ public void testProcessAnnotations_Class_AnnotationsGetAllTests() throws Excepti public void testProcessAnnotations_ExtendedInterface() throws Exception { Method extendedMethod = TestTemplate_Extended.class.getMethod("getAllTests", String.class); - MethodMetadata extendedData = this.contract.parseAndValidateMetadata( + MethodMetadata extendedData = contract.parseAndValidateMetadata( extendedMethod.getDeclaringClass(), extendedMethod); Method method = TestTemplate_Class_Annotations.class .getDeclaredMethod("getAllTests", String.class); - MethodMetadata data = this.contract + MethodMetadata data = contract .parseAndValidateMetadata(method.getDeclaringClass(), method); assertThat(data.template().url()).isEqualTo(extendedData.template().url()); @@ -222,7 +223,7 @@ public void testProcessAnnotations_ExtendedInterface() throws Exception { public void testProcessAnnotations_SimplePost() throws Exception { Method method = TestTemplate_Simple.class.getDeclaredMethod("postTest", TestObject.class); - MethodMetadata data = this.contract + MethodMetadata data = contract .parseAndValidateMetadata(method.getDeclaringClass(), method); assertThat(data.template().url()).isEqualTo("/"); @@ -236,7 +237,7 @@ public void testProcessAnnotations_SimplePost() throws Exception { public void testProcessAnnotations_SimplePostMapping() throws Exception { Method method = TestTemplate_Simple.class.getDeclaredMethod("postMappingTest", TestObject.class); - MethodMetadata data = this.contract + MethodMetadata data = contract .parseAndValidateMetadata(method.getDeclaringClass(), method); assertThat(data.template().url()).isEqualTo("/"); @@ -250,7 +251,7 @@ public void testProcessAnnotations_SimplePostMapping() throws Exception { public void testProcessAnnotationsOnMethod_Advanced() throws Exception { Method method = TestTemplate_Advanced.class.getDeclaredMethod("getTest", String.class, String.class, Integer.class); - MethodMetadata data = this.contract + MethodMetadata data = contract .parseAndValidateMetadata(method.getDeclaringClass(), method); assertThat(data.template().url()) @@ -265,7 +266,7 @@ public void testProcessAnnotationsOnMethod_Advanced_UnknownAnnotation() throws Exception { Method method = TestTemplate_Advanced.class.getDeclaredMethod("getTest", String.class, String.class, Integer.class); - this.contract.parseAndValidateMetadata(method.getDeclaringClass(), method); + contract.parseAndValidateMetadata(method.getDeclaringClass(), method); // Don't throw an exception and this passes } @@ -286,7 +287,7 @@ public void testProcessAnnotationsOnMethod_CollectionFormat() public void testProcessAnnotations_Advanced() throws Exception { Method method = TestTemplate_Advanced.class.getDeclaredMethod("getTest", String.class, String.class, Integer.class); - MethodMetadata data = this.contract + MethodMetadata data = contract .parseAndValidateMetadata(method.getDeclaringClass(), method); assertThat(data.template().url()) @@ -311,7 +312,7 @@ public void testProcessAnnotations_Advanced() throws Exception { public void testProcessAnnotations_Aliased() throws Exception { Method method = TestTemplate_Advanced.class.getDeclaredMethod("getTest2", String.class, Integer.class); - MethodMetadata data = this.contract + MethodMetadata data = contract .parseAndValidateMetadata(method.getDeclaringClass(), method); assertThat(data.template().url()) @@ -334,7 +335,7 @@ public void testProcessAnnotations_Aliased() throws Exception { public void testProcessAnnotations_DateTimeFormatParam() throws Exception { Method method = TestTemplate_DateTimeFormatParameter.class .getDeclaredMethod("getTest", LocalDateTime.class); - MethodMetadata data = this.contract + MethodMetadata data = contract .parseAndValidateMetadata(method.getDeclaringClass(), method); Param.Expander expander = data.indexToExpander().get(0); @@ -354,7 +355,7 @@ public void testProcessAnnotations_DateTimeFormatParam() throws Exception { public void testProcessAnnotations_NumberFormatParam() throws Exception { Method method = TestTemplate_NumberFormatParameter.class .getDeclaredMethod("getTest", BigDecimal.class); - MethodMetadata data = this.contract + MethodMetadata data = contract .parseAndValidateMetadata(method.getDeclaringClass(), method); Param.Expander expander = data.indexToExpander().get(0); @@ -374,7 +375,7 @@ public void testProcessAnnotations_NumberFormatParam() throws Exception { @Test public void testProcessAnnotations_Advanced2() throws Exception { Method method = TestTemplate_Advanced.class.getDeclaredMethod("getTest"); - MethodMetadata data = this.contract + MethodMetadata data = contract .parseAndValidateMetadata(method.getDeclaringClass(), method); assertThat(data.template().url()).isEqualTo("/advanced"); @@ -386,7 +387,7 @@ public void testProcessAnnotations_Advanced2() throws Exception { @Test public void testProcessAnnotations_Advanced3() throws Exception { Method method = TestTemplate_Simple.class.getDeclaredMethod("getTest"); - MethodMetadata data = this.contract + MethodMetadata data = contract .parseAndValidateMetadata(method.getDeclaringClass(), method); assertThat(data.template().url()).isEqualTo("/"); @@ -399,7 +400,7 @@ public void testProcessAnnotations_Advanced3() throws Exception { public void testProcessAnnotations_ListParams() throws Exception { Method method = TestTemplate_ListParams.class.getDeclaredMethod("getTest", List.class); - MethodMetadata data = this.contract + MethodMetadata data = contract .parseAndValidateMetadata(method.getDeclaringClass(), method); assertThat(data.template().url()).isEqualTo("/test?id=" + "{id}"); @@ -412,7 +413,7 @@ public void testProcessAnnotations_ListParams() throws Exception { public void testProcessAnnotations_ListParamsWithoutName() throws Exception { Method method = TestTemplate_ListParamsWithoutName.class .getDeclaredMethod("getTest", List.class); - MethodMetadata data = this.contract + MethodMetadata data = contract .parseAndValidateMetadata(method.getDeclaringClass(), method); assertThat(data.template().url()).isEqualTo("/test?id=" + "{id}"); @@ -425,7 +426,7 @@ public void testProcessAnnotations_ListParamsWithoutName() throws Exception { public void testProcessAnnotations_MapParams() throws Exception { Method method = TestTemplate_MapParams.class.getDeclaredMethod("getTest", Map.class); - MethodMetadata data = this.contract + MethodMetadata data = contract .parseAndValidateMetadata(method.getDeclaringClass(), method); assertThat(data.template().url()).isEqualTo("/test"); @@ -438,7 +439,7 @@ public void testProcessAnnotations_MapParams() throws Exception { public void testProcessHeaders() throws Exception { Method method = TestTemplate_Headers.class.getDeclaredMethod("getTest", String.class); - MethodMetadata data = this.contract + MethodMetadata data = contract .parseAndValidateMetadata(method.getDeclaringClass(), method); assertThat(data.template().url()).isEqualTo("/test/{id}"); @@ -451,7 +452,7 @@ public void testProcessHeaders() throws Exception { public void testProcessHeadersWithoutValues() throws Exception { Method method = TestTemplate_HeadersWithoutValues.class .getDeclaredMethod("getTest", String.class); - MethodMetadata data = this.contract + MethodMetadata data = contract .parseAndValidateMetadata(method.getDeclaringClass(), method); assertThat(data.template().url()).isEqualTo("/test/{id}"); @@ -467,7 +468,7 @@ public void testProcessAnnotations_Fallback() throws Exception { assumeTrue("does not have java 8 parameter names", hasJava8ParameterNames(method)); - MethodMetadata data = this.contract + MethodMetadata data = contract .parseAndValidateMetadata(method.getDeclaringClass(), method); assertThat(data.template().url()) @@ -491,7 +492,7 @@ public void testProcessAnnotations_Fallback() throws Exception { public void testProcessHeaderMap() throws Exception { Method method = TestTemplate_HeaderMap.class.getDeclaredMethod("headerMap", MultiValueMap.class, String.class); - MethodMetadata data = this.contract + MethodMetadata data = contract .parseAndValidateMetadata(method.getDeclaringClass(), method); assertThat(data.template().url()).isEqualTo("/headerMap"); @@ -505,14 +506,14 @@ public void testProcessHeaderMap() throws Exception { public void testProcessHeaderMapMoreThanOnce() throws Exception { Method method = TestTemplate_HeaderMap.class.getDeclaredMethod( "headerMapMoreThanOnce", MultiValueMap.class, MultiValueMap.class); - this.contract.parseAndValidateMetadata(method.getDeclaringClass(), method); + contract.parseAndValidateMetadata(method.getDeclaringClass(), method); } @Test public void testProcessQueryMap() throws Exception { Method method = TestTemplate_QueryMap.class.getDeclaredMethod("queryMap", MultiValueMap.class, String.class); - MethodMetadata data = this.contract + MethodMetadata data = contract .parseAndValidateMetadata(method.getDeclaringClass(), method); assertThat(data.template().url()).isEqualTo("/queryMap?aParam=" + "{aParam}"); @@ -526,7 +527,7 @@ public void testProcessQueryMap() throws Exception { public void testProcessQueryMapObject() throws Exception { Method method = TestTemplate_QueryMap.class.getDeclaredMethod("queryMapObject", TestObject.class, String.class); - MethodMetadata data = this.contract + MethodMetadata data = contract .parseAndValidateMetadata(method.getDeclaringClass(), method); assertThat(data.template().url()) @@ -541,14 +542,14 @@ public void testProcessQueryMapObject() throws Exception { public void testProcessQueryMapMoreThanOnce() throws Exception { Method method = TestTemplate_QueryMap.class.getDeclaredMethod( "queryMapMoreThanOnce", MultiValueMap.class, MultiValueMap.class); - this.contract.parseAndValidateMetadata(method.getDeclaringClass(), method); + contract.parseAndValidateMetadata(method.getDeclaringClass(), method); } @Test public void testMatrixVariable_MapParam() throws Exception { Method method = TestTemplate_MatrixVariable.class .getDeclaredMethod("matrixVariable", Map.class); - MethodMetadata data = this.contract + MethodMetadata data = contract .parseAndValidateMetadata(method.getDeclaringClass(), method); Map testMap = new HashMap<>(); @@ -564,7 +565,7 @@ public void testMatrixVariable_MapParam() throws Exception { public void testMatrixVariable_ObjectParam() throws Exception { Method method = TestTemplate_MatrixVariable.class .getDeclaredMethod("matrixVariableObject", Object.class); - MethodMetadata data = this.contract + MethodMetadata data = contract .parseAndValidateMetadata(method.getDeclaringClass(), method); assertThat(data.template().method()).isEqualTo("GET"); @@ -577,7 +578,7 @@ public void testMatrixVariable_ObjectParam() throws Exception { public void testMatrixVariableWithNoName() throws NoSuchMethodException { Method method = TestTemplate_MatrixVariable.class .getDeclaredMethod("matrixVariableNotNamed", Map.class); - MethodMetadata data = this.contract + MethodMetadata data = contract .parseAndValidateMetadata(method.getDeclaringClass(), method); Map testMap = new HashMap<>(); @@ -901,11 +902,10 @@ public boolean equals(Object o) { TestObject that = (TestObject) o; - if (this.number != null ? !this.number.equals(that.number) - : that.number != null) { + if (number != null ? !number.equals(that.number) : that.number != null) { return false; } - if (this.something != null ? !this.something.equals(that.something) + if (something != null ? !something.equals(that.something) : that.something != null) { return false; } @@ -915,16 +915,16 @@ public boolean equals(Object o) { @Override public int hashCode() { - int result = (this.something != null ? this.something.hashCode() : 0); - result = 31 * result + (this.number != null ? this.number.hashCode() : 0); + int result = (something != null ? something.hashCode() : 0); + result = 31 * result + (number != null ? number.hashCode() : 0); return result; } @Override public String toString() { return new StringBuilder("TestObject{").append("something='") - .append(this.something).append("', ").append("number=") - .append(this.number).append("}").toString(); + .append(something).append("', ").append("number=").append(number) + .append("}").toString(); } } From 5742cd44cbbaa3b4fa1a119dcbb751f2bd74590a Mon Sep 17 00:00:00 2001 From: Olga Maciaszek-Sharma Date: Thu, 16 Jul 2020 13:47:55 +0200 Subject: [PATCH 3/3] Fix docs. --- docs/src/main/asciidoc/spring-cloud-openfeign.adoc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/src/main/asciidoc/spring-cloud-openfeign.adoc b/docs/src/main/asciidoc/spring-cloud-openfeign.adoc index d48ba67ed..7cc788577 100644 --- a/docs/src/main/asciidoc/spring-cloud-openfeign.adoc +++ b/docs/src/main/asciidoc/spring-cloud-openfeign.adoc @@ -587,9 +587,9 @@ public interface DemoTemplate { ---- === Feign `CollectionFormat` support -We support `feign.CollectionFormat` by providing the `@CollectionFormat` annotation. You can annotate a feign client method with it passing the desired `feign.CollectionFormat` as annotation value. +We support `feign.CollectionFormat` by providing the `@CollectionFormat` annotation. You can annotate a Feign client method with it by passing the desired `feign.CollectionFormat` as annotation value. -For example, in the example below, the `CSV` format will be used instead of the default `EXPLODED` to process the method. +In the following example, the `CSV` format is used instead of the default `EXPLODED` to process the method. [source,java,indent=0] ---- @@ -603,7 +603,7 @@ For example, in the example below, the `CSV` format will be used instead of the } ---- -TIP: It's suggested to set the `CSV` format while sending `Pageable` as a query parameter in order for it to be encoded correctly. +TIP: Set the `CSV` format while sending `Pageable` as a query parameter in order for it to be encoded correctly. === Reactive Support As the https://github.com/OpenFeign/feign[OpenFeign project] does not currently support reactive clients, such as https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/reactive/function/client/WebClient.html[Spring WebClient], neither does Spring Cloud OpenFeign. We will add support for it here as soon as it becomes available in the core project.