Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bc7d39fa04 | ||
|
|
4cba1de153 | ||
|
|
1bed516e03 | ||
|
|
a142f6f2c0 | ||
|
|
434ce22328 | ||
|
|
9f160e7688 | ||
|
|
a3f270d3b4 | ||
|
|
1ff643f4c1 | ||
|
|
6704982bcd | ||
|
|
909da0afdf | ||
|
|
4cd0c363be | ||
|
|
86a2f28a9e | ||
|
|
573d933b3d | ||
|
|
d76869f214 | ||
|
|
12efc629a3 | ||
|
|
965c168c2c | ||
|
|
f2386e365d | ||
|
|
765431dce1 | ||
|
|
75fc0f6b2e | ||
|
|
f662c0547d | ||
|
|
aa4b8a280d | ||
|
|
973c048be5 |
+3
-1
@@ -6,7 +6,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-openfeign</artifactId>
|
||||
<version>3.1.1</version>
|
||||
<version>3.1.3</version>
|
||||
</parent>
|
||||
<artifactId>spring-cloud-openfeign-docs</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
@@ -17,6 +17,8 @@
|
||||
<main.basedir>${basedir}/..</main.basedir>
|
||||
<configprops.inclusionPattern>feign.*</configprops.inclusionPattern>
|
||||
<upload-docs-zip.phase>deploy</upload-docs-zip.phase>
|
||||
<!-- Don't upload docs jar to central / repo.spring.io -->
|
||||
<maven-deploy-plugin-default.phase>none</maven-deploy-plugin-default.phase>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|Name | Default | Description
|
||||
|
||||
|feign.autoconfiguration.jackson.enabled | `false` | If true, PageJacksonModule and SortJacksonModule bean will be provided for Jackson page decoding.
|
||||
|feign.circuitbreaker.alphanumeric-ids.enabled | `false` | If true, Circuit Breaker ids will only contain alphanumeric characters to allow for configuration via configuration properties.
|
||||
|feign.circuitbreaker.enabled | `false` | If true, an OpenFeign client will be wrapped with a Spring Cloud CircuitBreaker circuit breaker.
|
||||
|feign.circuitbreaker.group.enabled | `false` | If true, an OpenFeign client will be wrapped with a Spring Cloud CircuitBreaker circuit breaker with with group.
|
||||
|feign.client.config | |
|
||||
|
||||
@@ -372,6 +372,47 @@ public class FooConfiguration {
|
||||
|
||||
To enable Spring Cloud CircuitBreaker group set the `feign.circuitbreaker.group.enabled` property to `true` (by default `false`).
|
||||
|
||||
[[spring-clou-feign-circuitbreaker-configurationproperties]]
|
||||
=== Configuring CircuitBreakers With Configuration Properties
|
||||
|
||||
You can configure CircuitBreakers via configuration properties. To do set
|
||||
`feign.circuitbreaker.alphanumeric-ids.enabled` to `true`. Since
|
||||
you cannot use characters like `#`, `(`, `)` `,` in configuration property names we need to
|
||||
change the naming convention for the ids of the circuit breakers generated by OpenFeign. The above
|
||||
property will do this for you.
|
||||
|
||||
For example, if you had this Feign client
|
||||
|
||||
[source,java,indent=0]
|
||||
----
|
||||
@FeignClienturl = "http://localhost:8080")
|
||||
public interface DemoClient {
|
||||
|
||||
@GetMapping("demo")
|
||||
String getDemo();
|
||||
}
|
||||
----
|
||||
|
||||
You could configure it using configuration properties by doing the following
|
||||
|
||||
[source,yaml,indent=0]
|
||||
----
|
||||
feign:
|
||||
circuitbreaker:
|
||||
enabled: true
|
||||
alphanumeric-ids:
|
||||
enabled: true
|
||||
resilience4j:
|
||||
circuitbreaker:
|
||||
instances:
|
||||
DemoClientgetDemo:
|
||||
minimumNumberOfCalls: 69
|
||||
timelimiter:
|
||||
instances:
|
||||
DemoClientgetDemo:
|
||||
timeoutDuration: 10s
|
||||
----
|
||||
|
||||
|
||||
[[spring-cloud-feign-circuitbreaker-fallback]]
|
||||
=== Feign Spring Cloud CircuitBreaker Fallbacks
|
||||
|
||||
@@ -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>3.1.1</version>
|
||||
<version>3.1.3</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>3.1.1</version>
|
||||
<version>3.1.3</version>
|
||||
<relativePath/>
|
||||
</parent>
|
||||
<scm>
|
||||
@@ -26,7 +26,7 @@
|
||||
<properties>
|
||||
<main.basedir>${basedir}</main.basedir>
|
||||
<jackson.version>2.11.3</jackson.version>
|
||||
<spring-cloud-commons.version>3.1.1</spring-cloud-commons.version>
|
||||
<spring-cloud-commons.version>3.1.3</spring-cloud-commons.version>
|
||||
|
||||
<!-- Plugin versions -->
|
||||
<maven-eclipse-plugin.version>2.10</maven-eclipse-plugin.version>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-openfeign</artifactId>
|
||||
<version>3.1.1</version>
|
||||
<version>3.1.3</version>
|
||||
<relativePath>..</relativePath> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<artifactId>spring-cloud-openfeign-core</artifactId>
|
||||
|
||||
+18
@@ -166,10 +166,19 @@ public class FeignAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(CircuitBreakerNameResolver.class)
|
||||
@ConditionalOnProperty(value = "feign.circuitbreaker.alphanumeric-ids.enabled", havingValue = "false",
|
||||
matchIfMissing = true)
|
||||
public CircuitBreakerNameResolver circuitBreakerNameResolver() {
|
||||
return new DefaultCircuitBreakerNameResolver();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(CircuitBreakerNameResolver.class)
|
||||
@ConditionalOnProperty(value = "feign.circuitbreaker.alphanumeric-ids.enabled", havingValue = "true")
|
||||
public CircuitBreakerNameResolver alphanumericCircuitBreakerNameResolver() {
|
||||
return new AlphanumericCircuitBreakerNameResolver();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
@ConditionalOnBean(CircuitBreakerFactory.class)
|
||||
@@ -189,6 +198,15 @@ public class FeignAutoConfiguration {
|
||||
|
||||
}
|
||||
|
||||
static class AlphanumericCircuitBreakerNameResolver extends DefaultCircuitBreakerNameResolver {
|
||||
|
||||
@Override
|
||||
public String resolveCircuitBreakerName(String feignClientName, Target<?> target, Method method) {
|
||||
return super.resolveCircuitBreakerName(feignClientName, target, method).replaceAll("[^a-zA-Z0-9]", "");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// the following configuration is for alternate feign clients if
|
||||
|
||||
+18
-2
@@ -17,6 +17,7 @@
|
||||
package org.springframework.cloud.openfeign;
|
||||
|
||||
import java.lang.reflect.InvocationHandler;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.util.LinkedHashMap;
|
||||
@@ -29,6 +30,7 @@ import feign.Target;
|
||||
|
||||
import org.springframework.cloud.client.circuitbreaker.CircuitBreaker;
|
||||
import org.springframework.cloud.client.circuitbreaker.CircuitBreakerFactory;
|
||||
import org.springframework.cloud.client.circuitbreaker.NoFallbackAvailableException;
|
||||
import org.springframework.web.context.request.RequestAttributes;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
|
||||
@@ -95,15 +97,29 @@ class FeignCircuitBreakerInvocationHandler implements InvocationHandler {
|
||||
try {
|
||||
return this.fallbackMethodMap.get(method).invoke(fallback, args);
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new IllegalStateException(e);
|
||||
catch (Exception exception) {
|
||||
unwrapAndRethrow(exception);
|
||||
}
|
||||
return null;
|
||||
};
|
||||
return circuitBreaker.run(supplier, fallbackFunction);
|
||||
}
|
||||
return circuitBreaker.run(supplier);
|
||||
}
|
||||
|
||||
private void unwrapAndRethrow(Exception exception) {
|
||||
if (exception instanceof InvocationTargetException || exception instanceof NoFallbackAvailableException) {
|
||||
Throwable underlyingException = exception.getCause();
|
||||
if (underlyingException instanceof RuntimeException) {
|
||||
throw (RuntimeException) underlyingException;
|
||||
}
|
||||
if (underlyingException != null) {
|
||||
throw new IllegalStateException(underlyingException);
|
||||
}
|
||||
throw new IllegalStateException(exception);
|
||||
}
|
||||
}
|
||||
|
||||
private Supplier<Object> asSupplier(final Method method, final Object[] args) {
|
||||
final RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
|
||||
return () -> {
|
||||
|
||||
+2
-1
@@ -110,8 +110,9 @@ public class FeignBlockingLoadBalancerClient implements Client {
|
||||
}
|
||||
String reconstructedUrl = loadBalancerClient.reconstructURI(instance, originalUri).toString();
|
||||
Request newRequest = buildRequest(request, reconstructedUrl);
|
||||
LoadBalancerProperties loadBalancerProperties = loadBalancerClientFactory.getProperties(serviceId);
|
||||
return executeWithLoadBalancerLifecycleProcessing(delegate, options, newRequest, lbRequest, lbResponse,
|
||||
supportedLifecycleProcessors);
|
||||
supportedLifecycleProcessors, loadBalancerProperties.isUseRawStatusCodeInResponseData());
|
||||
}
|
||||
|
||||
protected Request buildRequest(Request request, String reconstructedUrl) {
|
||||
|
||||
+9
-5
@@ -50,14 +50,15 @@ final class LoadBalancerUtils {
|
||||
static Response executeWithLoadBalancerLifecycleProcessing(Client feignClient, Request.Options options,
|
||||
Request feignRequest, org.springframework.cloud.client.loadbalancer.Request lbRequest,
|
||||
org.springframework.cloud.client.loadbalancer.Response<ServiceInstance> lbResponse,
|
||||
Set<LoadBalancerLifecycle> supportedLifecycleProcessors, boolean loadBalanced) throws IOException {
|
||||
Set<LoadBalancerLifecycle> supportedLifecycleProcessors, boolean loadBalanced, boolean useRawStatusCodes)
|
||||
throws IOException {
|
||||
supportedLifecycleProcessors.forEach(lifecycle -> lifecycle.onStartRequest(lbRequest, lbResponse));
|
||||
try {
|
||||
Response response = feignClient.execute(feignRequest, options);
|
||||
if (loadBalanced) {
|
||||
supportedLifecycleProcessors.forEach(
|
||||
lifecycle -> lifecycle.onComplete(new CompletionContext<>(CompletionContext.Status.SUCCESS,
|
||||
lbRequest, lbResponse, buildResponseData(response))));
|
||||
lbRequest, lbResponse, buildResponseData(response, useRawStatusCodes))));
|
||||
}
|
||||
return response;
|
||||
}
|
||||
@@ -70,9 +71,12 @@ final class LoadBalancerUtils {
|
||||
}
|
||||
}
|
||||
|
||||
static ResponseData buildResponseData(Response response) {
|
||||
static ResponseData buildResponseData(Response response, boolean useRawStatusCodes) {
|
||||
HttpHeaders responseHeaders = new HttpHeaders();
|
||||
response.headers().forEach((key, value) -> responseHeaders.put(key, new ArrayList<>(value)));
|
||||
if (useRawStatusCodes) {
|
||||
return new ResponseData(responseHeaders, null, buildRequestData(response.request()), response.status());
|
||||
}
|
||||
return new ResponseData(HttpStatus.resolve(response.status()), responseHeaders, null,
|
||||
buildRequestData(response.request()));
|
||||
}
|
||||
@@ -87,9 +91,9 @@ final class LoadBalancerUtils {
|
||||
static Response executeWithLoadBalancerLifecycleProcessing(Client feignClient, Request.Options options,
|
||||
Request feignRequest, org.springframework.cloud.client.loadbalancer.Request lbRequest,
|
||||
org.springframework.cloud.client.loadbalancer.Response<ServiceInstance> lbResponse,
|
||||
Set<LoadBalancerLifecycle> supportedLifecycleProcessors) throws IOException {
|
||||
Set<LoadBalancerLifecycle> supportedLifecycleProcessors, boolean useRawStatusCodes) throws IOException {
|
||||
return executeWithLoadBalancerLifecycleProcessing(feignClient, options, feignRequest, lbRequest, lbResponse,
|
||||
supportedLifecycleProcessors, true);
|
||||
supportedLifecycleProcessors, true, useRawStatusCodes);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+3
-2
@@ -163,9 +163,10 @@ public class RetryableFeignBlockingLoadBalancerClient implements Client {
|
||||
}
|
||||
org.springframework.cloud.client.loadbalancer.Response<ServiceInstance> lbResponse = new DefaultResponse(
|
||||
retrievedServiceInstance);
|
||||
LoadBalancerProperties loadBalancerProperties = loadBalancerClientFactory.getProperties(serviceId);
|
||||
Response response = LoadBalancerUtils.executeWithLoadBalancerLifecycleProcessing(delegate, options,
|
||||
feignRequest, lbRequest, lbResponse, supportedLifecycleProcessors,
|
||||
retrievedServiceInstance != null);
|
||||
feignRequest, lbRequest, lbResponse, supportedLifecycleProcessors, retrievedServiceInstance != null,
|
||||
loadBalancerProperties.isUseRawStatusCodeInResponseData());
|
||||
int responseStatus = response.status();
|
||||
if (retryPolicy != null && retryPolicy.retryableStatusCode(responseStatus)) {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
|
||||
+15
@@ -196,6 +196,21 @@ public class PageJacksonModule extends Module {
|
||||
return delegate.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return delegate.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return delegate.equals(obj);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return delegate.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+6
@@ -14,6 +14,12 @@
|
||||
"description": "If true, an OpenFeign client will be wrapped with a Spring Cloud CircuitBreaker circuit breaker.",
|
||||
"defaultValue": "false"
|
||||
},
|
||||
{
|
||||
"name": "feign.circuitbreaker.alphanumeric-ids.enabled",
|
||||
"type": "java.lang.Boolean",
|
||||
"description": "If true, Circuit Breaker ids will only contain alphanumeric characters to allow for configuration via configuration properties.",
|
||||
"defaultValue": "false"
|
||||
},
|
||||
{
|
||||
"name": "feign.circuitbreaker.group.enabled",
|
||||
"type": "java.lang.Boolean",
|
||||
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright 2013-2022 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.circuitbreaker;
|
||||
|
||||
import feign.Target;
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.cloud.openfeign.CircuitBreakerNameResolver;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
public class CircuitBreakerAutoConfigurationTests {
|
||||
|
||||
@SpringBootTest(classes = CircuitBreakerTests.Application.class,
|
||||
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
|
||||
value = { "spring.application.name=springcircuittest", "spring.jmx.enabled=false",
|
||||
"feign.circuitbreaker.enabled=true" })
|
||||
@Nested
|
||||
class DefaultNamingStrategy {
|
||||
|
||||
@Autowired
|
||||
CircuitBreakerNameResolver nameResolver;
|
||||
|
||||
@Test
|
||||
public void assertDefaultNamingStrategy() throws Exception {
|
||||
Target target = mock(Target.class);
|
||||
when(target.type()).thenReturn(CircuitBreakerTests.TestClientWithFactory.class);
|
||||
assertThat(nameResolver.resolveCircuitBreakerName("foo", target,
|
||||
CircuitBreakerTests.TestClientWithFactory.class.getMethod("getHello")))
|
||||
.isEqualTo("TestClientWithFactory#getHello()");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@SpringBootTest(classes = CircuitBreakerTests.Application.class,
|
||||
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
|
||||
value = { "spring.application.name=springcircuittest", "spring.jmx.enabled=false",
|
||||
"feign.circuitbreaker.enabled=true", "feign.circuitbreaker.alphanumeric-ids.enabled=true" })
|
||||
@Nested
|
||||
class AlphanumericNamingStrategy {
|
||||
|
||||
@Autowired
|
||||
CircuitBreakerNameResolver nameResolver;
|
||||
|
||||
@Test
|
||||
public void assertAlphanumericNamingStrategy() throws Exception {
|
||||
Target target = mock(Target.class);
|
||||
when(target.type()).thenReturn(CircuitBreakerTests.TestClientWithFactory.class);
|
||||
assertThat(nameResolver.resolveCircuitBreakerName("foo", target,
|
||||
CircuitBreakerTests.TestClientWithFactory.class.getMethod("getHello")))
|
||||
.isEqualTo("TestClientWithFactorygetHello");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+53
-1
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.springframework.cloud.openfeign.circuitbreaker;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
@@ -47,6 +48,7 @@ import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
@@ -62,6 +64,9 @@ class CircuitBreakerTests {
|
||||
@Autowired
|
||||
TestClient testClient;
|
||||
|
||||
@Autowired
|
||||
ExceptionClient exceptionClient;
|
||||
|
||||
@Autowired
|
||||
TestClientWithFactory testClientWithFactory;
|
||||
|
||||
@@ -108,6 +113,17 @@ class CircuitBreakerTests {
|
||||
assertThat(testClientWithFactory.getException()).isEqualTo("Fixed response");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRuntimeExceptionUnwrapped() {
|
||||
assertThatExceptionOfType(UnsupportedOperationException.class)
|
||||
.isThrownBy(() -> exceptionClient.getRuntimeException());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCheckedExceptionWrapped() {
|
||||
assertThatExceptionOfType(IllegalStateException.class).isThrownBy(() -> exceptionClient.getCheckedException());
|
||||
}
|
||||
|
||||
@FeignClient(name = "test", url = "http://localhost:${server.port}/", fallback = Fallback.class)
|
||||
protected interface TestClient {
|
||||
|
||||
@@ -119,6 +135,18 @@ class CircuitBreakerTests {
|
||||
|
||||
}
|
||||
|
||||
@FeignClient(name = "exceptionClient", url = "http://localhost:${server.port}/",
|
||||
fallbackFactory = ExceptionThrowingFallbackFactory.class)
|
||||
protected interface ExceptionClient {
|
||||
|
||||
@GetMapping("/runtimeException")
|
||||
Hello getRuntimeException();
|
||||
|
||||
@GetMapping("/runtimeException")
|
||||
Hello getCheckedException() throws IOException;
|
||||
|
||||
}
|
||||
|
||||
@Component
|
||||
static class Fallback implements TestClient {
|
||||
|
||||
@@ -156,6 +184,25 @@ class CircuitBreakerTests {
|
||||
|
||||
}
|
||||
|
||||
static class ExceptionThrowingFallbackFactory implements FallbackFactory<ExceptionClient> {
|
||||
|
||||
@Override
|
||||
public ExceptionClient create(Throwable cause) {
|
||||
return new ExceptionClient() {
|
||||
@Override
|
||||
public Hello getRuntimeException() {
|
||||
throw new UnsupportedOperationException("Not implemented!");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Hello getCheckedException() throws IOException {
|
||||
throw new IOException();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class FallbackWithFactory implements TestClientWithFactory {
|
||||
|
||||
@Override
|
||||
@@ -173,7 +220,7 @@ class CircuitBreakerTests {
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@EnableAutoConfiguration
|
||||
@RestController
|
||||
@EnableFeignClients(clients = { TestClient.class, TestClientWithFactory.class })
|
||||
@EnableFeignClients(clients = { TestClient.class, TestClientWithFactory.class, ExceptionClient.class })
|
||||
@Import(NoSecurityConfiguration.class)
|
||||
protected static class Application implements TestClient {
|
||||
|
||||
@@ -225,6 +272,11 @@ class CircuitBreakerTests {
|
||||
return new TestFallbackFactory();
|
||||
}
|
||||
|
||||
@Bean
|
||||
ExceptionThrowingFallbackFactory exceptionThrowingFallbackFactory() {
|
||||
return new ExceptionThrowingFallbackFactory();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+42
-1
@@ -78,7 +78,9 @@ import static org.junit.jupiter.api.Assumptions.assumeTrue;
|
||||
* @author Olga Maciaszek-Sharma
|
||||
* @author Szymon Linowski
|
||||
* @author Sam Kruglov
|
||||
*/
|
||||
* @author Bhavya Agrawal
|
||||
**/
|
||||
|
||||
class SpringMvcContractTests {
|
||||
|
||||
private static final Class<?> EXECUTABLE_TYPE;
|
||||
@@ -171,6 +173,39 @@ class SpringMvcContractTests {
|
||||
assertThat(data.indexToName().get(0).iterator().next()).isEqualTo("id");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testProcessAnnotations_SimpleNoPath() throws Exception {
|
||||
Method method = TestTemplate_Simple.class.getDeclaredMethod("getTest");
|
||||
MethodMetadata data = contract.parseAndValidateMetadata(method.getDeclaringClass(), method);
|
||||
|
||||
assertThat(data.template().url()).isEqualTo("/");
|
||||
assertThat(data.template().method()).isEqualTo("GET");
|
||||
assertThat(data.template().headers().get("Accept").iterator().next())
|
||||
.isEqualTo(MediaType.APPLICATION_JSON_VALUE);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testProcessAnnotations_SimplePathIsOnlyASlash() throws Exception {
|
||||
Method method = TestTemplate_Simple.class.getDeclaredMethod("getSlashPath", String.class);
|
||||
MethodMetadata data = contract.parseAndValidateMetadata(method.getDeclaringClass(), method);
|
||||
|
||||
assertThat(data.template().url()).isEqualTo("/?id=" + "{id}");
|
||||
assertThat(data.template().method()).isEqualTo("GET");
|
||||
assertThat(data.template().headers().get("Accept").iterator().next())
|
||||
.isEqualTo(MediaType.APPLICATION_JSON_VALUE);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testProcessAnnotations_MissingLeadingSlashInPath() throws Exception {
|
||||
Method method = TestTemplate_Simple.class.getDeclaredMethod("getTestNoLeadingSlash", String.class);
|
||||
MethodMetadata data = contract.parseAndValidateMetadata(method.getDeclaringClass(), method);
|
||||
|
||||
assertThat(data.template().url()).isEqualTo("/test?name=" + "{name}");
|
||||
assertThat(data.template().method()).isEqualTo("GET");
|
||||
assertThat(data.template().headers().get("Accept").iterator().next())
|
||||
.isEqualTo(MediaType.APPLICATION_JSON_VALUE);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testProcessAnnotations_SimpleGetMapping() throws Exception {
|
||||
Method method = TestTemplate_Simple.class.getDeclaredMethod("getMappingTest", String.class);
|
||||
@@ -616,6 +651,12 @@ class SpringMvcContractTests {
|
||||
@PostMapping(produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
TestObject postMappingTest(@RequestBody TestObject object);
|
||||
|
||||
@GetMapping(value = "/", produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
ResponseEntity<TestObject> getSlashPath(@RequestParam("id") String id);
|
||||
|
||||
@GetMapping(path = "test", produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
ResponseEntity<TestObject> getTestNoLeadingSlash(@RequestParam("name") String name);
|
||||
|
||||
}
|
||||
|
||||
@RequestMapping("/prepend/{classId}")
|
||||
|
||||
@@ -6,11 +6,11 @@
|
||||
<parent>
|
||||
<artifactId>spring-cloud-dependencies-parent</artifactId>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<version>3.1.1</version>
|
||||
<version>3.1.3</version>
|
||||
<relativePath/>
|
||||
</parent>
|
||||
<artifactId>spring-cloud-openfeign-dependencies</artifactId>
|
||||
<version>3.1.1</version>
|
||||
<version>3.1.3</version>
|
||||
<packaging>pom</packaging>
|
||||
<name>spring-cloud-openfeign-dependencies</name>
|
||||
<description>Spring Cloud OpenFeign Dependencies</description>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-openfeign</artifactId>
|
||||
<version>3.1.1</version>
|
||||
<version>3.1.3</version>
|
||||
<relativePath>..</relativePath>
|
||||
</parent>
|
||||
<artifactId>spring-cloud-starter-openfeign</artifactId>
|
||||
|
||||
Reference in New Issue
Block a user