Compare commits

...
20 Commits
Author SHA1 Message Date
Olga MaciaszekSharma 161789e33a Going back to snapshots. 2021-11-17 23:12:16 +01:00
buildmaster 650b14ac22 Bumping versions 2021-10-21 11:07:10 +00:00
Olga MaciaszekSharma 17d0161faf Reformat. 2021-10-21 12:40:20 +02:00
Olga MaciaszekSharma d6783a6f1e Block clas-level request mapping on Feign clients. 2021-10-21 12:14:19 +02:00
Olga MaciaszekSharma 1275875ddb Reformat. 2021-07-07 18:58:48 +02:00
Olga MaciaszekSharma c4b9492a9b Bumping versions to 2.2.10.BUILD-SNAPSHOT after release
This reverts commit f7e490c466.
2021-07-07 18:46:53 +02:00
Olga MaciaszekSharma 3acf5f9e92 Going back to snapshots
This reverts commit 6479db0a54.
2021-07-07 18:46:20 +02:00
buildmaster e9e38d0274 Bumping versions 2021-07-07 14:07:20 +00:00
Olga MaciaszekSharma 28b50401c5 Revert "Update SNAPSHOT to 2.2.9.RELEASE"
This reverts commit 1005749eb5.
2021-07-07 14:38:12 +02:00
Olga MaciaszekSharma 6479db0a54 Revert "Going back to snapshots"
This reverts commit 190f5a9083.
2021-07-07 14:37:39 +02:00
Olga MaciaszekSharma f7e490c466 Revert "Bumping versions to 2.2.10.BUILD-SNAPSHOT after release"
This reverts commit d60cda824d.
2021-07-07 14:37:03 +02:00
buildmaster d60cda824d Bumping versions to 2.2.10.BUILD-SNAPSHOT after release 2021-07-06 14:53:53 +00:00
buildmaster 190f5a9083 Going back to snapshots 2021-07-06 14:53:53 +00:00
buildmaster 1005749eb5 Update SNAPSHOT to 2.2.9.RELEASE 2021-07-06 14:52:01 +00:00
Michal Domagala df1f982046 Fix for RetryableFeignBlockingLoadBalancerClient closes stream (#569) 2021-07-05 13:03:06 +02:00
Michal Domagala baf7cf95f2 Simplify Log.info logic (#556) 2021-07-05 12:43:59 +02:00
Olga MaciaszekSharma 00ea88c67f Fix typo. Refactor. 2021-07-05 11:55:41 +02:00
Olga MaciaszekSharma d897b18e16 Add missing author tags. Refactor. 2021-07-05 11:26:06 +02:00
Can Bezmen 8c08204c78 Issue #549
* Fixed: Form URL Encoded bug fixed.

Fixes gh-549.
2021-07-05 11:19:29 +02:00
Olga MaciaszekSharma 2f43715f6f Change log level. Fixes gh-552. 2021-06-10 16:04:17 +02:00
17 changed files with 264 additions and 223 deletions
+2 -16
View File
@@ -66,23 +66,9 @@ the `.mvn` configuration, so if you find you have to do it to make a
build succeed, please raise a ticket to get the settings added to
source control.
For hints on how to build the project look in `.travis.yml` if there
is one. There should be a "script" and maybe "install" command. Also
look at the "services" section to see if any services need to be
running locally (e.g. mongo or rabbit). Ignore the git-related bits
that you might find in "before_install" since they're related to setting git
credentials and you already have those.
The projects that require middleware (i.e. Redis) for testing generally
require that a local instance of [Docker](https://www.docker.com/get-started) is installed and running.
The projects that require middleware generally include a
`docker-compose.yml`, so consider using
https://docs.docker.com/compose/[Docker Compose] to run the middeware servers
in Docker containers. See the README in the
https://github.com/spring-cloud-samples/scripts[scripts demo
repository] for specific instructions about the common cases of mongo,
rabbit and redis.
NOTE: If all else fails, build with the command from `.travis.yml` (usually
`./mvnw install`).
=== Documentation
+1 -1
View File
@@ -6,7 +6,7 @@
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-openfeign</artifactId>
<version>2.2.9.BUILD-SNAPSHOT</version>
<version>2.2.11.BUILD-SNAPSHOT</version>
</parent>
<artifactId>spring-cloud-openfeign-docs</artifactId>
<packaging>pom</packaging>
@@ -523,10 +523,7 @@ public interface UserClient extends UserService {
}
----
NOTE: It is generally not advisable to share an interface between a
server and a client. It introduces tight coupling, and also actually
doesn't work with Spring MVC in its current form (method parameter
mapping is not inherited).
WARNING: `@FeignClient` interfaces should not be shared between server and client and annotating `@FeignClient` interfaces with `@RequestMapping` on class level is no longer supported.
=== Feign request/response compression
+4 -4
View File
@@ -4,14 +4,14 @@
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-cloud-openfeign</artifactId>
<version>2.2.9.BUILD-SNAPSHOT</version>
<version>2.2.11.BUILD-SNAPSHOT</version>
<packaging>pom</packaging>
<name>Spring Cloud OpenFeign</name>
<description>Spring Cloud OpenFeign</description>
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-build</artifactId>
<version>2.3.4.RELEASE</version>
<version>2.3.6.BUILD-SNAPSHOT</version>
<relativePath/>
</parent>
<scm>
@@ -26,8 +26,8 @@
<properties>
<main.basedir>${basedir}</main.basedir>
<jackson.version>2.11.3</jackson.version>
<spring-cloud-commons.version>2.2.9.BUILD-SNAPSHOT</spring-cloud-commons.version>
<spring-cloud-netflix.version>2.2.9.BUILD-SNAPSHOT</spring-cloud-netflix.version>
<spring-cloud-commons.version>2.2.9.RELEASE</spring-cloud-commons.version>
<spring-cloud-netflix.version>2.2.9.RELEASE</spring-cloud-netflix.version>
<!-- Plugin versions -->
<maven-eclipse-plugin.version>2.10</maven-eclipse-plugin.version>
+1 -1
View File
@@ -6,7 +6,7 @@
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-openfeign</artifactId>
<version>2.2.9.BUILD-SNAPSHOT</version>
<version>2.2.11.BUILD-SNAPSHOT</version>
<relativePath>..</relativePath> <!-- lookup parent from repository -->
</parent>
<artifactId>spring-cloud-openfeign-core</artifactId>
@@ -373,12 +373,10 @@ public class FeignClientFactoryBean implements FactoryBean<Object>, Initializing
Feign.Builder builder = feign(context);
if (!StringUtils.hasText(url)) {
if (url != null && LOG.isWarnEnabled()) {
LOG.warn(
"The provided URL is empty. Will try picking an instance via load-balancing.");
}
else if (LOG.isDebugEnabled()) {
LOG.debug("URL not provided. Will use LoadBalancer.");
if (LOG.isInfoEnabled()) {
LOG.info("For '" + name
+ "' URL not provided. Will try picking an instance via load-balancing.");
}
if (!name.startsWith("http")) {
url = "http://" + name;
@@ -76,7 +76,8 @@ public class MatrixVariableParameterProcessor implements AnnotatedParameterProce
Map<String, Object> paramMap = (Map) object;
return paramMap.keySet().stream().filter(key -> paramMap.get(key) != null)
.map(key -> ";" + key + "=" + paramMap.get(key).toString()).collect(Collectors.joining());
.map(key -> ";" + key + "=" + paramMap.get(key).toString())
.collect(Collectors.joining());
}
}
@@ -0,0 +1,50 @@
/*
* 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.loadbalancer;
import java.io.ByteArrayInputStream;
import java.net.URI;
import feign.Response;
import org.springframework.cloud.client.loadbalancer.RetryableStatusCodeException;
/**
* A {@link RetryableStatusCodeException} for {@link Response}s.
*
* @author Ryan Baxter
*/
public class LoadBalancerResponseStatusCodeException
extends RetryableStatusCodeException {
private final Response response;
public LoadBalancerResponseStatusCodeException(String serviceId, Response response,
byte[] body, URI uri) {
super(serviceId, response.status(), response, uri);
this.response = Response.builder()
.body(new ByteArrayInputStream(body), body.length)
.headers(response.headers()).reason(response.reason())
.status(response.status()).request(response.request()).build();
}
@Override
public Response getResponse() {
return this.response;
}
}
@@ -36,7 +36,6 @@ import org.springframework.cloud.client.loadbalancer.LoadBalancedRecoveryCallbac
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryContext;
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryFactory;
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryPolicy;
import org.springframework.cloud.client.loadbalancer.RetryableStatusCodeException;
import org.springframework.cloud.loadbalancer.blocking.client.BlockingLoadBalancerClient;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
@@ -46,6 +45,7 @@ import org.springframework.retry.backoff.BackOffPolicy;
import org.springframework.retry.backoff.NoBackOffPolicy;
import org.springframework.retry.policy.NeverRetryPolicy;
import org.springframework.retry.support.RetryTemplate;
import org.springframework.util.StreamUtils;
/**
* A {@link Client} implementation that provides Spring Retry support for requests
@@ -112,9 +112,11 @@ public class RetryableFeignBlockingLoadBalancerClient implements Client {
LOG.debug(
String.format("Retrying on status code: %d", responseStatus));
}
byte[] byteArray = response.body() == null ? new byte[] {}
: StreamUtils.copyToByteArray(response.body().asInputStream());
response.close();
throw new RetryableStatusCodeException(serviceId, responseStatus,
response, URI.create(request.url()));
throw new LoadBalancerResponseStatusCodeException(serviceId, response,
byteArray, URI.create(request.url()));
}
return response;
}, new LoadBalancedRecoveryCallback<Response, Response>() {
@@ -24,6 +24,7 @@ import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Collection;
import java.util.Objects;
import java.util.stream.Stream;
import feign.RequestTemplate;
@@ -48,6 +49,10 @@ import org.springframework.web.multipart.MultipartFile;
import static org.springframework.cloud.openfeign.support.FeignUtils.getHeaders;
import static org.springframework.cloud.openfeign.support.FeignUtils.getHttpHeaders;
import static org.springframework.http.MediaType.APPLICATION_FORM_URLENCODED;
import static org.springframework.http.MediaType.MULTIPART_FORM_DATA;
import static org.springframework.http.MediaType.MULTIPART_MIXED;
import static org.springframework.http.MediaType.MULTIPART_RELATED;
/**
* @author Spencer Gibb
@@ -56,6 +61,7 @@ import static org.springframework.cloud.openfeign.support.FeignUtils.getHttpHead
* @author Aaron Whiteside
* @author Darren Foong
* @author Olga Maciaszek-Sharma
* @author Can Bezmen
*/
@SuppressWarnings("rawtypes")
public class SpringEncoder implements Encoder {
@@ -99,8 +105,8 @@ public class SpringEncoder implements Encoder {
requestContentType = MediaType.valueOf(type);
}
if (isMultipartType(requestContentType)) {
this.springFormEncoder.encode(requestBody, bodyType, request);
if (isFormRelatedContentType(requestContentType)) {
springFormEncoder.encode(requestBody, bodyType, request);
return;
}
else {
@@ -117,7 +123,7 @@ public class SpringEncoder implements Encoder {
private void encodeWithMessageConverter(Object requestBody, Type bodyType,
RequestTemplate request, MediaType requestContentType) {
for (HttpMessageConverter messageConverter : this.messageConverters.getObject()
for (HttpMessageConverter messageConverter : messageConverters.getObject()
.getConverters()) {
FeignOutputMessage outputMessage;
try {
@@ -223,9 +229,18 @@ public class SpringEncoder implements Encoder {
}
}
private boolean isFormRelatedContentType(MediaType requestContentType) {
return isMultipartType(requestContentType)
|| isFormUrlEncoded(requestContentType);
}
private boolean isMultipartType(MediaType requestContentType) {
return Arrays.asList(MediaType.MULTIPART_FORM_DATA, MediaType.MULTIPART_MIXED,
MediaType.MULTIPART_RELATED).contains(requestContentType);
return Arrays.asList(MULTIPART_FORM_DATA, MULTIPART_MIXED, MULTIPART_RELATED)
.contains(requestContentType);
}
private boolean isFormUrlEncoded(MediaType requestContentType) {
return Objects.equals(APPLICATION_FORM_URLENCODED, requestContentType);
}
private boolean binaryContentType(FeignOutputMessage outputMessage) {
@@ -244,21 +259,21 @@ public class SpringEncoder implements Encoder {
private final HttpHeaders httpHeaders;
private FeignOutputMessage(RequestTemplate request) {
this.httpHeaders = getHttpHeaders(request.headers());
httpHeaders = getHttpHeaders(request.headers());
}
@Override
public OutputStream getBody() throws IOException {
return this.outputStream;
return outputStream;
}
@Override
public HttpHeaders getHeaders() {
return this.httpHeaders;
return httpHeaders;
}
public ByteArrayOutputStream getOutputStream() {
return this.outputStream;
return outputStream;
}
}
@@ -35,6 +35,8 @@ import feign.Feign;
import feign.MethodMetadata;
import feign.Param;
import feign.Request;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.cloud.openfeign.AnnotatedParameterProcessor;
import org.springframework.cloud.openfeign.CollectionFormat;
@@ -83,6 +85,8 @@ import static org.springframework.core.annotation.AnnotatedElementUtils.findMerg
public class SpringMvcContract extends Contract.BaseContract
implements ResourceLoaderAware {
private static final Log LOG = LogFactory.getLog(SpringMvcContract.class);
private static final String ACCEPT = "Accept";
private static final String CONTENT_TYPE = "Content-Type";
@@ -181,49 +185,19 @@ public class SpringMvcContract extends Contract.BaseContract
@Override
protected void processAnnotationOnClass(MethodMetadata data, Class<?> clz) {
if (clz.getInterfaces().length == 0) {
RequestMapping classAnnotation = findMergedAnnotation(clz,
RequestMapping.class);
if (classAnnotation != null) {
// Prepend path from class annotation if specified
if (classAnnotation.value().length > 0) {
String pathValue = emptyToNull(classAnnotation.value()[0]);
pathValue = resolve(pathValue);
if (!pathValue.startsWith("/")) {
pathValue = "/" + pathValue;
}
data.template().uri(pathValue);
if (data.template().decodeSlash() != decodeSlash) {
data.template().decodeSlash(decodeSlash);
}
}
}
RequestMapping classAnnotation = findMergedAnnotation(clz, RequestMapping.class);
if (classAnnotation != null) {
LOG.error("Cannot process class: " + clz.getName()
+ ". @RequestMapping annotation is not allowed on @FeignClient interfaces.");
throw new IllegalArgumentException(
"@RequestMapping annotation not allowed on @FeignClient interfaces");
}
}
@Override
public MethodMetadata parseAndValidateMetadata(Class<?> targetType, Method method) {
processedMethods.put(Feign.configKey(targetType, method), method);
MethodMetadata md = super.parseAndValidateMetadata(targetType, method);
RequestMapping classAnnotation = findMergedAnnotation(targetType,
RequestMapping.class);
if (classAnnotation != null) {
// produces - use from class annotation only if method has not specified this
if (!md.template().headers().containsKey(ACCEPT)) {
parseProduces(md, method, classAnnotation);
}
// consumes -- use from class annotation only if method has not specified this
if (!md.template().headers().containsKey(CONTENT_TYPE)) {
parseConsumes(md, method, classAnnotation);
}
// headers -- class annotation is inherited to methods, always write these if
// present
parseHeaders(md, method, classAnnotation);
}
return md;
return super.parseAndValidateMetadata(targetType, method);
}
@Override
@@ -16,7 +16,10 @@
package org.springframework.cloud.openfeign.loadbalancer;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.util.Collection;
@@ -27,6 +30,7 @@ import java.util.Map;
import feign.Client;
import feign.Request;
import feign.Response;
import org.apache.commons.io.IOUtils;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
@@ -113,6 +117,15 @@ class RetryableFeignBlockingLoadBalancerClientTests {
return Response.builder().request(testRequest()).status(status).build();
}
private Response testResponse(int status, String body) {
// ByteArrayInputStream ignores close() and must be wrapped
InputStream reallyCloseable = new BufferedInputStream(
new ByteArrayInputStream(body.getBytes(StandardCharsets.UTF_8)));
return Response.builder().request(testRequest()).status(status)
.body(reallyCloseable, null).build();
}
@Test
void shouldExecuteOriginalRequestIfInstanceNotFound() throws IOException {
Request request = testRequest();
@@ -148,6 +161,27 @@ class RetryableFeignBlockingLoadBalancerClientTests {
verify(delegate, times(2)).execute(any(), any());
}
@Test
void shouldExposeResponseBodyOnRetry() throws IOException {
properties.getRetryableStatusCodes().add(503);
Request request = testRequest();
when(delegate.execute(any(), any())).thenReturn(testResponse(503, "foo"),
testResponse(503, "foo"));
when(retryFactory.createRetryPolicy(any(), eq(loadBalancerClient)))
.thenReturn(new BlockingLoadBalancedRetryPolicy("test",
loadBalancerClient, properties));
when(loadBalancerClient.reconstructURI(serviceInstance,
URI.create("http://test/path")))
.thenReturn(URI.create("http://testhost:80/path"));
Response response = feignBlockingLoadBalancerClient.execute(request,
new Request.Options());
String bodyContent = IOUtils
.toString(response.body().asReader(StandardCharsets.UTF_8));
assertThat(bodyContent).isEqualTo("foo");
}
@Test
void shouldPassCorrectRequestToDelegate() throws IOException {
Request request = testRequest();
@@ -60,6 +60,7 @@ import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.http.HttpHeaders.ACCEPT;
import static org.springframework.http.HttpHeaders.CONTENT_LENGTH;
import static org.springframework.http.HttpHeaders.CONTENT_TYPE;
import static org.springframework.http.MediaType.APPLICATION_FORM_URLENCODED_VALUE;
import static org.springframework.http.MediaType.APPLICATION_OCTET_STREAM_VALUE;
import static org.springframework.http.MediaType.MULTIPART_FORM_DATA_VALUE;
import static org.springframework.http.MediaType.TEXT_PLAIN_VALUE;
@@ -68,6 +69,7 @@ import static org.springframework.http.MediaType.TEXT_PLAIN_VALUE;
* @author Spencer Gibb
* @author Olga Maciaszek-Sharma
* @author Ahmad Mozafarnia
* @author Can Bezmen
*/
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = SpringEncoderTests.Application.class,
@@ -192,6 +194,18 @@ public class SpringEncoderTests {
.as("Body content cannot be decoded").contains("hi");
}
@Test
public void testFromURLEncodedValue() {
Encoder encoder = context.getInstance("formUrlEncoded", Encoder.class);
assertThat(encoder).isNotNull();
RequestTemplate request = new RequestTemplate();
request.header(CONTENT_TYPE, APPLICATION_FORM_URLENCODED_VALUE);
String body = "test";
encoder.encode(body, String.class, request);
assertThat(new String(request.requestBody().asBytes()))
.as("Body content cannot be decoded").contains(body);
}
@Test
public void testNoCharsetForBinaryFiles() {
Encoder encoder = context.getInstance("test", Encoder.class);
@@ -62,6 +62,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.assertj.core.api.Assertions.assertThatIllegalArgumentException;
import static org.junit.Assume.assumeTrue;
/**
@@ -182,18 +183,12 @@ public class SpringMvcContractTests {
}
@Test
public void testProcessAnnotations_Class_AnnotationsGetSpecificTest()
throws Exception {
Method method = TestTemplate_Class_Annotations.class
.getDeclaredMethod("getSpecificTest", String.class, String.class);
MethodMetadata data = contract
.parseAndValidateMetadata(method.getDeclaringClass(), method);
assertThat(data.template().url()).isEqualTo("/prepend/{classId}/test/{testId}");
assertThat(data.template().method()).isEqualTo("GET");
assertThat(data.indexToName().get(0).iterator().next()).isEqualTo("classId");
assertThat(data.indexToName().get(1).iterator().next()).isEqualTo("testId");
public void testProcessAnnotations_Class_Annotations_RequestMapping() {
assertThatIllegalArgumentException().isThrownBy(() -> {
Method method = TestTemplate_Class_RequestMapping.class
.getDeclaredMethod("getSpecificTest", String.class, String.class);
contract.parseAndValidateMetadata(method.getDeclaringClass(), method);
});
}
@Test
@@ -203,7 +198,7 @@ public class SpringMvcContractTests {
MethodMetadata data = contract
.parseAndValidateMetadata(method.getDeclaringClass(), method);
assertThat(data.template().url()).isEqualTo("/prepend/{classId}");
assertThat(data.template().url()).isEqualTo("/");
assertThat(data.template().method()).isEqualTo("GET");
assertThat(data.indexToName().get(0).iterator().next()).isEqualTo("classId");
@@ -211,22 +206,6 @@ public class SpringMvcContractTests {
assertThat(data.template().decodeSlash()).isTrue();
}
@Test
public void testProcessAnnotations_Class_AnnotationsGetAllTests_EncodeSlash()
throws Exception {
contract = new SpringMvcContract(Collections.emptyList(), getConversionService(),
false);
Method method = TestTemplate_Class_Annotations.class
.getDeclaredMethod("getAllTests", String.class);
MethodMetadata data = contract
.parseAndValidateMetadata(method.getDeclaringClass(), method);
assertThat(data.template().url()).isEqualTo("/prepend/{classId}");
assertThat(data.template().decodeSlash()).isFalse();
}
@Test
public void testProcessAnnotations_ExtendedInterface() throws Exception {
Method extendedMethod = TestTemplate_Extended.class.getMethod("getAllTests",
@@ -249,27 +228,6 @@ public class SpringMvcContractTests {
assertThat(data.template().decodeSlash()).isTrue();
}
@Test
public void testProcessAnnotations_ExtendedInterface_EncodeSlash() throws Exception {
contract = new SpringMvcContract(Collections.emptyList(), getConversionService(),
false);
Method extendedMethod = TestTemplate_Extended.class.getMethod("getAllTests",
String.class);
MethodMetadata extendedData = contract.parseAndValidateMetadata(
extendedMethod.getDeclaringClass(), extendedMethod);
Method method = TestTemplate_Class_Annotations.class
.getDeclaredMethod("getAllTests", String.class);
MethodMetadata data = contract
.parseAndValidateMetadata(method.getDeclaringClass(), method);
assertThat(data.template().url()).isEqualTo(extendedData.template().url());
assertThat(data.template().method()).isEqualTo(extendedData.template().method());
assertThat(data.template().decodeSlash()).isFalse();
}
@Test
public void testProcessAnnotations_SimplePost() throws Exception {
Method method = TestTemplate_Simple.class.getDeclaredMethod("postTest",
@@ -305,8 +263,7 @@ public class SpringMvcContractTests {
MethodMetadata data = contract
.parseAndValidateMetadata(method.getDeclaringClass(), method);
assertThat(data.template().url())
.isEqualTo("/advanced/test/{id}?amount=" + "{amount}");
assertThat(data.template().url()).isEqualTo("/test/{id}?amount=" + "{amount}");
assertThat(data.template().method()).isEqualTo("PUT");
assertThat(data.template().headers().get("Accept").iterator().next())
.isEqualTo(MediaType.APPLICATION_JSON_VALUE);
@@ -341,8 +298,7 @@ public class SpringMvcContractTests {
MethodMetadata data = contract
.parseAndValidateMetadata(method.getDeclaringClass(), method);
assertThat(data.template().url())
.isEqualTo("/advanced/test/{id}?amount=" + "{amount}");
assertThat(data.template().url()).isEqualTo("/test/{id}?amount=" + "{amount}");
assertThat(data.template().method()).isEqualTo("PUT");
assertThat(data.template().headers().get("Accept").iterator().next())
.isEqualTo(MediaType.APPLICATION_JSON_VALUE);
@@ -366,8 +322,7 @@ public class SpringMvcContractTests {
MethodMetadata data = contract
.parseAndValidateMetadata(method.getDeclaringClass(), method);
assertThat(data.template().url())
.isEqualTo("/advanced/test2?amount=" + "{amount}");
assertThat(data.template().url()).isEqualTo("/test2?amount=" + "{amount}");
assertThat(data.template().method()).isEqualTo("PUT");
assertThat(data.template().headers().get("Accept").iterator().next())
.isEqualTo(MediaType.APPLICATION_JSON_VALUE);
@@ -429,7 +384,7 @@ public class SpringMvcContractTests {
MethodMetadata data = contract
.parseAndValidateMetadata(method.getDeclaringClass(), method);
assertThat(data.template().url()).isEqualTo("/advanced");
assertThat(data.template().url()).isEqualTo("/");
assertThat(data.template().method()).isEqualTo("GET");
assertThat(data.template().headers().get("Accept").iterator().next())
.isEqualTo(MediaType.APPLICATION_JSON_VALUE);
@@ -539,7 +494,7 @@ public class SpringMvcContractTests {
.parseAndValidateMetadata(method.getDeclaringClass(), method);
assertThat(data.template().url())
.isEqualTo("/advanced/testfallback/{id}?amount=" + "{amount}");
.isEqualTo("/testfallback/{id}?amount=" + "{amount}");
assertThat(data.template().method()).isEqualTo("PUT");
assertThat(data.template().headers().get("Accept").iterator().next())
.isEqualTo(MediaType.APPLICATION_JSON_VALUE);
@@ -708,6 +663,14 @@ public class SpringMvcContractTests {
}
@RequestMapping("/prepend/{classId}")
public interface TestTemplate_Class_RequestMapping {
@RequestMapping(value = "/test/{testId}", method = RequestMethod.GET)
TestObject getSpecificTest(@PathVariable("classId") String classId,
@PathVariable("testId") String testId);
}
public interface TestTemplate_Class_Annotations {
@RequestMapping(value = "/test/{testId}", method = RequestMethod.GET)
@@ -812,7 +775,6 @@ public class SpringMvcContractTests {
}
@JsonAutoDetect
@RequestMapping("/advanced")
public interface TestTemplate_Advanced {
@CollectionFormat(SSV)
@@ -17,7 +17,6 @@
package org.springframework.cloud.openfeign.valid;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.text.ParseException;
import java.time.LocalDate;
@@ -49,7 +48,6 @@ import feign.Feign;
import feign.Logger;
import feign.RequestInterceptor;
import feign.RequestTemplate;
import feign.Target;
import feign.codec.EncodeException;
import feign.hystrix.FallbackFactory;
import feign.hystrix.SetterFactory;
@@ -91,6 +89,7 @@ import org.springframework.http.ResponseEntity;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
@@ -102,6 +101,7 @@ import org.springframework.web.multipart.MultipartFile;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hamcrest.core.IsInstanceOf.instanceOf;
import static org.springframework.http.MediaType.APPLICATION_FORM_URLENCODED_VALUE;
/**
* @author Spencer Gibb
@@ -110,6 +110,7 @@ import static org.hamcrest.core.IsInstanceOf.instanceOf;
* @author Halvdan Hoem Grelland
* @author Aaron Whiteside
* @author Darren Foong
* @author Can Bezmen
*/
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = FeignClientTests.Application.class,
@@ -178,23 +179,23 @@ public class FeignClientTests {
@Test
public void testClient() {
assertThat(this.testClient).as("testClient was null").isNotNull();
assertThat(Proxy.isProxyClass(this.testClient.getClass()))
assertThat(testClient).as("testClient was null").isNotNull();
assertThat(Proxy.isProxyClass(testClient.getClass()))
.as("testClient is not a java Proxy").isTrue();
InvocationHandler invocationHandler = Proxy.getInvocationHandler(this.testClient);
InvocationHandler invocationHandler = Proxy.getInvocationHandler(testClient);
assertThat(invocationHandler).as("invocationHandler was null").isNotNull();
}
@Test
public void testRequestMappingClassLevelPropertyReplacement() {
Hello hello = this.testClient.getHelloUsingPropertyPlaceHolder();
Hello hello = testClient.getHelloUsingPropertyPlaceHolder();
assertThat(hello).as("hello was null").isNotNull();
assertThat(hello).as("first hello didn't match").isEqualTo(new Hello(OI_TERRA_2));
}
@Test
public void testSimpleType() {
Hello hello = this.testClient.getHello();
Hello hello = testClient.getHello();
assertThat(hello).as("hello was null").isNotNull();
assertThat(hello).as("first hello didn't match")
.isEqualTo(new Hello(HELLO_WORLD_1));
@@ -202,20 +203,20 @@ public class FeignClientTests {
@Test
public void testOptional() {
Optional<Hello> hello = this.testClient.getOptionalHello();
Optional<Hello> hello = testClient.getOptionalHello();
assertThat(hello).isNotNull().isPresent().contains(new Hello(HELLO_WORLD_1));
}
@Test
public void testGenericType() {
List<Hello> hellos = this.testClient.getHellos();
List<Hello> hellos = testClient.getHellos();
assertThat(hellos).as("hellos was null").isNotNull();
assertThat(getHelloList()).as("hellos didn't match").isEqualTo(hellos);
}
@Test
public void testRequestInterceptors() {
List<String> headers = this.testClient.getHelloHeaders();
List<String> headers = testClient.getHelloHeaders();
assertThat(headers).as("headers was null").isNotNull();
assertThat(headers.contains("myheader1value"))
.as("headers didn't contain myheader1value").isTrue();
@@ -225,24 +226,23 @@ public class FeignClientTests {
@Test
public void testHeaderPlaceholders() {
String header = this.testClient.getHelloHeadersPlaceholders();
String header = testClient.getHelloHeadersPlaceholders();
assertThat(header).as("header was null").isNotNull();
assertThat(header).as("header was wrong").isEqualTo("myPlaceholderHeaderValue");
}
@Test
public void testFeignClientType() throws IllegalAccessException {
assertThat(this.feignClient).isInstanceOf(LoadBalancerFeignClient.class);
LoadBalancerFeignClient client = (LoadBalancerFeignClient) this.feignClient;
public void testFeignClientType() {
assertThat(feignClient).isInstanceOf(LoadBalancerFeignClient.class);
LoadBalancerFeignClient client = (LoadBalancerFeignClient) feignClient;
Client delegate = client.getDelegate();
assertThat(delegate).isInstanceOf(Client.Default.class);
}
@Test
public void testServiceId() {
assertThat(this.testClientServiceId).as("testClientServiceId was null")
.isNotNull();
final Hello hello = this.testClientServiceId.getHello();
assertThat(testClientServiceId).as("testClientServiceId was null").isNotNull();
final Hello hello = testClientServiceId.getHello();
assertThat(hello).as("The hello response was null").isNotNull();
assertThat(hello).as("first hello didn't match")
.isEqualTo(new Hello(HELLO_WORLD_1));
@@ -251,7 +251,7 @@ public class FeignClientTests {
@Test
public void testParams() {
List<String> list = Arrays.asList("a", "1", "test");
List<String> params = this.testClient.getParams(list);
List<String> params = testClient.getParams(list);
assertThat(params).as("params was null").isNotNull();
assertThat(params.size()).as("params size was wrong").isEqualTo(list.size());
}
@@ -260,14 +260,14 @@ public class FeignClientTests {
public void testFormattedParams() {
List<LocalDate> list = Arrays.asList(LocalDate.of(2001, 1, 1),
LocalDate.of(2018, 6, 10));
List<LocalDate> params = this.testClient.getFormattedParams(list);
List<LocalDate> params = testClient.getFormattedParams(list);
assertThat(params).as("params was null").isNotNull();
assertThat(params).as("params not converted correctly").isEqualTo(list);
}
@Test
public void testHystrixCommand() throws NoSuchMethodException {
HystrixCommand<List<Hello>> command = this.testClient.getHellosHystrix();
HystrixCommand<List<Hello>> command = testClient.getHellosHystrix();
assertThat(command).as("command was null").isNotNull();
assertThat(command.getCommandGroup().name()).as(
"Hystrix command group name should match the name of the feign client")
@@ -284,7 +284,7 @@ public class FeignClientTests {
@Test
public void testSingle() {
Single<Hello> single = this.testClient.getHelloSingle();
Single<Hello> single = testClient.getHelloSingle();
assertThat(single).as("single was null").isNotNull();
Hello hello = single.toBlocking().value();
assertThat(hello).as("hello was null").isNotNull();
@@ -294,7 +294,7 @@ public class FeignClientTests {
@Test
public void testNoContentResponse() {
ResponseEntity<Void> response = this.testClient.noContent();
ResponseEntity<Void> response = testClient.noContent();
assertThat(response).as("response was null").isNotNull();
assertThat(response.getStatusCode()).as("status code was wrong")
.isEqualTo(HttpStatus.NO_CONTENT);
@@ -302,7 +302,7 @@ public class FeignClientTests {
@Test
public void testHeadResponse() {
ResponseEntity<Void> response = this.testClient.head();
ResponseEntity<Void> response = testClient.head();
assertThat(response).as("response was null").isNotNull();
assertThat(response.getStatusCode()).as("status code was wrong")
.isEqualTo(HttpStatus.OK);
@@ -310,7 +310,7 @@ public class FeignClientTests {
@Test
public void testHttpEntity() {
HttpEntity<Hello> entity = this.testClient.getHelloEntity();
HttpEntity<Hello> entity = testClient.getHelloEntity();
assertThat(entity).as("entity was null").isNotNull();
Hello hello = entity.getBody();
assertThat(hello).as("hello was null").isNotNull();
@@ -320,7 +320,7 @@ public class FeignClientTests {
@Test
public void testMoreComplexHeader() {
String response = this.testClient.moreComplexContentType("{\"value\":\"OK\"}");
String response = testClient.moreComplexContentType("{\"value\":\"OK\"}");
assertThat(response).as("response was null").isNotNull();
assertThat(response).as("didn't respond with {\"value\":\"OK\"}")
.isEqualTo("{\"value\":\"OK\"}");
@@ -328,7 +328,7 @@ public class FeignClientTests {
@Test
public void testDecodeNotFound() {
ResponseEntity<String> response = this.decodingTestClient.notFound();
ResponseEntity<String> response = decodingTestClient.notFound();
assertThat(response).as("response was null").isNotNull();
assertThat(response.getStatusCode()).as("status code was wrong")
.isEqualTo(HttpStatus.NOT_FOUND);
@@ -337,35 +337,35 @@ public class FeignClientTests {
@Test
public void testOptionalNotFound() {
Optional<String> s = this.decodingTestClient.optional();
Optional<String> s = decodingTestClient.optional();
assertThat(s).isNotPresent();
}
@Test
public void testConvertingExpander() {
assertThat(this.testClient.getToString(Arg.A)).isEqualTo(Arg.A.toString());
assertThat(this.testClient.getToString(Arg.B)).isEqualTo(Arg.B.toString());
assertThat(testClient.getToString(Arg.A)).isEqualTo(Arg.A.toString());
assertThat(testClient.getToString(Arg.B)).isEqualTo(Arg.B.toString());
assertThat(this.testClient.getToString(new OtherArg("foo"))).isEqualTo("bar");
assertThat(testClient.getToString(new OtherArg("foo"))).isEqualTo("bar");
List<OtherArg> args = new ArrayList<>();
args.add(new OtherArg("foo"));
args.add(new OtherArg("goo"));
List<String> expectedResult = new ArrayList<>();
expectedResult.add("bar");
expectedResult.add("goo");
assertThat(this.testClient.getToString(args)).isEqualTo(expectedResult);
assertThat(testClient.getToString(args)).isEqualTo(expectedResult);
}
@Test
public void testHystrixFallbackWorks() {
Hello hello = this.hystrixClient.fail();
Hello hello = hystrixClient.fail();
assertThat(hello).as("hello was null").isNotNull();
assertThat(hello.getMessage()).as("message was wrong").isEqualTo("fallback");
}
@Test
public void testHystrixFallbackSingle() {
Single<Hello> single = this.hystrixClient.failSingle();
Single<Hello> single = hystrixClient.failSingle();
assertThat(single).as("single was null").isNotNull();
Hello hello = single.toBlocking().value();
assertThat(hello).as("hello was null").isNotNull();
@@ -375,7 +375,7 @@ public class FeignClientTests {
@Test
public void testHystrixFallbackCommand() {
HystrixCommand<Hello> command = this.hystrixClient.failCommand();
HystrixCommand<Hello> command = hystrixClient.failCommand();
assertThat(command).as("command was null").isNotNull();
Hello hello = command.execute();
assertThat(hello).as("hello was null").isNotNull();
@@ -385,7 +385,7 @@ public class FeignClientTests {
@Test
public void testHystrixFallbackObservable() {
Observable<Hello> observable = this.hystrixClient.failObservable();
Observable<Hello> observable = hystrixClient.failObservable();
assertThat(observable).as("observable was null").isNotNull();
Hello hello = observable.toBlocking().first();
assertThat(hello).as("hello was null").isNotNull();
@@ -395,7 +395,7 @@ public class FeignClientTests {
@Test
public void testHystrixFallbackFuture() throws Exception {
Future<Hello> future = this.hystrixClient.failFuture();
Future<Hello> future = hystrixClient.failFuture();
assertThat(future).as("future was null").isNotNull();
Hello hello = future.get(1, TimeUnit.SECONDS);
assertThat(hello).as("hello was null").isNotNull();
@@ -405,7 +405,7 @@ public class FeignClientTests {
@Test
public void testHystrixClientWithFallBackFactory() throws Exception {
Hello hello = this.hystrixClientWithFallBackFactory.fail();
Hello hello = hystrixClientWithFallBackFactory.fail();
assertThat(hello).as("hello was null").isNotNull();
assertThat(hello.getMessage()).as("hello#message was null").isNotNull();
assertThat(hello.getMessage().contains("500")).as(
@@ -415,22 +415,29 @@ public class FeignClientTests {
@Test(expected = HystrixRuntimeException.class)
public void testInvalidTypeHystrixFallbackFactory() throws Exception {
this.invalidTypeHystrixClientWithFallBackFactory.fail();
invalidTypeHystrixClientWithFallBackFactory.fail();
}
@Test(expected = HystrixRuntimeException.class)
public void testNullHystrixFallbackFactory() throws Exception {
this.nullHystrixClientWithFallBackFactory.fail();
nullHystrixClientWithFallBackFactory.fail();
}
@Test
public void testFormURLEncoded() {
Hello hello = new Hello(HELLO_WORLD_1);
Hello response = testClient.postFormUrlEncoded(hello);
assertThat(response).isEqualTo(hello);
}
@Test
public void namedFeignClientWorks() {
assertThat(this.namedHystrixClient).as("namedHystrixClient was null").isNotNull();
assertThat(namedHystrixClient).as("namedHystrixClient was null").isNotNull();
}
@Test
public void testHystrixSetterFactory() {
HystrixCommand<List<Hello>> command = this.hystrixSetterFactoryClient
HystrixCommand<List<Hello>> command = hystrixSetterFactoryClient
.getHellosHystrix();
assertThat(command).as("command was null").isNotNull();
String setterPrefix = TestHystrixSetterFactoryClientConfig.SETTER_PREFIX;
@@ -449,13 +456,13 @@ public class FeignClientTests {
@Test
public void testSingleRequestPart() {
String response = this.multipartClient.singlePart("abc");
String response = multipartClient.singlePart("abc");
assertThat(response).isEqualTo("abc");
}
@Test
public void testSinglePojoRequestPart() {
String response = this.multipartClient.singlePojoPart(new Hello(HELLO_WORLD_1));
String response = multipartClient.singlePojoPart(new Hello(HELLO_WORLD_1));
assertThat(response).isEqualTo(HELLO_WORLD_1);
}
@@ -463,7 +470,7 @@ public class FeignClientTests {
public void testMultipleRequestParts() {
MockMultipartFile file = new MockMultipartFile("file", "hello.bin", null,
"hello".getBytes());
String response = this.multipartClient.multipart("abc", "123", file);
String response = multipartClient.multipart("abc", "123", file);
assertThat(response).isEqualTo("abc123hello.bin");
}
@@ -473,8 +480,7 @@ public class FeignClientTests {
Hello pojo2 = new Hello(OI_TERRA_2);
MockMultipartFile file = new MockMultipartFile("file", "hello.bin", null,
"hello".getBytes());
String response = this.multipartClient.multipartPojo("abc", "123", pojo1, pojo2,
file);
String response = multipartClient.multipartPojo("abc", "123", pojo1, pojo2, file);
assertThat(response).isEqualTo("abc123hello world 1oi terra 2hello.bin");
}
@@ -483,10 +489,10 @@ public class FeignClientTests {
List<MultipartFile> multipartFiles = Arrays.asList(
new MockMultipartFile("file1", "hello1.bin", null, "hello".getBytes()),
new MockMultipartFile("file2", "hello2.bin", null, "hello".getBytes()));
String partNames = this.multipartClient
String partNames = multipartClient
.requestPartListOfMultipartFilesReturnsPartNames(multipartFiles);
assertThat(partNames).isEqualTo("files,files");
String fileNames = this.multipartClient
String fileNames = multipartClient
.requestPartListOfMultipartFilesReturnsFileNames(multipartFiles);
assertThat(fileNames).contains("hello1.bin", "hello2.bin");
}
@@ -499,9 +505,8 @@ public class FeignClientTests {
"hello".getBytes());
MockMultipartFile file2 = new MockMultipartFile("file2", "hello2.bin", null,
"hello".getBytes());
String response = this.multipartClient
.requestPartListOfPojosAndListOfMultipartFiles(
Arrays.asList(pojo1, pojo2), Arrays.asList(file1, file2));
String response = multipartClient.requestPartListOfPojosAndListOfMultipartFiles(
Arrays.asList(pojo1, pojo2), Arrays.asList(file1, file2));
assertThat(response).isEqualTo("hello world 1oi terra 2hello1.binhello2.bin");
}
@@ -510,7 +515,7 @@ public class FeignClientTests {
String partName = UUID.randomUUID().toString();
MockMultipartFile file1 = new MockMultipartFile(partName, "hello1.bin", null,
"hello".getBytes());
String response = this.multipartClient.requestBodySingleMultipartFile(file1);
String response = multipartClient.requestBodySingleMultipartFile(file1);
assertThat(response).isEqualTo(partName);
}
@@ -520,7 +525,7 @@ public class FeignClientTests {
"hello".getBytes());
MockMultipartFile file2 = new MockMultipartFile("file2", "hello2.bin", null,
"hello".getBytes());
String response = this.multipartClient
String response = multipartClient
.requestBodyListOfMultipartFiles(Arrays.asList(file1, file2));
assertThat(response).contains("file1", "file2");
}
@@ -535,7 +540,7 @@ public class FeignClientTests {
form.put("file1", file1);
form.put("file2", file2);
form.put("hello", "world");
String response = this.multipartClient.requestBodyMap(form);
String response = multipartClient.requestBodyMap(form);
assertThat(response).contains("file1", "file2", "hello");
}
@@ -544,7 +549,7 @@ public class FeignClientTests {
MockMultipartFile file = new MockMultipartFile("file1", "hello1.bin", null,
"hello".getBytes());
expected.expectCause(instanceOf(EncodeException.class));
this.multipartClient.invalid(file);
multipartClient.invalid(file);
}
protected enum Arg {
@@ -615,6 +620,10 @@ public class FeignClientTests {
@RequestMapping(method = RequestMethod.GET, path = "/tostring")
String getToString(@RequestParam("arg") Arg arg);
@PostMapping(path = "/form-urlencoded",
consumes = APPLICATION_FORM_URLENCODED_VALUE)
Hello postFormUrlEncoded(Hello hello);
@RequestMapping(method = RequestMethod.GET, path = "/tostring2")
String getToString(@RequestParam("arg") OtherArg arg);
@@ -776,7 +785,7 @@ public class FeignClientTests {
@Override
public String toString() {
return this.value;
return value;
}
}
@@ -810,13 +819,9 @@ public class FeignClientTests {
@Override
public HystrixClientWithFallBackFactory create(final Throwable cause) {
return new HystrixClientWithFallBackFactory() {
@Override
public Hello fail() {
assertThat(cause).isNotNull().as("Cause was null");
return new Hello(
"Hello from the fallback side: " + cause.getMessage());
}
return () -> {
assertThat(cause).isNotNull().as("Cause was null");
return new Hello("Hello from the fallback side: " + cause.getMessage());
};
}
@@ -876,18 +881,15 @@ public class FeignClientTests {
@Bean
public SetterFactory commandKeyIsRequestLineSetterFactory() {
return new SetterFactory() {
@Override
public HystrixCommand.Setter create(Target<?> target, Method method) {
String groupKey = SETTER_PREFIX + target.name();
RequestMapping requestMapping = method
.getAnnotation(RequestMapping.class);
String commandKey = SETTER_PREFIX + requestMapping.method()[0] + " "
+ requestMapping.path()[0];
return HystrixCommand.Setter
.withGroupKey(HystrixCommandGroupKey.Factory.asKey(groupKey))
.andCommandKey(HystrixCommandKey.Factory.asKey(commandKey));
}
return (target, method) -> {
String groupKey = SETTER_PREFIX + target.name();
RequestMapping requestMapping = method
.getAnnotation(RequestMapping.class);
String commandKey = SETTER_PREFIX + requestMapping.method()[0] + " "
+ requestMapping.path()[0];
return HystrixCommand.Setter
.withGroupKey(HystrixCommandGroupKey.Factory.asKey(groupKey))
.andCommandKey(HystrixCommandKey.Factory.asKey(commandKey));
};
}
@@ -1154,6 +1156,12 @@ public class FeignClientTests {
return result.toString();
}
@PostMapping(path = "/form-urlencoded",
consumes = APPLICATION_FORM_URLENCODED_VALUE)
Hello postFormUrlEncoded(Hello hello) {
return hello;
}
}
public static class Hello {
@@ -1168,7 +1176,7 @@ public class FeignClientTests {
}
public String getMessage() {
return this.message;
return message;
}
public void setMessage(String message) {
@@ -1184,12 +1192,12 @@ public class FeignClientTests {
return false;
}
Hello that = (Hello) o;
return Objects.equals(this.message, that.message);
return Objects.equals(message, that.message);
}
@Override
public int hashCode() {
return Objects.hash(this.message);
return Objects.hash(message);
}
}
@@ -1213,7 +1221,7 @@ public class FeignClientTests {
@Bean
public ServerList<Server> ribbonServerList() {
return new StaticServerList<>(new Server("localhost", this.port));
return new StaticServerList<>(new Server("localhost", port));
}
}
+2 -2
View File
@@ -6,11 +6,11 @@
<parent>
<artifactId>spring-cloud-dependencies-parent</artifactId>
<groupId>org.springframework.cloud</groupId>
<version>2.3.5.BUILD-SNAPSHOT</version>
<version>2.3.6.BUILD-SNAPSHOT</version>
<relativePath/>
</parent>
<artifactId>spring-cloud-openfeign-dependencies</artifactId>
<version>2.2.9.BUILD-SNAPSHOT</version>
<version>2.2.11.BUILD-SNAPSHOT</version>
<packaging>pom</packaging>
<name>spring-cloud-openfeign-dependencies</name>
<description>Spring Cloud OpenFeign Dependencies</description>
+1 -1
View File
@@ -5,7 +5,7 @@
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-openfeign</artifactId>
<version>2.2.9.BUILD-SNAPSHOT</version>
<version>2.2.11.BUILD-SNAPSHOT</version>
<relativePath>..</relativePath>
</parent>
<artifactId>spring-cloud-starter-openfeign</artifactId>