Compare commits
22
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c2430c0e29 | ||
|
|
b66e08fda8 | ||
|
|
84819e1328 | ||
|
|
b6422db8e9 | ||
|
|
4bf010e810 | ||
|
|
bd671efed9 | ||
|
|
bfd473cb8f | ||
|
|
2f5d63a061 | ||
|
|
14929cbfee | ||
|
|
d3da7e16e8 | ||
|
|
4e89dd1523 | ||
|
|
63ba4d40ff | ||
|
|
06862ab090 | ||
|
|
72784920d8 | ||
|
|
bf13ae95c9 | ||
|
|
af06086c30 | ||
|
|
5497f5a37c | ||
|
|
12084de3a9 | ||
|
|
84cf97b391 | ||
|
|
3002643ee7 | ||
|
|
b1ec55cb01 | ||
|
|
f0e0e7ea64 |
@@ -20,4 +20,5 @@ _site/
|
||||
*.swo
|
||||
.vscode/
|
||||
.flattened-pom.xml
|
||||
.sdkmanrc
|
||||
|
||||
|
||||
+2
-1
@@ -4,6 +4,7 @@ Manual changes to this file will be lost when it is generated again.
|
||||
Edit the files in the src/main/asciidoc/ directory instead.
|
||||
////
|
||||
|
||||
|
||||
image:https://circleci.com/gh/spring-cloud/spring-cloud-openfeign.svg?style=svg["CircleCI", link="https://circleci.com/gh/spring-cloud/spring-cloud-openfeign"]
|
||||
|
||||
image:https://codecov.io/gh/spring-cloud/spring-cloud-openfeign/branch/master/graph/badge.svg["Codecov", link="https://codecov.io/gh/spring-cloud/spring-cloud-openfeign"]
|
||||
@@ -312,4 +313,4 @@ Go to `File` -> `Settings` -> `Other settings` -> `Checkstyle`. There click on t
|
||||
- `checkstyle.suppressions.file` - default suppressions. Please point it to the Spring Cloud Build's, `spring-cloud-build-tools/src/checkstyle/checkstyle-suppressions.xml` file either in your cloned repo or via the `https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/master/spring-cloud-build-tools/src/checkstyle/checkstyle-suppressions.xml` URL.
|
||||
- `checkstyle.additional.suppressions.file` - this variable corresponds to suppressions in your local project. E.g. you're working on `spring-cloud-contract`. Then point to the `project-root/src/checkstyle/checkstyle-suppressions.xml` folder. Example for `spring-cloud-contract` would be: `/home/username/spring-cloud-contract/src/checkstyle/checkstyle-suppressions.xml`.
|
||||
|
||||
IMPORTANT: Remember to set the `Scan Scope` to `All sources` since we apply checkstyle rules for production and test sources.
|
||||
IMPORTANT: Remember to set the `Scan Scope` to `All sources` since we apply checkstyle rules for production and test sources.
|
||||
|
||||
+5
-15
@@ -6,7 +6,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-openfeign</artifactId>
|
||||
<version>2.2.3.BUILD-SNAPSHOT</version>
|
||||
<version>2.2.5.RELEASE</version>
|
||||
</parent>
|
||||
<artifactId>spring-cloud-openfeign-docs</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
@@ -15,8 +15,8 @@
|
||||
<properties>
|
||||
<docs.main>spring-cloud-openfeign</docs.main>
|
||||
<main.basedir>${basedir}/..</main.basedir>
|
||||
<docs.whitelisted.branches>2.1.x,2.2.x</docs.whitelisted.branches>
|
||||
<configprops.inclusionPattern>feign.*</configprops.inclusionPattern>
|
||||
<upload-docs-zip.phase>deploy</upload-docs-zip.phase>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
@@ -24,16 +24,6 @@
|
||||
<artifactId>spring-cloud-starter-openfeign</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-deploy-plugin</artifactId>
|
||||
<configuration>
|
||||
<skip>true</skip>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>docs</id>
|
||||
@@ -44,11 +34,9 @@
|
||||
<artifactId>git-commit-id-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-dependency-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-resources-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
@@ -60,9 +48,11 @@
|
||||
<artifactId>asciidoctor-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-antrun-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-deploy-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
|
||||
@@ -21,4 +21,4 @@
|
||||
|feign.hystrix.enabled | false | If true, an OpenFeign client will be wrapped with a Hystrix circuit breaker.
|
||||
|feign.okhttp.enabled | false | Enables the use of the OK HTTP Client by Feign.
|
||||
|
||||
|===
|
||||
|===
|
||||
@@ -112,7 +112,7 @@ public interface StoreClient {
|
||||
}
|
||||
----
|
||||
|
||||
Spring Cloud Netflix provides the following beans by default for feign (`BeanType` beanName: `ClassName`):
|
||||
Spring Cloud OpenFeign provides the following beans by default for feign (`BeanType` beanName: `ClassName`):
|
||||
|
||||
* `Decoder` feignDecoder: `ResponseEntityDecoder` (which wraps a `SpringDecoder`)
|
||||
* `Encoder` feignEncoder: `SpringEncoder`
|
||||
@@ -122,7 +122,7 @@ Spring Cloud Netflix provides the following beans by default for feign (`BeanTyp
|
||||
* `Client` feignClient: if Ribbon is in the classpath and is enabled it is a `LoadBalancerFeignClient`, otherwise if Spring Cloud LoadBalancer is in the classpath, `FeignBlockingLoadBalancerClient` is used.
|
||||
If none of them is in the classpath, the default feign client is used.
|
||||
|
||||
NOTE: `spring-cloud-starter-openfeign` contains both `spring-cloud-starter-netflix-ribbon` and `spring-cloud-starter-loadbalancer`.
|
||||
NOTE: `spring-cloud-starter-openfeign` supports both `spring-cloud-starter-netflix-ribbon` and `spring-cloud-starter-loadbalancer`. However, as they are optional dependencies, you need to make sure the one you want to use has been added to your project.
|
||||
|
||||
The OkHttpClient and ApacheHttpClient feign clients can be used by setting `feign.okhttp.enabled` or `feign.httpclient.enabled` to `true`, respectively, and having them on the classpath.
|
||||
You can customize the HTTP client used by providing a bean of either `org.apache.http.impl.client.CloseableHttpClient` when using Apache or `okhttp3.OkHttpClient` when using OK HTTP.
|
||||
@@ -137,6 +137,10 @@ Spring Cloud OpenFeign _does not_ provide the following beans by default for fei
|
||||
* `SetterFactory`
|
||||
* `QueryMapEncoder`
|
||||
|
||||
A bean of `Retryer.NEVER_RETRY` with the type `Retryer` is created by default, which will disable retrying.
|
||||
Notice this retrying behavior is different from the Feign default one, where it will automatically retry IOExceptions,
|
||||
treating them as transient network related exceptions, and any RetryableException thrown from an ErrorDecoder.
|
||||
|
||||
Creating a bean of one of those type and placing it in a `@FeignClient` configuration (such as `FooConfiguration` above) allows you to override each one of the beans described. Example:
|
||||
|
||||
[source,java,indent=0]
|
||||
@@ -301,7 +305,7 @@ class FooController {
|
||||
----
|
||||
|
||||
NOTE: In the above example `FeignClientsConfiguration.class` is the default configuration
|
||||
provided by Spring Cloud Netflix.
|
||||
provided by Spring Cloud OpenFeign.
|
||||
|
||||
NOTE: `PROD-SVC` is the name of the service the Clients will be making requests to.
|
||||
|
||||
@@ -384,7 +388,7 @@ WARNING: There is a limitation with the implementation of fallbacks in Feign and
|
||||
|
||||
=== Feign and `@Primary`
|
||||
|
||||
When using Feign with Hystrix fallbacks, there are multiple beans in the `ApplicationContext` of the same type. This will cause `@Autowired` to not work because there isn't exactly one bean, or one marked as primary. To work around this, Spring Cloud Netflix marks all Feign instances as `@Primary`, so Spring Framework will know which bean to inject. In some cases, this may not be desirable. To turn off this behavior set the `primary` attribute of `@FeignClient` to false.
|
||||
When using Feign with Hystrix fallbacks, there are multiple beans in the `ApplicationContext` of the same type. This will cause `@Autowired` to not work because there isn't exactly one bean, or one marked as primary. To work around this, Spring Cloud OpenFeign marks all Feign instances as `@Primary`, so Spring Framework will know which bean to inject. In some cases, this may not be desirable. To turn off this behavior set the `primary` attribute of `@FeignClient` to false.
|
||||
|
||||
[source,java,indent=0]
|
||||
----
|
||||
@@ -582,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 by passing the desired `feign.CollectionFormat` as annotation value.
|
||||
|
||||
In the following example, the `CSV` format is 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: 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.
|
||||
|
||||
|
||||
@@ -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.3.BUILD-SNAPSHOT</version>
|
||||
<version>2.2.5.RELEASE</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.0.BUILD-SNAPSHOT</version>
|
||||
<version>2.3.1.RELEASE</version>
|
||||
<relativePath/>
|
||||
</parent>
|
||||
<scm>
|
||||
@@ -26,8 +26,8 @@
|
||||
<properties>
|
||||
<main.basedir>${basedir}</main.basedir>
|
||||
<jackson.version>2.7.3</jackson.version>
|
||||
<spring-cloud-commons.version>2.2.3.BUILD-SNAPSHOT</spring-cloud-commons.version>
|
||||
<spring-cloud-netflix.version>2.2.3.BUILD-SNAPSHOT</spring-cloud-netflix.version>
|
||||
<spring-cloud-commons.version>2.2.5.RELEASE</spring-cloud-commons.version>
|
||||
<spring-cloud-netflix.version>2.2.5.RELEASE</spring-cloud-netflix.version>
|
||||
|
||||
<!-- Plugin versions -->
|
||||
<maven-compiler-plugin.version>3.6.1</maven-compiler-plugin.version>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-openfeign</artifactId>
|
||||
<version>2.2.3.BUILD-SNAPSHOT</version>
|
||||
<version>2.2.5.RELEASE</version>
|
||||
<relativePath>..</relativePath> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<artifactId>spring-cloud-openfeign-core</artifactId>
|
||||
@@ -231,12 +231,6 @@
|
||||
<artifactId>spring-cloud-loadbalancer</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>pl.pragmatists</groupId>
|
||||
<artifactId>JUnitParams</artifactId>
|
||||
<version>1.1.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<profiles>
|
||||
<profile>
|
||||
|
||||
+41
@@ -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();
|
||||
|
||||
}
|
||||
+2
@@ -18,6 +18,7 @@ package org.springframework.cloud.openfeign;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Inherited;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
@@ -36,6 +37,7 @@ import org.springframework.core.annotation.AliasFor;
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Inherited
|
||||
public @interface FeignClient {
|
||||
|
||||
/**
|
||||
|
||||
+68
-63
@@ -18,6 +18,7 @@ package org.springframework.cloud.openfeign;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import feign.Client;
|
||||
import feign.Contract;
|
||||
@@ -52,6 +53,7 @@ import org.springframework.util.StringUtils;
|
||||
* @author Eko Kurniawan Khannedy
|
||||
* @author Gregor Zurowski
|
||||
* @author Matt King
|
||||
* @author Olga Maciaszek-Sharma
|
||||
*/
|
||||
class FeignClientFactoryBean
|
||||
implements FactoryBean<Object>, InitializingBean, ApplicationContextAware {
|
||||
@@ -81,15 +83,19 @@ class FeignClientFactoryBean
|
||||
|
||||
private Class<?> fallbackFactory = void.class;
|
||||
|
||||
private int readTimeoutMillis = new Request.Options().readTimeoutMillis();
|
||||
|
||||
private int connectTimeoutMillis = new Request.Options().connectTimeoutMillis();
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
Assert.hasText(this.contextId, "Context id must be set");
|
||||
Assert.hasText(this.name, "Name must be set");
|
||||
public void afterPropertiesSet() {
|
||||
Assert.hasText(contextId, "Context id must be set");
|
||||
Assert.hasText(name, "Name must be set");
|
||||
}
|
||||
|
||||
protected Feign.Builder feign(FeignContext context) {
|
||||
FeignLoggerFactory loggerFactory = get(context, FeignLoggerFactory.class);
|
||||
Logger logger = loggerFactory.create(this.type);
|
||||
Logger logger = loggerFactory.create(type);
|
||||
|
||||
// @formatter:off
|
||||
Feign.Builder builder = get(context, Feign.Builder.class)
|
||||
@@ -106,7 +112,7 @@ class FeignClientFactoryBean
|
||||
}
|
||||
|
||||
protected void configureFeign(FeignContext context, Feign.Builder builder) {
|
||||
FeignClientProperties properties = this.applicationContext
|
||||
FeignClientProperties properties = applicationContext
|
||||
.getBean(FeignClientProperties.class);
|
||||
|
||||
FeignClientConfigurer feignClientConfigurer = getOptional(context,
|
||||
@@ -119,15 +125,13 @@ class FeignClientFactoryBean
|
||||
configureUsingProperties(
|
||||
properties.getConfig().get(properties.getDefaultConfig()),
|
||||
builder);
|
||||
configureUsingProperties(properties.getConfig().get(this.contextId),
|
||||
builder);
|
||||
configureUsingProperties(properties.getConfig().get(contextId), builder);
|
||||
}
|
||||
else {
|
||||
configureUsingProperties(
|
||||
properties.getConfig().get(properties.getDefaultConfig()),
|
||||
builder);
|
||||
configureUsingProperties(properties.getConfig().get(this.contextId),
|
||||
builder);
|
||||
configureUsingProperties(properties.getConfig().get(contextId), builder);
|
||||
configureUsingConfiguration(context, builder);
|
||||
}
|
||||
}
|
||||
@@ -155,7 +159,7 @@ class FeignClientFactoryBean
|
||||
FeignErrorDecoderFactory errorDecoderFactory = getOptional(context,
|
||||
FeignErrorDecoderFactory.class);
|
||||
if (errorDecoderFactory != null) {
|
||||
ErrorDecoder factoryErrorDecoder = errorDecoderFactory.create(this.type);
|
||||
ErrorDecoder factoryErrorDecoder = errorDecoderFactory.create(type);
|
||||
builder.errorDecoder(factoryErrorDecoder);
|
||||
}
|
||||
}
|
||||
@@ -163,6 +167,8 @@ class FeignClientFactoryBean
|
||||
Request.Options.class);
|
||||
if (options != null) {
|
||||
builder.options(options);
|
||||
readTimeoutMillis = options.readTimeoutMillis();
|
||||
connectTimeoutMillis = options.connectTimeoutMillis();
|
||||
}
|
||||
Map<String, RequestInterceptor> requestInterceptors = getInheritedAwareInstances(
|
||||
context, RequestInterceptor.class);
|
||||
@@ -174,7 +180,7 @@ class FeignClientFactoryBean
|
||||
if (queryMapEncoder != null) {
|
||||
builder.queryMapEncoder(queryMapEncoder);
|
||||
}
|
||||
if (this.decode404) {
|
||||
if (decode404) {
|
||||
builder.decode404();
|
||||
}
|
||||
ExceptionPropagationPolicy exceptionPropagationPolicy = getInheritedAwareOptional(
|
||||
@@ -195,10 +201,13 @@ class FeignClientFactoryBean
|
||||
builder.logLevel(config.getLoggerLevel());
|
||||
}
|
||||
|
||||
if (config.getConnectTimeout() != null && config.getReadTimeout() != null) {
|
||||
builder.options(new Request.Options(config.getConnectTimeout(),
|
||||
config.getReadTimeout()));
|
||||
}
|
||||
connectTimeoutMillis = config.getConnectTimeout() != null
|
||||
? config.getConnectTimeout() : connectTimeoutMillis;
|
||||
readTimeoutMillis = config.getReadTimeout() != null ? config.getReadTimeout()
|
||||
: readTimeoutMillis;
|
||||
|
||||
builder.options(new Request.Options(connectTimeoutMillis, TimeUnit.MILLISECONDS,
|
||||
readTimeoutMillis, TimeUnit.MILLISECONDS, true));
|
||||
|
||||
if (config.getRetryer() != null) {
|
||||
Retryer retryer = getOrInstantiate(config.getRetryer());
|
||||
@@ -244,7 +253,7 @@ class FeignClientFactoryBean
|
||||
|
||||
private <T> T getOrInstantiate(Class<T> tClass) {
|
||||
try {
|
||||
return this.applicationContext.getBean(tClass);
|
||||
return applicationContext.getBean(tClass);
|
||||
}
|
||||
catch (NoSuchBeanDefinitionException e) {
|
||||
return BeanUtils.instantiateClass(tClass);
|
||||
@@ -252,16 +261,16 @@ class FeignClientFactoryBean
|
||||
}
|
||||
|
||||
protected <T> T get(FeignContext context, Class<T> type) {
|
||||
T instance = context.getInstance(this.contextId, type);
|
||||
T instance = context.getInstance(contextId, type);
|
||||
if (instance == null) {
|
||||
throw new IllegalStateException(
|
||||
"No bean found of type " + type + " for " + this.contextId);
|
||||
"No bean found of type " + type + " for " + contextId);
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
protected <T> T getOptional(FeignContext context, Class<T> type) {
|
||||
return context.getInstance(this.contextId, type);
|
||||
return context.getInstance(contextId, type);
|
||||
}
|
||||
|
||||
protected <T> T getInheritedAwareOptional(FeignContext context, Class<T> type) {
|
||||
@@ -269,17 +278,17 @@ class FeignClientFactoryBean
|
||||
return getOptional(context, type);
|
||||
}
|
||||
else {
|
||||
return context.getInstanceWithoutAncestors(this.contextId, type);
|
||||
return context.getInstanceWithoutAncestors(contextId, type);
|
||||
}
|
||||
}
|
||||
|
||||
protected <T> Map<String, T> getInheritedAwareInstances(FeignContext context,
|
||||
Class<T> type) {
|
||||
if (inheritParentContext) {
|
||||
return context.getInstances(this.contextId, type);
|
||||
return context.getInstances(contextId, type);
|
||||
}
|
||||
else {
|
||||
return context.getInstancesWithoutAncestors(this.contextId, type);
|
||||
return context.getInstancesWithoutAncestors(contextId, type);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -307,22 +316,22 @@ class FeignClientFactoryBean
|
||||
* information
|
||||
*/
|
||||
<T> T getTarget() {
|
||||
FeignContext context = this.applicationContext.getBean(FeignContext.class);
|
||||
FeignContext context = applicationContext.getBean(FeignContext.class);
|
||||
Feign.Builder builder = feign(context);
|
||||
|
||||
if (!StringUtils.hasText(this.url)) {
|
||||
if (!this.name.startsWith("http")) {
|
||||
this.url = "http://" + this.name;
|
||||
if (!StringUtils.hasText(url)) {
|
||||
if (!name.startsWith("http")) {
|
||||
url = "http://" + name;
|
||||
}
|
||||
else {
|
||||
this.url = this.name;
|
||||
url = name;
|
||||
}
|
||||
this.url += cleanPath();
|
||||
url += cleanPath();
|
||||
return (T) loadBalance(builder, context,
|
||||
new HardCodedTarget<>(this.type, this.name, this.url));
|
||||
new HardCodedTarget<>(type, name, url));
|
||||
}
|
||||
if (StringUtils.hasText(this.url) && !this.url.startsWith("http")) {
|
||||
this.url = "http://" + this.url;
|
||||
if (StringUtils.hasText(url) && !url.startsWith("http")) {
|
||||
url = "http://" + url;
|
||||
}
|
||||
String url = this.url + cleanPath();
|
||||
Client client = getOptional(context, Client.class);
|
||||
@@ -341,7 +350,7 @@ class FeignClientFactoryBean
|
||||
}
|
||||
Targeter targeter = get(context, Targeter.class);
|
||||
return (T) targeter.target(this, builder, context,
|
||||
new HardCodedTarget<>(this.type, this.name, url));
|
||||
new HardCodedTarget<>(type, name, url));
|
||||
}
|
||||
|
||||
private String cleanPath() {
|
||||
@@ -359,7 +368,7 @@ class FeignClientFactoryBean
|
||||
|
||||
@Override
|
||||
public Class<?> getObjectType() {
|
||||
return this.type;
|
||||
return type;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -368,7 +377,7 @@ class FeignClientFactoryBean
|
||||
}
|
||||
|
||||
public Class<?> getType() {
|
||||
return this.type;
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(Class<?> type) {
|
||||
@@ -376,7 +385,7 @@ class FeignClientFactoryBean
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return this.name;
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
@@ -384,7 +393,7 @@ class FeignClientFactoryBean
|
||||
}
|
||||
|
||||
public String getContextId() {
|
||||
return this.contextId;
|
||||
return contextId;
|
||||
}
|
||||
|
||||
public void setContextId(String contextId) {
|
||||
@@ -392,7 +401,7 @@ class FeignClientFactoryBean
|
||||
}
|
||||
|
||||
public String getUrl() {
|
||||
return this.url;
|
||||
return url;
|
||||
}
|
||||
|
||||
public void setUrl(String url) {
|
||||
@@ -400,7 +409,7 @@ class FeignClientFactoryBean
|
||||
}
|
||||
|
||||
public String getPath() {
|
||||
return this.path;
|
||||
return path;
|
||||
}
|
||||
|
||||
public void setPath(String path) {
|
||||
@@ -408,7 +417,7 @@ class FeignClientFactoryBean
|
||||
}
|
||||
|
||||
public boolean isDecode404() {
|
||||
return this.decode404;
|
||||
return decode404;
|
||||
}
|
||||
|
||||
public void setDecode404(boolean decode404) {
|
||||
@@ -424,7 +433,7 @@ class FeignClientFactoryBean
|
||||
}
|
||||
|
||||
public ApplicationContext getApplicationContext() {
|
||||
return this.applicationContext;
|
||||
return applicationContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -433,7 +442,7 @@ class FeignClientFactoryBean
|
||||
}
|
||||
|
||||
public Class<?> getFallback() {
|
||||
return this.fallback;
|
||||
return fallback;
|
||||
}
|
||||
|
||||
public void setFallback(Class<?> fallback) {
|
||||
@@ -441,7 +450,7 @@ class FeignClientFactoryBean
|
||||
}
|
||||
|
||||
public Class<?> getFallbackFactory() {
|
||||
return this.fallbackFactory;
|
||||
return fallbackFactory;
|
||||
}
|
||||
|
||||
public void setFallbackFactory(Class<?> fallbackFactory) {
|
||||
@@ -457,35 +466,31 @@ class FeignClientFactoryBean
|
||||
return false;
|
||||
}
|
||||
FeignClientFactoryBean that = (FeignClientFactoryBean) o;
|
||||
return Objects.equals(this.applicationContext, that.applicationContext)
|
||||
&& this.decode404 == that.decode404
|
||||
&& this.inheritParentContext == that.inheritParentContext
|
||||
&& Objects.equals(this.fallback, that.fallback)
|
||||
&& Objects.equals(this.fallbackFactory, that.fallbackFactory)
|
||||
&& Objects.equals(this.name, that.name)
|
||||
&& Objects.equals(this.path, that.path)
|
||||
&& Objects.equals(this.type, that.type)
|
||||
&& Objects.equals(this.url, that.url);
|
||||
return Objects.equals(applicationContext, that.applicationContext)
|
||||
&& decode404 == that.decode404
|
||||
&& inheritParentContext == that.inheritParentContext
|
||||
&& Objects.equals(fallback, that.fallback)
|
||||
&& Objects.equals(fallbackFactory, that.fallbackFactory)
|
||||
&& Objects.equals(name, that.name) && Objects.equals(path, that.path)
|
||||
&& Objects.equals(type, that.type) && Objects.equals(url, that.url);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(this.applicationContext, this.decode404,
|
||||
this.inheritParentContext, this.fallback, this.fallbackFactory, this.name,
|
||||
this.path, this.type, this.url);
|
||||
return Objects.hash(applicationContext, decode404, inheritParentContext, fallback,
|
||||
fallbackFactory, name, path, type, url);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new StringBuilder("FeignClientFactoryBean{").append("type=")
|
||||
.append(this.type).append(", ").append("name='").append(this.name)
|
||||
.append("', ").append("url='").append(this.url).append("', ")
|
||||
.append("path='").append(this.path).append("', ").append("decode404=")
|
||||
.append(this.decode404).append(", ").append("inheritParentContext=")
|
||||
.append(this.inheritParentContext).append(", ")
|
||||
.append("applicationContext=").append(this.applicationContext)
|
||||
.append(", ").append("fallback=").append(this.fallback).append(", ")
|
||||
.append("fallbackFactory=").append(this.fallbackFactory).append("}")
|
||||
return new StringBuilder("FeignClientFactoryBean{").append("type=").append(type)
|
||||
.append(", ").append("name='").append(name).append("', ").append("url='")
|
||||
.append(url).append("', ").append("path='").append(path).append("', ")
|
||||
.append("decode404=").append(decode404).append(", ")
|
||||
.append("inheritParentContext=").append(inheritParentContext).append(", ")
|
||||
.append("applicationContext=").append(applicationContext).append(", ")
|
||||
.append("fallback=").append(fallback).append(", ")
|
||||
.append("fallbackFactory=").append(fallbackFactory).append("}")
|
||||
.toString();
|
||||
}
|
||||
|
||||
|
||||
+1
-5
@@ -16,11 +16,9 @@
|
||||
|
||||
package org.springframework.cloud.openfeign.encoding;
|
||||
|
||||
import feign.Client;
|
||||
import feign.Feign;
|
||||
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
@@ -38,12 +36,10 @@ import org.springframework.context.annotation.Configuration;
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@EnableConfigurationProperties(FeignClientEncodingProperties.class)
|
||||
@ConditionalOnClass(Feign.class)
|
||||
@ConditionalOnBean(Client.class)
|
||||
// The OK HTTP client uses "transparent" compression.
|
||||
// If the content-encoding header is present it disable transparent compression
|
||||
@ConditionalOnMissingBean(type = "okhttp3.OkHttpClient")
|
||||
@ConditionalOnProperty(value = "feign.compression.request.enabled",
|
||||
matchIfMissing = false)
|
||||
@ConditionalOnProperty("feign.compression.request.enabled")
|
||||
@AutoConfigureAfter(FeignAutoConfiguration.class)
|
||||
public class FeignContentGzipEncodingAutoConfiguration {
|
||||
|
||||
|
||||
+40
-24
@@ -37,6 +37,7 @@ import feign.Param;
|
||||
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;
|
||||
@@ -56,6 +57,7 @@ import org.springframework.core.convert.TypeDescriptor;
|
||||
import org.springframework.core.convert.support.DefaultConversionService;
|
||||
import org.springframework.core.io.DefaultResourceLoader;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.http.InvalidMediaTypeException;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
@@ -121,9 +123,9 @@ public class SpringMvcContract extends Contract.BaseContract
|
||||
List<AnnotatedParameterProcessor> 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) {
|
||||
@@ -172,14 +174,15 @@ public class SpringMvcContract extends Contract.BaseContract
|
||||
if (clz.getInterfaces().length == 0) {
|
||||
RequestMapping classAnnotation = findMergedAnnotation(clz,
|
||||
RequestMapping.class);
|
||||
if (classAnnotation != null && classAnnotation.value().length > 0) {
|
||||
if (classAnnotation != null) {
|
||||
// Prepend path from class annotation if specified
|
||||
String pathValue = emptyToNull(classAnnotation.value()[0]);
|
||||
if (pathValue != null) {
|
||||
if (classAnnotation.value().length > 0) {
|
||||
String pathValue = emptyToNull(classAnnotation.value()[0]);
|
||||
pathValue = resolve(pathValue);
|
||||
if (!pathValue.equals("/")) {
|
||||
data.template().uri(pathValue);
|
||||
if (!pathValue.startsWith("/")) {
|
||||
pathValue = "/" + pathValue;
|
||||
}
|
||||
data.template().uri(pathValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -187,7 +190,7 @@ public class SpringMvcContract extends Contract.BaseContract
|
||||
|
||||
@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,
|
||||
@@ -213,6 +216,12 @@ public class SpringMvcContract extends Contract.BaseContract
|
||||
@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;
|
||||
@@ -233,9 +242,11 @@ public class SpringMvcContract extends Contract.BaseContract
|
||||
String pathValue = emptyToNull(methodMapping.value()[0]);
|
||||
if (pathValue != null) {
|
||||
pathValue = resolve(pathValue);
|
||||
if (!pathValue.equals("/")) {
|
||||
data.template().uri(pathValue, true);
|
||||
// Append path from @RequestMapping if value is present on method
|
||||
if (!pathValue.startsWith("/") && !data.template().path().endsWith("/")) {
|
||||
pathValue = "/" + pathValue;
|
||||
}
|
||||
data.template().uri(pathValue, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -248,13 +259,13 @@ public class SpringMvcContract extends Contract.BaseContract
|
||||
// headers
|
||||
parseHeaders(data, method, methodMapping);
|
||||
|
||||
data.indexToExpander(new LinkedHashMap<Integer, Param.Expander>());
|
||||
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;
|
||||
@@ -280,9 +291,9 @@ public class SpringMvcContract extends Contract.BaseContract
|
||||
|
||||
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;
|
||||
@@ -298,9 +309,8 @@ public class SpringMvcContract extends Contract.BaseContract
|
||||
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);
|
||||
@@ -399,7 +409,13 @@ public class SpringMvcContract extends Contract.BaseContract
|
||||
|
||||
if (contentTypes != null && !contentTypes.isEmpty()) {
|
||||
String type = contentTypes.iterator().next();
|
||||
return Objects.equals(MediaType.valueOf(type), MediaType.MULTIPART_FORM_DATA);
|
||||
try {
|
||||
return Objects.equals(MediaType.valueOf(type),
|
||||
MediaType.MULTIPART_FORM_DATA);
|
||||
}
|
||||
catch (InvalidMediaTypeException ignored) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -419,7 +435,7 @@ public class SpringMvcContract extends Contract.BaseContract
|
||||
|
||||
@Override
|
||||
public String expand(Object value) {
|
||||
return this.conversionService.convert(value, String.class);
|
||||
return conversionService.convert(value, String.class);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -434,7 +450,7 @@ public class SpringMvcContract extends Contract.BaseContract
|
||||
|
||||
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;
|
||||
};
|
||||
@@ -457,17 +473,17 @@ public class SpringMvcContract extends Contract.BaseContract
|
||||
|
||||
@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
|
||||
|
||||
+82
-24
@@ -17,13 +17,19 @@
|
||||
package org.springframework.cloud.openfeign;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.lang.reflect.Type;
|
||||
import java.net.SocketTimeoutException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import feign.InvocationHandlerFactory;
|
||||
import feign.Request;
|
||||
import feign.RequestInterceptor;
|
||||
import feign.RequestTemplate;
|
||||
import feign.RetryableException;
|
||||
@@ -47,6 +53,8 @@ import org.springframework.http.MediaType;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.TestPropertySource;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
@@ -57,7 +65,9 @@ import static org.springframework.boot.test.context.SpringBootTest.WebEnvironmen
|
||||
|
||||
/**
|
||||
* @author Eko Kurniawan Khannedy
|
||||
* @author Olga Maciaszek-Sharma
|
||||
*/
|
||||
@SuppressWarnings("FieldMayBeFinal")
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@SpringBootTest(classes = FeignClientUsingPropertiesTests.Application.class,
|
||||
webEnvironment = RANDOM_PORT)
|
||||
@@ -83,45 +93,45 @@ public class FeignClientUsingPropertiesTests {
|
||||
private FeignClientFactoryBean formFactoryBean;
|
||||
|
||||
public FeignClientUsingPropertiesTests() {
|
||||
this.fooFactoryBean = new FeignClientFactoryBean();
|
||||
this.fooFactoryBean.setContextId("foo");
|
||||
this.fooFactoryBean.setType(FeignClientFactoryBean.class);
|
||||
fooFactoryBean = new FeignClientFactoryBean();
|
||||
fooFactoryBean.setContextId("foo");
|
||||
fooFactoryBean.setType(FeignClientFactoryBean.class);
|
||||
|
||||
this.barFactoryBean = new FeignClientFactoryBean();
|
||||
this.barFactoryBean.setContextId("bar");
|
||||
this.barFactoryBean.setType(FeignClientFactoryBean.class);
|
||||
barFactoryBean = new FeignClientFactoryBean();
|
||||
barFactoryBean.setContextId("bar");
|
||||
barFactoryBean.setType(FeignClientFactoryBean.class);
|
||||
|
||||
this.unwrapFactoryBean = new FeignClientFactoryBean();
|
||||
this.unwrapFactoryBean.setContextId("unwrap");
|
||||
this.unwrapFactoryBean.setType(FeignClientFactoryBean.class);
|
||||
unwrapFactoryBean = new FeignClientFactoryBean();
|
||||
unwrapFactoryBean.setContextId("unwrap");
|
||||
unwrapFactoryBean.setType(FeignClientFactoryBean.class);
|
||||
|
||||
this.formFactoryBean = new FeignClientFactoryBean();
|
||||
this.formFactoryBean.setContextId("form");
|
||||
this.formFactoryBean.setType(FeignClientFactoryBean.class);
|
||||
formFactoryBean = new FeignClientFactoryBean();
|
||||
formFactoryBean.setContextId("form");
|
||||
formFactoryBean.setType(FeignClientFactoryBean.class);
|
||||
}
|
||||
|
||||
public FooClient fooClient() {
|
||||
this.fooFactoryBean.setApplicationContext(this.applicationContext);
|
||||
return this.fooFactoryBean.feign(this.context).target(FooClient.class,
|
||||
"http://localhost:" + this.port);
|
||||
fooFactoryBean.setApplicationContext(applicationContext);
|
||||
return fooFactoryBean.feign(context).target(FooClient.class,
|
||||
"http://localhost:" + port);
|
||||
}
|
||||
|
||||
public BarClient barClient() {
|
||||
this.barFactoryBean.setApplicationContext(this.applicationContext);
|
||||
return this.barFactoryBean.feign(this.context).target(BarClient.class,
|
||||
"http://localhost:" + this.port);
|
||||
barFactoryBean.setApplicationContext(applicationContext);
|
||||
return barFactoryBean.feign(context).target(BarClient.class,
|
||||
"http://localhost:" + port);
|
||||
}
|
||||
|
||||
public UnwrapClient unwrapClient() {
|
||||
this.unwrapFactoryBean.setApplicationContext(this.applicationContext);
|
||||
return this.unwrapFactoryBean.feign(this.context).target(UnwrapClient.class,
|
||||
"http://localhost:" + this.port);
|
||||
unwrapFactoryBean.setApplicationContext(applicationContext);
|
||||
return unwrapFactoryBean.feign(context).target(UnwrapClient.class,
|
||||
"http://localhost:" + port);
|
||||
}
|
||||
|
||||
public FormClient formClient() {
|
||||
this.formFactoryBean.setApplicationContext(this.applicationContext);
|
||||
return this.formFactoryBean.feign(this.context).target(FormClient.class,
|
||||
"http://localhost:" + this.port);
|
||||
formFactoryBean.setApplicationContext(applicationContext);
|
||||
return formFactoryBean.feign(context).target(FormClient.class,
|
||||
"http://localhost:" + port);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -149,6 +159,47 @@ public class FeignClientUsingPropertiesTests {
|
||||
assertThat(response).isEqualTo("Data");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void readTimeoutShouldWorkWhenConnectTimeoutNotSet() {
|
||||
FeignClientFactoryBean readTimeoutFactoryBean = new FeignClientFactoryBean();
|
||||
readTimeoutFactoryBean.setContextId("readTimeout");
|
||||
readTimeoutFactoryBean.setType(FeignClientFactoryBean.class);
|
||||
readTimeoutFactoryBean.setApplicationContext(applicationContext);
|
||||
|
||||
TimeoutClient client = readTimeoutFactoryBean.feign(context)
|
||||
.target(TimeoutClient.class, "http://localhost:" + port);
|
||||
|
||||
Request.Options options = getRequestOptions((Proxy) client);
|
||||
|
||||
assertThat(options.readTimeoutMillis()).isEqualTo(1000);
|
||||
assertThat(options.connectTimeoutMillis()).isEqualTo(5000);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void connectTimeoutShouldWorkWhenReadTimeoutNotSet() {
|
||||
FeignClientFactoryBean readTimeoutFactoryBean = new FeignClientFactoryBean();
|
||||
readTimeoutFactoryBean.setContextId("connectTimeout");
|
||||
readTimeoutFactoryBean.setType(FeignClientFactoryBean.class);
|
||||
readTimeoutFactoryBean.setApplicationContext(applicationContext);
|
||||
|
||||
TimeoutClient client = readTimeoutFactoryBean.feign(context)
|
||||
.target(TimeoutClient.class, "http://localhost:" + port);
|
||||
|
||||
Request.Options options = getRequestOptions((Proxy) client);
|
||||
|
||||
assertThat(options.connectTimeoutMillis()).isEqualTo(1000);
|
||||
assertThat(options.readTimeoutMillis()).isEqualTo(5000);
|
||||
}
|
||||
|
||||
private Request.Options getRequestOptions(Proxy client) {
|
||||
Object invocationHandler = ReflectionTestUtils.getField(client, "h");
|
||||
Map<Method, InvocationHandlerFactory.MethodHandler> dispatch = (Map<Method, InvocationHandlerFactory.MethodHandler>) ReflectionTestUtils
|
||||
.getField(Objects.requireNonNull(invocationHandler), "dispatch");
|
||||
Method key = new ArrayList<>(dispatch.keySet()).get(0);
|
||||
return (Request.Options) ReflectionTestUtils.getField(dispatch.get(key),
|
||||
"options");
|
||||
}
|
||||
|
||||
protected interface FooClient {
|
||||
|
||||
@RequestMapping(method = RequestMethod.GET, value = "/foo")
|
||||
@@ -178,6 +229,13 @@ public class FeignClientUsingPropertiesTests {
|
||||
|
||||
}
|
||||
|
||||
protected interface TimeoutClient {
|
||||
|
||||
@GetMapping("/timeouts")
|
||||
String timeouts();
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@EnableAutoConfiguration
|
||||
@RestController
|
||||
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* 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.beans;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.mock.mockito.MockBean;
|
||||
import org.springframework.cloud.openfeign.FeignClient;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
* @author Olga Maciaszek-Sharma
|
||||
*/
|
||||
@SpringBootTest(classes = FeignClientMockBeanTests.Config.class)
|
||||
public class FeignClientMockBeanTests {
|
||||
|
||||
@MockBean
|
||||
private RandomClient randomClient;
|
||||
|
||||
@Autowired
|
||||
private TestService testService;
|
||||
|
||||
@Test
|
||||
public void randomClientShouldBeMocked() {
|
||||
String mockMessage = "Mocked Feign Client";
|
||||
when(randomClient.getRandomString()).thenReturn(mockMessage);
|
||||
|
||||
String returnedMessage = testService.testMethod();
|
||||
|
||||
assertThat(returnedMessage).isEqualTo(mockMessage);
|
||||
}
|
||||
|
||||
@FeignClient("random-test")
|
||||
protected interface RandomClient {
|
||||
|
||||
@GetMapping("/random-test")
|
||||
String getRandomString();
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
protected static class Config {
|
||||
|
||||
@Bean
|
||||
TestService testService() {
|
||||
return new TestService();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class TestService {
|
||||
|
||||
@Autowired
|
||||
private FeignClientMockBeanTests.RandomClient randomClient;
|
||||
|
||||
public String testMethod() {
|
||||
return randomClient.getRandomString();
|
||||
}
|
||||
|
||||
}
|
||||
+94
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* 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.AfterAll;
|
||||
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()));
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
public static void afterClass() {
|
||||
System.clearProperty("server.port");
|
||||
}
|
||||
|
||||
@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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+116
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
* 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 java.nio.charset.Charset;
|
||||
|
||||
import feign.codec.Decoder;
|
||||
import feign.codec.Encoder;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
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.EnableFeignClients;
|
||||
import org.springframework.cloud.openfeign.FeignClient;
|
||||
import org.springframework.cloud.openfeign.test.NoSecurityConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.util.SocketUtils;
|
||||
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.RestController;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThatCode;
|
||||
|
||||
/**
|
||||
* Integration tests for {@link SpringMvcContract}
|
||||
*
|
||||
* @author Olga Maciaszek-Sharma
|
||||
*/
|
||||
@SpringBootTest(classes = SpringMvcContractIntegrationTests.Config.class,
|
||||
webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
|
||||
public class SpringMvcContractIntegrationTests {
|
||||
|
||||
@Autowired
|
||||
private TestClient client;
|
||||
|
||||
@BeforeAll
|
||||
public static void beforeClass() {
|
||||
System.setProperty("server.port",
|
||||
String.valueOf(SocketUtils.findAvailableTcpPort()));
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
public static void afterClass() {
|
||||
System.clearProperty("server.port");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldNotThrowInvalidMediaTypeExceptionWhenContentTypeTemplateUsed() {
|
||||
assertThatCode(() -> client.sendMessage("test", "text/markdown"))
|
||||
.doesNotThrowAnyException();
|
||||
}
|
||||
|
||||
@FeignClient(name = "test", url = "http://localhost:${server.port}/",
|
||||
configuration = NoCodecsFeignConfiguration.class)
|
||||
interface TestClient {
|
||||
|
||||
@PostMapping("/test")
|
||||
Object sendMessage(@RequestBody String message,
|
||||
@RequestHeader(HttpHeaders.CONTENT_TYPE) String acceptHeader);
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@EnableFeignClients(clients = TestClient.class)
|
||||
@EnableAutoConfiguration
|
||||
@RestController
|
||||
@Import(NoSecurityConfiguration.class)
|
||||
protected static class Config {
|
||||
|
||||
@PostMapping("/test")
|
||||
Object sendMessage(@RequestBody String message,
|
||||
@RequestHeader(HttpHeaders.CONTENT_TYPE) String acceptHeader) {
|
||||
return message;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// avoid feign.codec.EncodeException - this feature works for users that override
|
||||
// Encoder
|
||||
protected static class NoCodecsFeignConfiguration {
|
||||
|
||||
@Bean
|
||||
public Decoder decoder() {
|
||||
return (response, type) -> response;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Encoder encoder() {
|
||||
return (object, bodyType, request) -> request
|
||||
.body(object.toString().getBytes(), Charset.defaultCharset());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+58
-125
@@ -31,12 +31,10 @@ import java.util.Map;
|
||||
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||
import feign.MethodMetadata;
|
||||
import feign.Param;
|
||||
import junitparams.JUnitParamsRunner;
|
||||
import junitparams.Parameters;
|
||||
import org.junit.Before;
|
||||
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 +60,7 @@ import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
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;
|
||||
|
||||
@@ -71,8 +70,8 @@ import static org.junit.Assume.assumeTrue;
|
||||
* @author Aram Peres
|
||||
* @author Aaron Whiteside
|
||||
* @author Artyom Romanenko
|
||||
* @author Olga Maciaszek-Sharma
|
||||
*/
|
||||
@RunWith(JUnitParamsRunner.class)
|
||||
public class SpringMvcContractTests {
|
||||
|
||||
private static final Class<?> EXECUTABLE_TYPE;
|
||||
@@ -123,14 +122,14 @@ public class SpringMvcContractTests {
|
||||
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}");
|
||||
@@ -143,7 +142,7 @@ public class SpringMvcContractTests {
|
||||
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}");
|
||||
@@ -158,7 +157,7 @@ public class SpringMvcContractTests {
|
||||
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}");
|
||||
@@ -174,7 +173,7 @@ public class SpringMvcContractTests {
|
||||
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}");
|
||||
@@ -188,7 +187,7 @@ public class SpringMvcContractTests {
|
||||
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}");
|
||||
@@ -201,12 +200,12 @@ public class SpringMvcContractTests {
|
||||
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());
|
||||
@@ -220,7 +219,7 @@ public class SpringMvcContractTests {
|
||||
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("/");
|
||||
@@ -234,7 +233,7 @@ public class SpringMvcContractTests {
|
||||
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("/");
|
||||
@@ -248,7 +247,7 @@ public class SpringMvcContractTests {
|
||||
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())
|
||||
@@ -263,16 +262,28 @@ public class SpringMvcContractTests {
|
||||
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
|
||||
}
|
||||
|
||||
@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",
|
||||
String.class, String.class, Integer.class);
|
||||
MethodMetadata data = this.contract
|
||||
MethodMetadata data = contract
|
||||
.parseAndValidateMetadata(method.getDeclaringClass(), method);
|
||||
|
||||
assertThat(data.template().url())
|
||||
@@ -297,7 +308,7 @@ public class SpringMvcContractTests {
|
||||
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())
|
||||
@@ -320,7 +331,7 @@ public class SpringMvcContractTests {
|
||||
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);
|
||||
@@ -340,7 +351,7 @@ public class SpringMvcContractTests {
|
||||
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);
|
||||
@@ -360,7 +371,7 @@ public class SpringMvcContractTests {
|
||||
@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");
|
||||
@@ -372,7 +383,7 @@ public class SpringMvcContractTests {
|
||||
@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("/");
|
||||
@@ -385,7 +396,7 @@ public class SpringMvcContractTests {
|
||||
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}");
|
||||
@@ -398,7 +409,7 @@ public class SpringMvcContractTests {
|
||||
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}");
|
||||
@@ -411,7 +422,7 @@ public class SpringMvcContractTests {
|
||||
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");
|
||||
@@ -424,12 +435,12 @@ public class SpringMvcContractTests {
|
||||
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}");
|
||||
assertThat(data.template().method()).isEqualTo("GET");
|
||||
assertThat(data.template().headers().get("X-Foo").iterator().next())
|
||||
assertThat(data.template().headers().get("x-Foo").iterator().next())
|
||||
.isEqualTo("bar");
|
||||
}
|
||||
|
||||
@@ -437,7 +448,7 @@ public class SpringMvcContractTests {
|
||||
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}");
|
||||
@@ -453,7 +464,7 @@ public class SpringMvcContractTests {
|
||||
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())
|
||||
@@ -477,7 +488,7 @@ public class SpringMvcContractTests {
|
||||
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");
|
||||
@@ -491,14 +502,14 @@ public class SpringMvcContractTests {
|
||||
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}");
|
||||
@@ -512,7 +523,7 @@ public class SpringMvcContractTests {
|
||||
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())
|
||||
@@ -527,14 +538,14 @@ public class SpringMvcContractTests {
|
||||
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<String, String> testMap = new HashMap<>();
|
||||
@@ -550,7 +561,7 @@ public class SpringMvcContractTests {
|
||||
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");
|
||||
@@ -563,7 +574,7 @@ public class SpringMvcContractTests {
|
||||
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<String, String> testMap = new HashMap<>();
|
||||
|
||||
@@ -587,53 +598,6 @@ public class SpringMvcContractTests {
|
||||
"{Accept}");
|
||||
}
|
||||
|
||||
private Class[] doubleMappingClassesProvider() {
|
||||
return new Class[] { TestTemplate_RequestMapping_Empty_Class.class,
|
||||
TestTemplate_RequestMapping_Empty_Method.class };
|
||||
}
|
||||
|
||||
@Test
|
||||
@Parameters(method = "doubleMappingClassesProvider")
|
||||
public void testDoubleRequestMapping_root(Class clazz) throws NoSuchMethodException {
|
||||
Method method = clazz.getDeclaredMethod("root");
|
||||
MethodMetadata data = contract
|
||||
.parseAndValidateMetadata(method.getDeclaringClass(), method);
|
||||
|
||||
assertThat(data.template().url()).isEqualTo("/");
|
||||
}
|
||||
|
||||
@Test
|
||||
@Parameters(method = "doubleMappingClassesProvider")
|
||||
public void testDoubleRequestMapping_rootReverse(Class clazz)
|
||||
throws NoSuchMethodException {
|
||||
Method method = clazz.getDeclaredMethod("rootReverse");
|
||||
MethodMetadata data = contract
|
||||
.parseAndValidateMetadata(method.getDeclaringClass(), method);
|
||||
|
||||
assertThat(data.template().url()).isEqualTo("/");
|
||||
}
|
||||
|
||||
@Test
|
||||
@Parameters(method = "doubleMappingClassesProvider")
|
||||
public void testDoubleRequestMapping_sub(Class clazz) throws NoSuchMethodException {
|
||||
Method method = clazz.getDeclaredMethod("sub");
|
||||
MethodMetadata data = contract
|
||||
.parseAndValidateMetadata(method.getDeclaringClass(), method);
|
||||
|
||||
assertThat(data.template().url()).isEqualTo("/sub");
|
||||
}
|
||||
|
||||
@Test
|
||||
@Parameters(method = "doubleMappingClassesProvider")
|
||||
public void testDoubleRequestMapping_subEmpty(Class clazz)
|
||||
throws NoSuchMethodException {
|
||||
Method method = clazz.getDeclaredMethod("subEmpty");
|
||||
MethodMetadata data = contract
|
||||
.parseAndValidateMetadata(method.getDeclaringClass(), method);
|
||||
|
||||
assertThat(data.template().url()).isEqualTo("/subEmpty");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultipleRequestPartAnnotations() throws NoSuchMethodException {
|
||||
Method method = TestTemplate_RequestPart.class.getDeclaredMethod(
|
||||
@@ -774,6 +738,10 @@ public class SpringMvcContractTests {
|
||||
@RequestMapping("/advanced")
|
||||
public interface TestTemplate_Advanced {
|
||||
|
||||
@CollectionFormat(SSV)
|
||||
@GetMapping
|
||||
ResponseEntity<TestObject> getWithCollectionFormat();
|
||||
|
||||
@ExceptionHandler
|
||||
@RequestMapping(path = "/test/{id}", method = RequestMethod.PUT,
|
||||
produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
@@ -822,40 +790,6 @@ public class SpringMvcContractTests {
|
||||
|
||||
}
|
||||
|
||||
@RequestMapping("")
|
||||
public interface TestTemplate_RequestMapping_Empty_Class {
|
||||
|
||||
@RequestMapping("/")
|
||||
String root();
|
||||
|
||||
@RequestMapping("")
|
||||
String rootReverse();
|
||||
|
||||
@RequestMapping("/sub")
|
||||
String sub();
|
||||
|
||||
@RequestMapping("subEmpty")
|
||||
String subEmpty();
|
||||
|
||||
}
|
||||
|
||||
@RequestMapping("/")
|
||||
public interface TestTemplate_RequestMapping_Empty_Method {
|
||||
|
||||
@RequestMapping("")
|
||||
String root();
|
||||
|
||||
@RequestMapping("/")
|
||||
String rootReverse();
|
||||
|
||||
@RequestMapping("/sub")
|
||||
String sub();
|
||||
|
||||
@RequestMapping("subEmpty")
|
||||
String subEmpty();
|
||||
|
||||
}
|
||||
|
||||
@JsonAutoDetect(fieldVisibility = ANY, getterVisibility = NONE,
|
||||
setterVisibility = NONE)
|
||||
public class TestObject {
|
||||
@@ -883,11 +817,10 @@ public class SpringMvcContractTests {
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -897,16 +830,16 @@ public class SpringMvcContractTests {
|
||||
|
||||
@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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -16,3 +16,5 @@ feign.client.config.form.encoder=org.springframework.cloud.openfeign.FeignClient
|
||||
feign.client.config.unwrap.connectTimeout=1000
|
||||
feign.client.config.unwrap.readTimeout=1000
|
||||
feign.client.config.unwrap.exceptionPropagationPolicy=unwrap
|
||||
feign.client.config.readTimeout.readTimeout=1000
|
||||
feign.client.config.connectTimeout.connectTimeout=1000
|
||||
|
||||
@@ -6,11 +6,11 @@
|
||||
<parent>
|
||||
<artifactId>spring-cloud-dependencies-parent</artifactId>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<version>2.3.0.BUILD-SNAPSHOT</version>
|
||||
<version>2.3.1.RELEASE</version>
|
||||
<relativePath/>
|
||||
</parent>
|
||||
<artifactId>spring-cloud-openfeign-dependencies</artifactId>
|
||||
<version>2.2.3.BUILD-SNAPSHOT</version>
|
||||
<version>2.2.5.RELEASE</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>2.2.3.BUILD-SNAPSHOT</version>
|
||||
<version>2.2.5.RELEASE</version>
|
||||
<relativePath>..</relativePath>
|
||||
</parent>
|
||||
<artifactId>spring-cloud-starter-openfeign</artifactId>
|
||||
|
||||
Reference in New Issue
Block a user