Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
161789e33a | ||
|
|
650b14ac22 | ||
|
|
17d0161faf | ||
|
|
d6783a6f1e | ||
|
|
1275875ddb | ||
|
|
c4b9492a9b | ||
|
|
3acf5f9e92 | ||
|
|
e9e38d0274 | ||
|
|
28b50401c5 | ||
|
|
6479db0a54 | ||
|
|
f7e490c466 | ||
|
|
d60cda824d | ||
|
|
190f5a9083 | ||
|
|
1005749eb5 | ||
|
|
df1f982046 | ||
|
|
baf7cf95f2 | ||
|
|
00ea88c67f | ||
|
|
d897b18e16 | ||
|
|
8c08204c78 | ||
|
|
2f43715f6f | ||
|
|
421df91a80 | ||
|
|
ec1f2ad5bb | ||
|
|
5baa8c8ed5 | ||
|
|
0684ee4bc2 | ||
|
|
0b34d89adc | ||
|
|
c33bc490cd | ||
|
|
d738b3b224 | ||
|
|
0c1d281970 | ||
|
|
0bf0426f60 | ||
|
|
9f9ce39789 | ||
|
|
4d0caec7ee | ||
|
|
92f7ba9901 | ||
|
|
b2c208ba5f | ||
|
|
cdfd3695c2 | ||
|
|
59216f2f82 | ||
|
|
4d827623f0 | ||
|
|
5e494aa196 |
@@ -1,38 +0,0 @@
|
||||
version: 2
|
||||
jobs:
|
||||
build:
|
||||
docker:
|
||||
- image: springcloud/pipeline-base
|
||||
environment:
|
||||
_JAVA_OPTIONS: "-Xms1024m -Xmx2048m"
|
||||
TERM: dumb
|
||||
branches:
|
||||
ignore:
|
||||
- gh-pages # list of branches to ignore
|
||||
steps:
|
||||
- checkout
|
||||
- restore_cache:
|
||||
key: sc-openfeign-{{ .Branch }}
|
||||
- run:
|
||||
name: "Download dependencies"
|
||||
command: ./mvnw -s .settings.xml -U --fail-never dependency:go-offline || true
|
||||
- save_cache:
|
||||
key: sc-openfeign-{{ .Branch }}
|
||||
paths:
|
||||
- ~/.m2
|
||||
- run:
|
||||
name: "Running build"
|
||||
command: ./mvnw -s .settings.xml clean org.jacoco:jacoco-maven-plugin:prepare-agent install -U -P sonar -nsu --batch-mode -Dmaven.test.redirectTestOutputToFile=true -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn
|
||||
- run:
|
||||
name: "Aggregate test results"
|
||||
when: always
|
||||
command: |
|
||||
mkdir -p $CIRCLE_TEST_REPORTS/junit/
|
||||
find . -type f -regex ".*/target/.*-reports/.*" -exec cp {} $CIRCLE_TEST_REPORTS/junit/ \;
|
||||
bash <(curl -s https://codecov.io/bash)
|
||||
- store_artifacts:
|
||||
path: /junit/
|
||||
destination: artifacts
|
||||
- store_test_results:
|
||||
path: /junit/
|
||||
destination: testartifacts
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
Spring Cloud is released under the non-restrictive Apache 2.0 license,
|
||||
and follows a very standard Github development process, using Github
|
||||
tracker for issues and merging pull requests into master. If you want
|
||||
tracker for issues and merging pull requests into main. If you want
|
||||
to contribute even something trivial please do not hesitate, but
|
||||
follow the guidelines below.
|
||||
|
||||
@@ -17,7 +17,7 @@ given the ability to merge pull requests.
|
||||
|
||||
## Code of Conduct
|
||||
This project adheres to the Contributor Covenant [code of
|
||||
conduct](https://github.com/spring-cloud/spring-cloud-build/blob/master/docs/src/main/asciidoc/code-of-conduct.adoc). By participating, you are expected to uphold this code. Please report
|
||||
conduct](https://github.com/spring-cloud/spring-cloud-build/blob/main/docs/src/main/asciidoc/code-of-conduct.adoc). By participating, you are expected to uphold this code. Please report
|
||||
unacceptable behavior to spring-code-of-conduct@pivotal.io.
|
||||
|
||||
## Code Conventions and Housekeeping
|
||||
@@ -27,7 +27,7 @@ added after the original pull request but before a merge.
|
||||
* Use the Spring Framework code format conventions. If you use Eclipse
|
||||
you can import formatter settings using the
|
||||
`eclipse-code-formatter.xml` file from the
|
||||
[Spring Cloud Build](https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/master/spring-cloud-dependencies-parent/eclipse-code-formatter.xml) project. If using IntelliJ, you can use the
|
||||
[Spring Cloud Build](https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/main/spring-cloud-dependencies-parent/eclipse-code-formatter.xml) project. If using IntelliJ, you can use the
|
||||
[Eclipse Code Formatter Plugin](https://plugins.jetbrains.com/plugin/6546) to import the same file.
|
||||
* Make sure all new `.java` files to have a simple Javadoc class comment with at least an
|
||||
`@author` tag identifying you, and preferably at least a paragraph on what the class is
|
||||
@@ -38,7 +38,7 @@ added after the original pull request but before a merge.
|
||||
than cosmetic changes).
|
||||
* Add some Javadocs and, if you change the namespace, some XSD doc elements.
|
||||
* A few unit tests would help a lot as well -- someone has to do it.
|
||||
* If no-one else is using your branch, please rebase it against the current master (or
|
||||
* If no-one else is using your branch, please rebase it against the current main (or
|
||||
other target branch in the main project).
|
||||
* When writing a commit message please follow [these conventions](https://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html),
|
||||
if you are fixing an existing issue please add `Fixes gh-XXXX` at the end of the commit
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
# This workflow will build a Java project with Maven
|
||||
# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven
|
||||
|
||||
name: Build
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ 2.2.x ]
|
||||
pull_request:
|
||||
branches: [ 2.2.x ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
java: ["8", "11", "16"]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up JDK 1.8
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: 1.8
|
||||
- name: Cache local Maven repository
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ~/.m2/repository
|
||||
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-maven-
|
||||
- name: Build with Maven
|
||||
run: ./mvnw clean install -B -U
|
||||
+5
-19
@@ -5,9 +5,9 @@ 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://github.com/spring-cloud/spring-cloud-openfeign/workflows/Build/badge.svg?branch=2.2.x&style=svg["Build",link="https://github.com/spring-cloud/spring-cloud-openfeign/actions"]
|
||||
|
||||
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"]
|
||||
image:https://codecov.io/gh/spring-cloud/spring-cloud-openfeign/branch/main/graph/badge.svg["Codecov", link="https://codecov.io/gh/spring-cloud/spring-cloud-openfeign"]
|
||||
|
||||
image:https://api.codacy.com/project/badge/Grade/97b04c4e609c4b4f86b415e4437a6484["Codacy code quality", link="https://www.codacy.com/app/Spring-Cloud/spring-cloud-openfeign?utm_source=github.com&utm_medium=referral&utm_content=spring-cloud/spring-cloud-openfeign&utm_campaign=Badge_Grade"]
|
||||
|
||||
@@ -66,23 +66,9 @@ the `.mvn` configuration, so if you find you have to do it to make a
|
||||
build succeed, please raise a ticket to get the settings added to
|
||||
source control.
|
||||
|
||||
For hints on how to build the project look in `.travis.yml` if there
|
||||
is one. There should be a "script" and maybe "install" command. Also
|
||||
look at the "services" section to see if any services need to be
|
||||
running locally (e.g. mongo or rabbit). Ignore the git-related bits
|
||||
that you might find in "before_install" since they're related to setting git
|
||||
credentials and you already have those.
|
||||
The projects that require middleware (i.e. Redis) for testing generally
|
||||
require that a local instance of [Docker](https://www.docker.com/get-started) is installed and running.
|
||||
|
||||
The projects that require middleware generally include a
|
||||
`docker-compose.yml`, so consider using
|
||||
https://docs.docker.com/compose/[Docker Compose] to run the middeware servers
|
||||
in Docker containers. See the README in the
|
||||
https://github.com/spring-cloud-samples/scripts[scripts demo
|
||||
repository] for specific instructions about the common cases of mongo,
|
||||
rabbit and redis.
|
||||
|
||||
NOTE: If all else fails, build with the command from `.travis.yml` (usually
|
||||
`./mvnw install`).
|
||||
|
||||
=== Documentation
|
||||
|
||||
@@ -319,4 +305,4 @@ IMPORTANT: Remember to set the `Scan Scope` to `All sources` since we apply chec
|
||||
|
||||
== License
|
||||
|
||||
The project license file is available https://raw.githubusercontent.com/spring-cloud/spring-cloud-openfeign/master/LICENSE.txt[here].
|
||||
The project license file is available https://raw.githubusercontent.com/spring-cloud/spring-cloud-openfeign/main/LICENSE.txt[here].
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-openfeign</artifactId>
|
||||
<version>2.2.8.BUILD-SNAPSHOT</version>
|
||||
<version>2.2.11.BUILD-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>spring-cloud-openfeign-docs</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
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://github.com/spring-cloud/spring-cloud-openfeign/workflows/Build/badge.svg?branch=2.2.x&style=svg["Build",link="https://github.com/spring-cloud/spring-cloud-openfeign/actions"]
|
||||
|
||||
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"]
|
||||
image:https://codecov.io/gh/spring-cloud/spring-cloud-openfeign/branch/main/graph/badge.svg["Codecov", link="https://codecov.io/gh/spring-cloud/spring-cloud-openfeign"]
|
||||
|
||||
image:https://api.codacy.com/project/badge/Grade/97b04c4e609c4b4f86b415e4437a6484["Codacy code quality", link="https://www.codacy.com/app/Spring-Cloud/spring-cloud-openfeign?utm_source=github.com&utm_medium=referral&utm_content=spring-cloud/spring-cloud-openfeign&utm_campaign=Badge_Grade"]
|
||||
|
||||
@@ -14,12 +14,12 @@ include::intro.adoc[]
|
||||
|
||||
== Building
|
||||
|
||||
include::https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/master/docs/src/main/asciidoc/building-jdk8.adoc[]
|
||||
include::https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/main/docs/src/main/asciidoc/building-jdk8.adoc[]
|
||||
|
||||
== Contributing
|
||||
|
||||
include::https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/master/docs/src/main/asciidoc/contributing.adoc[]
|
||||
include::https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/main/docs/src/main/asciidoc/contributing.adoc[]
|
||||
|
||||
== License
|
||||
|
||||
The project license file is available https://raw.githubusercontent.com/spring-cloud/spring-cloud-openfeign/master/LICENSE.txt[here].
|
||||
The project license file is available https://raw.githubusercontent.com/spring-cloud/spring-cloud-openfeign/main/LICENSE.txt[here].
|
||||
|
||||
@@ -18,15 +18,16 @@
|
||||
|feign.httpclient.disable-ssl-validation | `false` |
|
||||
|feign.httpclient.enabled | `true` | Enables the use of the Apache HTTP Client by Feign.
|
||||
|feign.httpclient.follow-redirects | `true` |
|
||||
|feign.httpclient.hc5.enabled | `false` | Enables the use of the Apache HTTP Client 5 by Feign.
|
||||
|feign.httpclient.hc5.pool-concurrency-policy | | Pool concurrency policies.
|
||||
|feign.httpclient.hc5.pool-reuse-policy | | Pool connection re-use policies.
|
||||
|feign.httpclient.hc5.socket-timeout | `5` | Default value for socket timeout.
|
||||
|feign.httpclient.hc5.socket-timeout-unit | | Default value for socket timeout unit.
|
||||
|feign.httpclient.max-connections | `200` |
|
||||
|feign.httpclient.max-connections-per-route | `50` |
|
||||
|feign.httpclient.time-to-live | `900` |
|
||||
|feign.httpclient.time-to-live-unit | |
|
||||
|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.
|
||||
|feign.httpclient.hc5.enabled | `false` | Enables the use of the Apache HC5 by Feign.
|
||||
|feign.httpclient.hc5.socket-timeout | `5` |
|
||||
|feign.httpclient.hc5.socket-timeout-unit | `seconds` |
|
||||
|feign.httpclient.hc5.pool-reuse-policy | `fifo` | Pooled connection re-use policies
|
||||
|feign.httpclient.hc5.pool-concurrency-policy | `strict` | Pool concurrency policies
|
||||
|===
|
||||
|
||||
|===
|
||||
@@ -39,7 +39,7 @@ function check_if_anything_to_sync() {
|
||||
}
|
||||
|
||||
function retrieve_current_branch() {
|
||||
# Code getting the name of the current branch. For master we want to publish as we did until now
|
||||
# Code getting the name of the current branch. For main we want to publish as we did until now
|
||||
# https://stackoverflow.com/questions/1593051/how-to-programmatically-determine-the-current-checked-out-git-branch
|
||||
# If there is a branch already passed will reuse it - otherwise will try to find it
|
||||
CURRENT_BRANCH=${BRANCH}
|
||||
@@ -134,8 +134,8 @@ function add_docs_from_target() {
|
||||
|
||||
# Copies the docs by using the retrieved properties from Maven build
|
||||
function copy_docs_for_current_version() {
|
||||
if [[ "${CURRENT_BRANCH}" == "master" ]] ; then
|
||||
echo -e "Current branch is master - will copy the current docs only to the root folder"
|
||||
if [[ "${CURRENT_BRANCH}" == "main" ]] ; then
|
||||
echo -e "Current branch is main - will copy the current docs only to the root folder"
|
||||
for f in docs/target/generated-docs/*; do
|
||||
file=${f#docs/target/generated-docs/*}
|
||||
if ! git ls-files -i -o --exclude-standard --directory | grep -q ^$file$; then
|
||||
@@ -216,7 +216,7 @@ function commit_changes_if_applicable() {
|
||||
git commit -a -m "Sync docs from ${CURRENT_BRANCH} to gh-pages" && COMMIT_SUCCESSFUL="yes" || echo "Failed to commit changes"
|
||||
|
||||
# Uncomment the following push if you want to auto push to
|
||||
# the gh-pages branch whenever you commit to master locally.
|
||||
# the gh-pages branch whenever you commit to main locally.
|
||||
# This is a little extreme. Use with care!
|
||||
###################################################################
|
||||
if [[ "${COMMIT_SUCCESSFUL}" == "yes" ]] ; then
|
||||
@@ -249,7 +249,7 @@ The idea of this script is to update gh-pages branch with the generated docs. Wi
|
||||
the script will work in the following manner:
|
||||
|
||||
- if there's no gh-pages / target for docs module then the script ends
|
||||
- for master branch the generated docs are copied to the root of gh-pages branch
|
||||
- for main branch the generated docs are copied to the root of gh-pages branch
|
||||
- for any other branch (if that branch is whitelisted) a subfolder with branch name is created
|
||||
and docs are copied there
|
||||
- if the version switch is passed (-v) then a tag with (v) prefix will be retrieved and a folder
|
||||
@@ -327,4 +327,4 @@ build_docs_if_applicable
|
||||
retrieve_doc_properties
|
||||
stash_changes
|
||||
add_docs_from_target
|
||||
checkout_previous_branch
|
||||
checkout_previous_branch
|
||||
|
||||
@@ -27,7 +27,7 @@ public class WebApplication {
|
||||
|
||||
## Contributing
|
||||
|
||||
We welcome contributions. You can read more on how to contribute to the project https://github.com/spring-cloud/spring-cloud-openfeign/blob/master/README.adoc#3-contributing[here].
|
||||
We welcome contributions. You can read more on how to contribute to the project https://github.com/spring-cloud/spring-cloud-openfeign/blob/main/README.adoc#3-contributing[here].
|
||||
|
||||
## Community Support
|
||||
|
||||
|
||||
@@ -55,7 +55,7 @@ public interface StoreClient {
|
||||
}
|
||||
----
|
||||
|
||||
In the `@FeignClient` annotation the String value ("stores" above) is an arbitrary client name, which is used to create either a https://github.com/Netflix/ribbon[Ribbon] load-balancer (see <<spring-cloud-ribbon,below for details of Ribbon support>> and <<spring-cloud-circuitbreaker,below for details of Spring Cloud CircuitBreaker support>>) or https://github.com/spring-cloud/spring-cloud-commons/blob/master/spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/reactive/ReactiveLoadBalancer.java[Spring Cloud LoadBalancer].
|
||||
In the `@FeignClient` annotation the String value ("stores" above) is an arbitrary client name, which is used to create either a https://github.com/Netflix/ribbon[Ribbon] load-balancer (see <<spring-cloud-ribbon,below for details of Ribbon support>> and <<spring-cloud-circuitbreaker,below for details of Spring Cloud CircuitBreaker support>>) or https://github.com/spring-cloud/spring-cloud-commons/blob/main/spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/reactive/ReactiveLoadBalancer.java[Spring Cloud LoadBalancer].
|
||||
You can also specify a URL using the `url` attribute
|
||||
(absolute value or just a hostname). The name of the bean in the
|
||||
application context is the fully qualified name of the interface.
|
||||
@@ -393,7 +393,7 @@ favor for an opt-in approach.
|
||||
[[spring-cloud-feign-hystrix-fallback]]
|
||||
=== Feign Hystrix Fallbacks
|
||||
|
||||
Hystrix supports the notion of a fallback: a default code path that is executed when they circuit is open or there is an error. To enable fallbacks for a given `@FeignClient` set the `fallback` attribute to the class name that implements the fallback. You also need to declare your implementation as a Spring bean.
|
||||
Hystrix supports the notion of a fallback: a default code path that is executed when the circuit is open or there is an error. To enable fallbacks for a given `@FeignClient` set the `fallback` attribute to the class name that implements the fallback. You also need to declare your implementation as a Spring bean.
|
||||
|
||||
[source,java,indent=0]
|
||||
----
|
||||
@@ -461,7 +461,7 @@ The circuit breaker name follows this pattern `<feignClientName>#<calledMethod>`
|
||||
[[spring-cloud-feign-circuitbreaker-fallback]]
|
||||
=== Feign Spring Cloud CircuitBreaker Fallbacks
|
||||
|
||||
Spring Cloud CircuitBreaker supports the notion of a fallback: a default code path that is executed when they circuit is open or there is an error. To enable fallbacks for a given `@FeignClient` set the `fallback` attribute to the class name that implements the fallback. You also need to declare your implementation as a Spring bean.
|
||||
Spring Cloud CircuitBreaker supports the notion of a fallback: a default code path that is executed when the circuit is open or there is an error. To enable fallbacks for a given `@FeignClient` set the `fallback` attribute to the class name that implements the fallback. You also need to declare your implementation as a Spring bean.
|
||||
|
||||
[source,java,indent=0]
|
||||
----
|
||||
@@ -523,10 +523,7 @@ public interface UserClient extends UserService {
|
||||
}
|
||||
----
|
||||
|
||||
NOTE: It is generally not advisable to share an interface between a
|
||||
server and a client. It introduces tight coupling, and also actually
|
||||
doesn't work with Spring MVC in its current form (method parameter
|
||||
mapping is not inherited).
|
||||
WARNING: `@FeignClient` interfaces should not be shared between server and client and annotating `@FeignClient` interfaces with `@RequestMapping` on class level is no longer supported.
|
||||
|
||||
=== Feign request/response compression
|
||||
|
||||
|
||||
@@ -4,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.8.BUILD-SNAPSHOT</version>
|
||||
<version>2.2.11.BUILD-SNAPSHOT</version>
|
||||
<packaging>pom</packaging>
|
||||
<name>Spring Cloud OpenFeign</name>
|
||||
<description>Spring Cloud OpenFeign</description>
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-build</artifactId>
|
||||
<version>2.3.2.RELEASE</version>
|
||||
<version>2.3.6.BUILD-SNAPSHOT</version>
|
||||
<relativePath/>
|
||||
</parent>
|
||||
<scm>
|
||||
@@ -26,8 +26,8 @@
|
||||
<properties>
|
||||
<main.basedir>${basedir}</main.basedir>
|
||||
<jackson.version>2.11.3</jackson.version>
|
||||
<spring-cloud-commons.version>2.2.8.BUILD-SNAPSHOT</spring-cloud-commons.version>
|
||||
<spring-cloud-netflix.version>2.2.8.BUILD-SNAPSHOT</spring-cloud-netflix.version>
|
||||
<spring-cloud-commons.version>2.2.9.RELEASE</spring-cloud-commons.version>
|
||||
<spring-cloud-netflix.version>2.2.9.RELEASE</spring-cloud-netflix.version>
|
||||
|
||||
<!-- Plugin versions -->
|
||||
<maven-eclipse-plugin.version>2.10</maven-eclipse-plugin.version>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-openfeign</artifactId>
|
||||
<version>2.2.8.BUILD-SNAPSHOT</version>
|
||||
<version>2.2.11.BUILD-SNAPSHOT</version>
|
||||
<relativePath>..</relativePath> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<artifactId>spring-cloud-openfeign-core</artifactId>
|
||||
|
||||
+14
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.springframework.cloud.openfeign;
|
||||
|
||||
import feign.Feign;
|
||||
import feign.hystrix.FallbackFactory;
|
||||
|
||||
import org.springframework.context.ApplicationContext;
|
||||
@@ -28,6 +29,7 @@ import org.springframework.context.ApplicationContext;
|
||||
*
|
||||
* @author Sven Döring
|
||||
* @author Matt King
|
||||
* @author Sam Kruglov
|
||||
*/
|
||||
public class FeignClientBuilder {
|
||||
|
||||
@@ -69,6 +71,18 @@ public class FeignClientBuilder {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies a {@link FeignBuilderCustomizer} to the underlying
|
||||
* {@link Feign.Builder}. May be called multiple times.
|
||||
* @param customizer applied in the same order as supplied here after applying
|
||||
* customizers found in the context.
|
||||
* @return the {@link Builder} with the customizer added
|
||||
*/
|
||||
public Builder<T> customize(final FeignBuilderCustomizer customizer) {
|
||||
this.feignClientFactoryBean.addCustomizer(customizer);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder<T> contextId(final String contextId) {
|
||||
this.feignClientFactoryBean.setContextId(contextId);
|
||||
return this;
|
||||
|
||||
+18
-1
@@ -35,6 +35,8 @@ import feign.Target.HardCodedTarget;
|
||||
import feign.codec.Decoder;
|
||||
import feign.codec.Encoder;
|
||||
import feign.codec.ErrorDecoder;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.BeansException;
|
||||
@@ -62,6 +64,7 @@ import org.springframework.util.StringUtils;
|
||||
* @author Olga Maciaszek-Sharma
|
||||
* @author Ilia Ilinykh
|
||||
* @author Marcin Grzejszczak
|
||||
* @author Sam Kruglov
|
||||
*/
|
||||
public class FeignClientFactoryBean implements FactoryBean<Object>, InitializingBean,
|
||||
ApplicationContextAware, BeanFactoryAware {
|
||||
@@ -71,6 +74,8 @@ public class FeignClientFactoryBean implements FactoryBean<Object>, Initializing
|
||||
* lifecycle race condition.
|
||||
***********************************/
|
||||
|
||||
private static Log LOG = LogFactory.getLog(FeignClientFactoryBean.class);
|
||||
|
||||
private Class<?> type;
|
||||
|
||||
private String name;
|
||||
@@ -99,6 +104,8 @@ public class FeignClientFactoryBean implements FactoryBean<Object>, Initializing
|
||||
|
||||
private boolean followRedirects = new Request.Options().isFollowRedirects();
|
||||
|
||||
private List<FeignBuilderCustomizer> additionalCustomizers = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
Assert.hasText(contextId, "Context id must be set");
|
||||
@@ -134,6 +141,7 @@ public class FeignClientFactoryBean implements FactoryBean<Object>, Initializing
|
||||
.forEach(feignBuilderCustomizer -> feignBuilderCustomizer
|
||||
.customize(builder));
|
||||
}
|
||||
additionalCustomizers.forEach(customizer -> customizer.customize(builder));
|
||||
}
|
||||
|
||||
protected void configureFeign(FeignContext context, Feign.Builder builder) {
|
||||
@@ -345,7 +353,7 @@ public class FeignClientFactoryBean implements FactoryBean<Object>, Initializing
|
||||
}
|
||||
|
||||
throw new IllegalStateException(
|
||||
"No Feign Client for loadBalancing defined. Did you forget to include spring-cloud-starter-netflix-ribbon?");
|
||||
"No Feign Client for loadBalancing defined. Did you forget to include spring-cloud-starter-netflix-ribbon or spring-cloud-starter-loadbalancer?");
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -365,6 +373,11 @@ public class FeignClientFactoryBean implements FactoryBean<Object>, Initializing
|
||||
Feign.Builder builder = feign(context);
|
||||
|
||||
if (!StringUtils.hasText(url)) {
|
||||
|
||||
if (LOG.isInfoEnabled()) {
|
||||
LOG.info("For '" + name
|
||||
+ "' URL not provided. Will try picking an instance via load-balancing.");
|
||||
}
|
||||
if (!name.startsWith("http")) {
|
||||
url = "http://" + name;
|
||||
}
|
||||
@@ -483,6 +496,10 @@ public class FeignClientFactoryBean implements FactoryBean<Object>, Initializing
|
||||
this.inheritParentContext = inheritParentContext;
|
||||
}
|
||||
|
||||
public void addCustomizer(FeignBuilderCustomizer customizer) {
|
||||
additionalCustomizers.add(customizer);
|
||||
}
|
||||
|
||||
public ApplicationContext getApplicationContext() {
|
||||
return applicationContext;
|
||||
}
|
||||
|
||||
+1
-1
@@ -75,7 +75,7 @@ public class MatrixVariableParameterProcessor implements AnnotatedParameterProce
|
||||
private String expandMap(Object object) {
|
||||
Map<String, Object> paramMap = (Map) object;
|
||||
|
||||
return paramMap.keySet().stream()
|
||||
return paramMap.keySet().stream().filter(key -> paramMap.get(key) != null)
|
||||
.map(key -> ";" + key + "=" + paramMap.get(key).toString())
|
||||
.collect(Collectors.joining());
|
||||
}
|
||||
|
||||
+8
-37
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2016-2020 the original author or authors.
|
||||
* Copyright 2016-2021 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.
|
||||
@@ -16,37 +16,30 @@
|
||||
|
||||
package org.springframework.cloud.openfeign.hateoas;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
|
||||
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.ConditionalOnWebApplication;
|
||||
import org.springframework.boot.autoconfigure.data.rest.RepositoryRestMvcAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.hateoas.HypermediaAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.hateoas.RepresentationModel;
|
||||
import org.springframework.hateoas.mediatype.MessageResolver;
|
||||
import org.springframework.hateoas.mediatype.hal.CurieProvider;
|
||||
import org.springframework.hateoas.mediatype.hal.DefaultCurieProvider;
|
||||
import org.springframework.hateoas.mediatype.hal.HalConfiguration;
|
||||
import org.springframework.hateoas.mediatype.hal.Jackson2HalModule;
|
||||
import org.springframework.hateoas.server.LinkRelationProvider;
|
||||
import org.springframework.hateoas.mediatype.hal.HalMediaTypeConfiguration;
|
||||
import org.springframework.hateoas.server.mvc.TypeConstrainedMappingJackson2HttpMessageConverter;
|
||||
|
||||
import static org.springframework.hateoas.MediaTypes.HAL_JSON;
|
||||
|
||||
/**
|
||||
* @author Hector Espert
|
||||
* @author Olga Maciaszek-Sharma
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnWebApplication
|
||||
@@ -54,41 +47,19 @@ import static org.springframework.hateoas.MediaTypes.HAL_JSON;
|
||||
@AutoConfigureAfter({ JacksonAutoConfiguration.class,
|
||||
HttpMessageConvertersAutoConfiguration.class,
|
||||
RepositoryRestMvcAutoConfiguration.class })
|
||||
@AutoConfigureBefore(HypermediaAutoConfiguration.class)
|
||||
public class FeignHalAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnBean(HalMediaTypeConfiguration.class)
|
||||
@ConditionalOnMissingBean
|
||||
public TypeConstrainedMappingJackson2HttpMessageConverter halJacksonHttpMessageConverter(
|
||||
ObjectProvider<ObjectMapper> objectMapper,
|
||||
ObjectProvider<HalConfiguration> halConfiguration,
|
||||
ObjectProvider<MessageResolver> messageResolver,
|
||||
ObjectProvider<CurieProvider> curieProvider,
|
||||
ObjectProvider<LinkRelationProvider> linkRelationProvider) {
|
||||
|
||||
HalMediaTypeConfiguration halConfiguration) {
|
||||
ObjectMapper mapper = objectMapper.getIfAvailable(ObjectMapper::new).copy();
|
||||
mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
|
||||
|
||||
HalConfiguration configuration = halConfiguration
|
||||
.getIfAvailable(HalConfiguration::new);
|
||||
|
||||
CurieProvider curieProviderInstance = curieProvider
|
||||
.getIfAvailable(() -> new DefaultCurieProvider(Collections.emptyMap()));
|
||||
|
||||
Jackson2HalModule.HalHandlerInstantiator halHandlerInstantiator = new Jackson2HalModule.HalHandlerInstantiator(
|
||||
linkRelationProvider.getIfAvailable(), curieProviderInstance,
|
||||
messageResolver.getIfAvailable(), configuration);
|
||||
|
||||
mapper.setHandlerInstantiator(halHandlerInstantiator);
|
||||
|
||||
if (!Jackson2HalModule.isAlreadyRegisteredIn(mapper)) {
|
||||
Jackson2HalModule halModule = new Jackson2HalModule();
|
||||
mapper.registerModule(halModule);
|
||||
}
|
||||
|
||||
halConfiguration.configureObjectMapper(mapper);
|
||||
TypeConstrainedMappingJackson2HttpMessageConverter converter = new TypeConstrainedMappingJackson2HttpMessageConverter(
|
||||
RepresentationModel.class);
|
||||
converter.setSupportedMediaTypes(Arrays.asList(HAL_JSON));
|
||||
converter.setSupportedMediaTypes(Collections.singletonList(HAL_JSON));
|
||||
converter.setObjectMapper(mapper);
|
||||
return converter;
|
||||
}
|
||||
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright 2013-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.openfeign.loadbalancer;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.net.URI;
|
||||
|
||||
import feign.Response;
|
||||
|
||||
import org.springframework.cloud.client.loadbalancer.RetryableStatusCodeException;
|
||||
|
||||
/**
|
||||
* A {@link RetryableStatusCodeException} for {@link Response}s.
|
||||
*
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
public class LoadBalancerResponseStatusCodeException
|
||||
extends RetryableStatusCodeException {
|
||||
|
||||
private final Response response;
|
||||
|
||||
public LoadBalancerResponseStatusCodeException(String serviceId, Response response,
|
||||
byte[] body, URI uri) {
|
||||
super(serviceId, response.status(), response, uri);
|
||||
this.response = Response.builder()
|
||||
.body(new ByteArrayInputStream(body), body.length)
|
||||
.headers(response.headers()).reason(response.reason())
|
||||
.status(response.status()).request(response.request()).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response getResponse() {
|
||||
return this.response;
|
||||
}
|
||||
|
||||
}
|
||||
+5
-3
@@ -36,7 +36,6 @@ import org.springframework.cloud.client.loadbalancer.LoadBalancedRecoveryCallbac
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryContext;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryFactory;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryPolicy;
|
||||
import org.springframework.cloud.client.loadbalancer.RetryableStatusCodeException;
|
||||
import org.springframework.cloud.loadbalancer.blocking.client.BlockingLoadBalancerClient;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
@@ -46,6 +45,7 @@ import org.springframework.retry.backoff.BackOffPolicy;
|
||||
import org.springframework.retry.backoff.NoBackOffPolicy;
|
||||
import org.springframework.retry.policy.NeverRetryPolicy;
|
||||
import org.springframework.retry.support.RetryTemplate;
|
||||
import org.springframework.util.StreamUtils;
|
||||
|
||||
/**
|
||||
* A {@link Client} implementation that provides Spring Retry support for requests
|
||||
@@ -112,9 +112,11 @@ public class RetryableFeignBlockingLoadBalancerClient implements Client {
|
||||
LOG.debug(
|
||||
String.format("Retrying on status code: %d", responseStatus));
|
||||
}
|
||||
byte[] byteArray = response.body() == null ? new byte[] {}
|
||||
: StreamUtils.copyToByteArray(response.body().asInputStream());
|
||||
response.close();
|
||||
throw new RetryableStatusCodeException(serviceId, responseStatus,
|
||||
response, URI.create(request.url()));
|
||||
throw new LoadBalancerResponseStatusCodeException(serviceId, response,
|
||||
byteArray, URI.create(request.url()));
|
||||
}
|
||||
return response;
|
||||
}, new LoadBalancedRecoveryCallback<Response, Response>() {
|
||||
|
||||
+24
-9
@@ -24,6 +24,7 @@ import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import feign.RequestTemplate;
|
||||
@@ -48,6 +49,10 @@ import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import static org.springframework.cloud.openfeign.support.FeignUtils.getHeaders;
|
||||
import static org.springframework.cloud.openfeign.support.FeignUtils.getHttpHeaders;
|
||||
import static org.springframework.http.MediaType.APPLICATION_FORM_URLENCODED;
|
||||
import static org.springframework.http.MediaType.MULTIPART_FORM_DATA;
|
||||
import static org.springframework.http.MediaType.MULTIPART_MIXED;
|
||||
import static org.springframework.http.MediaType.MULTIPART_RELATED;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
@@ -56,6 +61,7 @@ import static org.springframework.cloud.openfeign.support.FeignUtils.getHttpHead
|
||||
* @author Aaron Whiteside
|
||||
* @author Darren Foong
|
||||
* @author Olga Maciaszek-Sharma
|
||||
* @author Can Bezmen
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
public class SpringEncoder implements Encoder {
|
||||
@@ -99,8 +105,8 @@ public class SpringEncoder implements Encoder {
|
||||
requestContentType = MediaType.valueOf(type);
|
||||
}
|
||||
|
||||
if (isMultipartType(requestContentType)) {
|
||||
this.springFormEncoder.encode(requestBody, bodyType, request);
|
||||
if (isFormRelatedContentType(requestContentType)) {
|
||||
springFormEncoder.encode(requestBody, bodyType, request);
|
||||
return;
|
||||
}
|
||||
else {
|
||||
@@ -117,7 +123,7 @@ public class SpringEncoder implements Encoder {
|
||||
|
||||
private void encodeWithMessageConverter(Object requestBody, Type bodyType,
|
||||
RequestTemplate request, MediaType requestContentType) {
|
||||
for (HttpMessageConverter messageConverter : this.messageConverters.getObject()
|
||||
for (HttpMessageConverter messageConverter : messageConverters.getObject()
|
||||
.getConverters()) {
|
||||
FeignOutputMessage outputMessage;
|
||||
try {
|
||||
@@ -223,9 +229,18 @@ public class SpringEncoder implements Encoder {
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isFormRelatedContentType(MediaType requestContentType) {
|
||||
return isMultipartType(requestContentType)
|
||||
|| isFormUrlEncoded(requestContentType);
|
||||
}
|
||||
|
||||
private boolean isMultipartType(MediaType requestContentType) {
|
||||
return Arrays.asList(MediaType.MULTIPART_FORM_DATA, MediaType.MULTIPART_MIXED,
|
||||
MediaType.MULTIPART_RELATED).contains(requestContentType);
|
||||
return Arrays.asList(MULTIPART_FORM_DATA, MULTIPART_MIXED, MULTIPART_RELATED)
|
||||
.contains(requestContentType);
|
||||
}
|
||||
|
||||
private boolean isFormUrlEncoded(MediaType requestContentType) {
|
||||
return Objects.equals(APPLICATION_FORM_URLENCODED, requestContentType);
|
||||
}
|
||||
|
||||
private boolean binaryContentType(FeignOutputMessage outputMessage) {
|
||||
@@ -244,21 +259,21 @@ public class SpringEncoder implements Encoder {
|
||||
private final HttpHeaders httpHeaders;
|
||||
|
||||
private FeignOutputMessage(RequestTemplate request) {
|
||||
this.httpHeaders = getHttpHeaders(request.headers());
|
||||
httpHeaders = getHttpHeaders(request.headers());
|
||||
}
|
||||
|
||||
@Override
|
||||
public OutputStream getBody() throws IOException {
|
||||
return this.outputStream;
|
||||
return outputStream;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpHeaders getHeaders() {
|
||||
return this.httpHeaders;
|
||||
return httpHeaders;
|
||||
}
|
||||
|
||||
public ByteArrayOutputStream getOutputStream() {
|
||||
return this.outputStream;
|
||||
return outputStream;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+11
-37
@@ -35,6 +35,8 @@ import feign.Feign;
|
||||
import feign.MethodMetadata;
|
||||
import feign.Param;
|
||||
import feign.Request;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.cloud.openfeign.AnnotatedParameterProcessor;
|
||||
import org.springframework.cloud.openfeign.CollectionFormat;
|
||||
@@ -83,6 +85,8 @@ import static org.springframework.core.annotation.AnnotatedElementUtils.findMerg
|
||||
public class SpringMvcContract extends Contract.BaseContract
|
||||
implements ResourceLoaderAware {
|
||||
|
||||
private static final Log LOG = LogFactory.getLog(SpringMvcContract.class);
|
||||
|
||||
private static final String ACCEPT = "Accept";
|
||||
|
||||
private static final String CONTENT_TYPE = "Content-Type";
|
||||
@@ -181,49 +185,19 @@ public class SpringMvcContract extends Contract.BaseContract
|
||||
|
||||
@Override
|
||||
protected void processAnnotationOnClass(MethodMetadata data, Class<?> clz) {
|
||||
if (clz.getInterfaces().length == 0) {
|
||||
RequestMapping classAnnotation = findMergedAnnotation(clz,
|
||||
RequestMapping.class);
|
||||
if (classAnnotation != null) {
|
||||
// Prepend path from class annotation if specified
|
||||
if (classAnnotation.value().length > 0) {
|
||||
String pathValue = emptyToNull(classAnnotation.value()[0]);
|
||||
pathValue = resolve(pathValue);
|
||||
if (!pathValue.startsWith("/")) {
|
||||
pathValue = "/" + pathValue;
|
||||
}
|
||||
data.template().uri(pathValue);
|
||||
if (data.template().decodeSlash() != decodeSlash) {
|
||||
data.template().decodeSlash(decodeSlash);
|
||||
}
|
||||
}
|
||||
}
|
||||
RequestMapping classAnnotation = findMergedAnnotation(clz, RequestMapping.class);
|
||||
if (classAnnotation != null) {
|
||||
LOG.error("Cannot process class: " + clz.getName()
|
||||
+ ". @RequestMapping annotation is not allowed on @FeignClient interfaces.");
|
||||
throw new IllegalArgumentException(
|
||||
"@RequestMapping annotation not allowed on @FeignClient interfaces");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public MethodMetadata parseAndValidateMetadata(Class<?> targetType, Method method) {
|
||||
processedMethods.put(Feign.configKey(targetType, method), method);
|
||||
MethodMetadata md = super.parseAndValidateMetadata(targetType, method);
|
||||
|
||||
RequestMapping classAnnotation = findMergedAnnotation(targetType,
|
||||
RequestMapping.class);
|
||||
if (classAnnotation != null) {
|
||||
// produces - use from class annotation only if method has not specified this
|
||||
if (!md.template().headers().containsKey(ACCEPT)) {
|
||||
parseProduces(md, method, classAnnotation);
|
||||
}
|
||||
|
||||
// consumes -- use from class annotation only if method has not specified this
|
||||
if (!md.template().headers().containsKey(CONTENT_TYPE)) {
|
||||
parseConsumes(md, method, classAnnotation);
|
||||
}
|
||||
|
||||
// headers -- class annotation is inherited to methods, always write these if
|
||||
// present
|
||||
parseHeaders(md, method, classAnnotation);
|
||||
}
|
||||
return md;
|
||||
return super.parseAndValidateMetadata(targetType, method);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+20
@@ -33,6 +33,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author Matt King
|
||||
* @author Sam Kruglov
|
||||
*/
|
||||
public class FeignBuilderCustomizerTests {
|
||||
|
||||
@@ -78,6 +79,25 @@ public class FeignBuilderCustomizerTests {
|
||||
context.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBuildCustomizerOrderedWithAdditional() {
|
||||
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
|
||||
FeignBuilderCustomizerTests.SampleConfiguration3.class);
|
||||
|
||||
FeignClientFactoryBean clientFactoryBean = context
|
||||
.getBean(FeignClientFactoryBean.class);
|
||||
clientFactoryBean.addCustomizer(builder -> builder.logLevel(Logger.Level.BASIC));
|
||||
clientFactoryBean.addCustomizer(Feign.Builder::doNotCloseAfterDecode);
|
||||
FeignContext feignContext = context.getBean(FeignContext.class);
|
||||
|
||||
Feign.Builder builder = clientFactoryBean.feign(feignContext);
|
||||
assertFeignBuilderField(builder, "logLevel", Logger.Level.BASIC);
|
||||
assertFeignBuilderField(builder, "decode404", true);
|
||||
assertFeignBuilderField(builder, "closeAfterDecode", false);
|
||||
|
||||
context.close();
|
||||
}
|
||||
|
||||
private static FeignClientFactoryBean defaultFeignClientFactoryBean() {
|
||||
FeignClientFactoryBean feignClientFactoryBean = new FeignClientFactoryBean();
|
||||
feignClientFactoryBean.setContextId("test");
|
||||
|
||||
+22
-6
@@ -24,6 +24,7 @@ import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import feign.Feign;
|
||||
import feign.hystrix.FallbackFactory;
|
||||
import org.hamcrest.Matchers;
|
||||
import org.junit.Before;
|
||||
@@ -40,6 +41,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author Sven Döring
|
||||
* @author Sam Kruglov
|
||||
*/
|
||||
public class FeignClientBuilderTests {
|
||||
|
||||
@@ -58,6 +60,14 @@ public class FeignClientBuilderTests {
|
||||
|
||||
private static void assertFactoryBeanField(final FeignClientBuilder.Builder builder,
|
||||
final String fieldName, final Object expectedValue) {
|
||||
final Object value = getFactoryBeanField(builder, fieldName);
|
||||
assertThat(value).as("Expected value for the field '" + fieldName + "':")
|
||||
.isEqualTo(expectedValue);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static <T> T getFactoryBeanField(final FeignClientBuilder.Builder builder,
|
||||
final String fieldName) {
|
||||
final Field factoryBeanField = ReflectionUtils
|
||||
.findField(FeignClientBuilder.Builder.class, "feignClientFactoryBean");
|
||||
ReflectionUtils.makeAccessible(factoryBeanField);
|
||||
@@ -67,9 +77,7 @@ public class FeignClientBuilderTests {
|
||||
final Field field = ReflectionUtils.findField(FeignClientFactoryBean.class,
|
||||
fieldName);
|
||||
ReflectionUtils.makeAccessible(field);
|
||||
final Object value = ReflectionUtils.getField(field, factoryBean);
|
||||
assertThat(value).as("Expected value for the field '" + fieldName + "':")
|
||||
.isEqualTo(expectedValue);
|
||||
return (T) ReflectionUtils.getField(field, factoryBean);
|
||||
}
|
||||
|
||||
@Before
|
||||
@@ -131,7 +139,8 @@ public class FeignClientBuilderTests {
|
||||
.forType(TestFeignClient.class, "TestClient").inheritParentContext(false)
|
||||
.fallback(TestFeignClientFallback.class)
|
||||
.fallbackFactory(TestFeignClientFallbackFactory.class).decode404(true)
|
||||
.url("Url/").path("/Path").contextId("TestContext");
|
||||
.url("Url/").path("/Path").contextId("TestContext")
|
||||
.customize(Feign.Builder::doNotCloseAfterDecode);
|
||||
|
||||
// then:
|
||||
assertFactoryBeanField(builder, "applicationContext", this.applicationContext);
|
||||
@@ -147,14 +156,21 @@ public class FeignClientBuilderTests {
|
||||
assertFactoryBeanField(builder, "fallback", TestFeignClientFallback.class);
|
||||
assertFactoryBeanField(builder, "fallbackFactory",
|
||||
TestFeignClientFallbackFactory.class);
|
||||
List<FeignBuilderCustomizer> additionalCustomizers = getFactoryBeanField(builder,
|
||||
"additionalCustomizers");
|
||||
assertThat(additionalCustomizers).hasSize(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void forType_build() {
|
||||
// given:
|
||||
Mockito.when(this.applicationContext.getBean(FeignContext.class))
|
||||
.thenThrow(new ClosedFileSystemException()); // throw an unusual exception
|
||||
// in the
|
||||
.thenThrow(new ClosedFileSystemException()); // throw
|
||||
// an
|
||||
// unusual
|
||||
// exception
|
||||
// in
|
||||
// the
|
||||
// FeignClientFactoryBean
|
||||
final FeignClientBuilder.Builder builder = this.feignClientBuilder
|
||||
.forType(TestClient.class, "TestClient");
|
||||
|
||||
+15
-11
@@ -16,7 +16,7 @@
|
||||
|
||||
package org.springframework.cloud.openfeign.hateoas;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.junit.Test;
|
||||
@@ -26,9 +26,11 @@ import org.mockito.Mock;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.hateoas.mediatype.MessageResolver;
|
||||
import org.springframework.hateoas.mediatype.hal.CurieProvider;
|
||||
import org.springframework.hateoas.mediatype.hal.HalConfiguration;
|
||||
import org.springframework.hateoas.mediatype.hal.HalMediaTypeConfiguration;
|
||||
import org.springframework.hateoas.mediatype.hal.Jackson2HalModule;
|
||||
import org.springframework.hateoas.server.LinkRelationProvider;
|
||||
import org.springframework.hateoas.server.mvc.TypeConstrainedMappingJackson2HttpMessageConverter;
|
||||
@@ -45,20 +47,20 @@ import static org.springframework.hateoas.MediaTypes.HAL_JSON;
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class FeignHalAutoConfigurationTests {
|
||||
|
||||
@Mock
|
||||
private ObjectProvider<ObjectMapper> objectMapper;
|
||||
|
||||
@Mock
|
||||
private ObjectProvider<HalConfiguration> halConfiguration;
|
||||
|
||||
@Mock
|
||||
private ObjectProvider<LinkRelationProvider> relProvider;
|
||||
private ObjectProvider<ObjectMapper> objectMapper;
|
||||
|
||||
@Mock
|
||||
private LinkRelationProvider relProvider;
|
||||
|
||||
@Mock
|
||||
private ObjectProvider<CurieProvider> curieProvider;
|
||||
|
||||
@Mock
|
||||
private ObjectProvider<MessageResolver> messageResolver;
|
||||
private MessageResolver messageResolver;
|
||||
|
||||
@InjectMocks
|
||||
private FeignHalAutoConfiguration feignHalAutoConfiguration;
|
||||
@@ -70,17 +72,19 @@ public class FeignHalAutoConfigurationTests {
|
||||
|
||||
when(halConfiguration.getIfAvailable(any()))
|
||||
.thenReturn(mock(HalConfiguration.class));
|
||||
when(relProvider.getIfAvailable()).thenReturn(mock(LinkRelationProvider.class));
|
||||
when(curieProvider.getIfAvailable(any())).thenReturn(mock(CurieProvider.class));
|
||||
when(messageResolver.getIfAvailable()).thenReturn(mock(MessageResolver.class));
|
||||
|
||||
HalMediaTypeConfiguration halMediaTypeConfiguration = new HalMediaTypeConfiguration(
|
||||
relProvider, curieProvider, halConfiguration, messageResolver,
|
||||
new DefaultListableBeanFactory());
|
||||
|
||||
TypeConstrainedMappingJackson2HttpMessageConverter converter = feignHalAutoConfiguration
|
||||
.halJacksonHttpMessageConverter(objectMapper, halConfiguration,
|
||||
messageResolver, curieProvider, relProvider);
|
||||
.halJacksonHttpMessageConverter(objectMapper, halMediaTypeConfiguration);
|
||||
|
||||
assertThat(converter).isNotNull();
|
||||
assertThat(converter.getObjectMapper()).isNotNull();
|
||||
assertThat(converter.getSupportedMediaTypes()).isEqualTo(Arrays.asList(HAL_JSON));
|
||||
assertThat(converter.getSupportedMediaTypes())
|
||||
.isEqualTo(Collections.singletonList(HAL_JSON));
|
||||
|
||||
assertThat(Jackson2HalModule.isAlreadyRegisteredIn(converter.getObjectMapper()))
|
||||
.isTrue();
|
||||
|
||||
+1
-1
@@ -54,7 +54,7 @@ import static org.mockito.Mockito.when;
|
||||
* {@link FeignBlockingLoadBalancerClient} and its delegates.
|
||||
*
|
||||
* @see <a href=
|
||||
* "https://github.com/spring-cloud/spring-cloud-commons/blob/master/spring-cloud-loadbalancer/src/test/java/org/springframework/cloud/loadbalancer/blocking/client/BlockingLoadBalancerClientTests.java">BlockingLoadBalancerClientTests</a>
|
||||
* "https://github.com/spring-cloud/spring-cloud-commons/blob/main/spring-cloud-loadbalancer/src/test/java/org/springframework/cloud/loadbalancer/blocking/client/BlockingLoadBalancerClientTests.java">BlockingLoadBalancerClientTests</a>
|
||||
* @author Olga Maciaszek-Sharma
|
||||
*/
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
|
||||
+35
-1
@@ -16,7 +16,10 @@
|
||||
|
||||
package org.springframework.cloud.openfeign.loadbalancer;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URI;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Collection;
|
||||
@@ -27,6 +30,7 @@ import java.util.Map;
|
||||
import feign.Client;
|
||||
import feign.Request;
|
||||
import feign.Response;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
@@ -58,7 +62,7 @@ import static org.mockito.Mockito.when;
|
||||
* {@link RetryableFeignBlockingLoadBalancerClient} and its delegates.
|
||||
*
|
||||
* @see <a href=
|
||||
* "https://github.com/spring-cloud/spring-cloud-commons/blob/master/spring-cloud-loadbalancer/src/test/java/org/springframework/cloud/loadbalancer/blocking/client/BlockingLoadBalancerClientTests.java">BlockingLoadBalancerClientTests</a>
|
||||
* "https://github.com/spring-cloud/spring-cloud-commons/blob/main/spring-cloud-loadbalancer/src/test/java/org/springframework/cloud/loadbalancer/blocking/client/BlockingLoadBalancerClientTests.java">BlockingLoadBalancerClientTests</a>
|
||||
* @author Olga Maciaszek-Sharma
|
||||
*/
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
@@ -113,6 +117,15 @@ class RetryableFeignBlockingLoadBalancerClientTests {
|
||||
return Response.builder().request(testRequest()).status(status).build();
|
||||
}
|
||||
|
||||
private Response testResponse(int status, String body) {
|
||||
// ByteArrayInputStream ignores close() and must be wrapped
|
||||
InputStream reallyCloseable = new BufferedInputStream(
|
||||
new ByteArrayInputStream(body.getBytes(StandardCharsets.UTF_8)));
|
||||
return Response.builder().request(testRequest()).status(status)
|
||||
.body(reallyCloseable, null).build();
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldExecuteOriginalRequestIfInstanceNotFound() throws IOException {
|
||||
Request request = testRequest();
|
||||
@@ -148,6 +161,27 @@ class RetryableFeignBlockingLoadBalancerClientTests {
|
||||
verify(delegate, times(2)).execute(any(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldExposeResponseBodyOnRetry() throws IOException {
|
||||
properties.getRetryableStatusCodes().add(503);
|
||||
Request request = testRequest();
|
||||
when(delegate.execute(any(), any())).thenReturn(testResponse(503, "foo"),
|
||||
testResponse(503, "foo"));
|
||||
when(retryFactory.createRetryPolicy(any(), eq(loadBalancerClient)))
|
||||
.thenReturn(new BlockingLoadBalancedRetryPolicy("test",
|
||||
loadBalancerClient, properties));
|
||||
when(loadBalancerClient.reconstructURI(serviceInstance,
|
||||
URI.create("http://test/path")))
|
||||
.thenReturn(URI.create("http://testhost:80/path"));
|
||||
|
||||
Response response = feignBlockingLoadBalancerClient.execute(request,
|
||||
new Request.Options());
|
||||
|
||||
String bodyContent = IOUtils
|
||||
.toString(response.body().asReader(StandardCharsets.UTF_8));
|
||||
assertThat(bodyContent).isEqualTo("foo");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldPassCorrectRequestToDelegate() throws IOException {
|
||||
Request request = testRequest();
|
||||
|
||||
+14
@@ -60,6 +60,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.springframework.http.HttpHeaders.ACCEPT;
|
||||
import static org.springframework.http.HttpHeaders.CONTENT_LENGTH;
|
||||
import static org.springframework.http.HttpHeaders.CONTENT_TYPE;
|
||||
import static org.springframework.http.MediaType.APPLICATION_FORM_URLENCODED_VALUE;
|
||||
import static org.springframework.http.MediaType.APPLICATION_OCTET_STREAM_VALUE;
|
||||
import static org.springframework.http.MediaType.MULTIPART_FORM_DATA_VALUE;
|
||||
import static org.springframework.http.MediaType.TEXT_PLAIN_VALUE;
|
||||
@@ -68,6 +69,7 @@ import static org.springframework.http.MediaType.TEXT_PLAIN_VALUE;
|
||||
* @author Spencer Gibb
|
||||
* @author Olga Maciaszek-Sharma
|
||||
* @author Ahmad Mozafarnia
|
||||
* @author Can Bezmen
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@SpringBootTest(classes = SpringEncoderTests.Application.class,
|
||||
@@ -192,6 +194,18 @@ public class SpringEncoderTests {
|
||||
.as("Body content cannot be decoded").contains("hi");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFromURLEncodedValue() {
|
||||
Encoder encoder = context.getInstance("formUrlEncoded", Encoder.class);
|
||||
assertThat(encoder).isNotNull();
|
||||
RequestTemplate request = new RequestTemplate();
|
||||
request.header(CONTENT_TYPE, APPLICATION_FORM_URLENCODED_VALUE);
|
||||
String body = "test";
|
||||
encoder.encode(body, String.class, request);
|
||||
assertThat(new String(request.requestBody().asBytes()))
|
||||
.as("Body content cannot be decoded").contains(body);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNoCharsetForBinaryFiles() {
|
||||
Encoder encoder = context.getInstance("test", Encoder.class);
|
||||
|
||||
+21
-59
@@ -62,6 +62,7 @@ import static com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility.ANY;
|
||||
import static com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility.NONE;
|
||||
import static feign.CollectionFormat.SSV;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
import static org.junit.Assume.assumeTrue;
|
||||
|
||||
/**
|
||||
@@ -182,18 +183,12 @@ public class SpringMvcContractTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testProcessAnnotations_Class_AnnotationsGetSpecificTest()
|
||||
throws Exception {
|
||||
Method method = TestTemplate_Class_Annotations.class
|
||||
.getDeclaredMethod("getSpecificTest", String.class, String.class);
|
||||
MethodMetadata data = contract
|
||||
.parseAndValidateMetadata(method.getDeclaringClass(), method);
|
||||
|
||||
assertThat(data.template().url()).isEqualTo("/prepend/{classId}/test/{testId}");
|
||||
assertThat(data.template().method()).isEqualTo("GET");
|
||||
|
||||
assertThat(data.indexToName().get(0).iterator().next()).isEqualTo("classId");
|
||||
assertThat(data.indexToName().get(1).iterator().next()).isEqualTo("testId");
|
||||
public void testProcessAnnotations_Class_Annotations_RequestMapping() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> {
|
||||
Method method = TestTemplate_Class_RequestMapping.class
|
||||
.getDeclaredMethod("getSpecificTest", String.class, String.class);
|
||||
contract.parseAndValidateMetadata(method.getDeclaringClass(), method);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -203,7 +198,7 @@ public class SpringMvcContractTests {
|
||||
MethodMetadata data = contract
|
||||
.parseAndValidateMetadata(method.getDeclaringClass(), method);
|
||||
|
||||
assertThat(data.template().url()).isEqualTo("/prepend/{classId}");
|
||||
assertThat(data.template().url()).isEqualTo("/");
|
||||
assertThat(data.template().method()).isEqualTo("GET");
|
||||
|
||||
assertThat(data.indexToName().get(0).iterator().next()).isEqualTo("classId");
|
||||
@@ -211,22 +206,6 @@ public class SpringMvcContractTests {
|
||||
assertThat(data.template().decodeSlash()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testProcessAnnotations_Class_AnnotationsGetAllTests_EncodeSlash()
|
||||
throws Exception {
|
||||
contract = new SpringMvcContract(Collections.emptyList(), getConversionService(),
|
||||
false);
|
||||
|
||||
Method method = TestTemplate_Class_Annotations.class
|
||||
.getDeclaredMethod("getAllTests", String.class);
|
||||
MethodMetadata data = contract
|
||||
.parseAndValidateMetadata(method.getDeclaringClass(), method);
|
||||
|
||||
assertThat(data.template().url()).isEqualTo("/prepend/{classId}");
|
||||
|
||||
assertThat(data.template().decodeSlash()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testProcessAnnotations_ExtendedInterface() throws Exception {
|
||||
Method extendedMethod = TestTemplate_Extended.class.getMethod("getAllTests",
|
||||
@@ -249,27 +228,6 @@ public class SpringMvcContractTests {
|
||||
assertThat(data.template().decodeSlash()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testProcessAnnotations_ExtendedInterface_EncodeSlash() throws Exception {
|
||||
contract = new SpringMvcContract(Collections.emptyList(), getConversionService(),
|
||||
false);
|
||||
|
||||
Method extendedMethod = TestTemplate_Extended.class.getMethod("getAllTests",
|
||||
String.class);
|
||||
MethodMetadata extendedData = contract.parseAndValidateMetadata(
|
||||
extendedMethod.getDeclaringClass(), extendedMethod);
|
||||
|
||||
Method method = TestTemplate_Class_Annotations.class
|
||||
.getDeclaredMethod("getAllTests", String.class);
|
||||
MethodMetadata data = contract
|
||||
.parseAndValidateMetadata(method.getDeclaringClass(), method);
|
||||
|
||||
assertThat(data.template().url()).isEqualTo(extendedData.template().url());
|
||||
assertThat(data.template().method()).isEqualTo(extendedData.template().method());
|
||||
|
||||
assertThat(data.template().decodeSlash()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testProcessAnnotations_SimplePost() throws Exception {
|
||||
Method method = TestTemplate_Simple.class.getDeclaredMethod("postTest",
|
||||
@@ -305,8 +263,7 @@ public class SpringMvcContractTests {
|
||||
MethodMetadata data = contract
|
||||
.parseAndValidateMetadata(method.getDeclaringClass(), method);
|
||||
|
||||
assertThat(data.template().url())
|
||||
.isEqualTo("/advanced/test/{id}?amount=" + "{amount}");
|
||||
assertThat(data.template().url()).isEqualTo("/test/{id}?amount=" + "{amount}");
|
||||
assertThat(data.template().method()).isEqualTo("PUT");
|
||||
assertThat(data.template().headers().get("Accept").iterator().next())
|
||||
.isEqualTo(MediaType.APPLICATION_JSON_VALUE);
|
||||
@@ -341,8 +298,7 @@ public class SpringMvcContractTests {
|
||||
MethodMetadata data = contract
|
||||
.parseAndValidateMetadata(method.getDeclaringClass(), method);
|
||||
|
||||
assertThat(data.template().url())
|
||||
.isEqualTo("/advanced/test/{id}?amount=" + "{amount}");
|
||||
assertThat(data.template().url()).isEqualTo("/test/{id}?amount=" + "{amount}");
|
||||
assertThat(data.template().method()).isEqualTo("PUT");
|
||||
assertThat(data.template().headers().get("Accept").iterator().next())
|
||||
.isEqualTo(MediaType.APPLICATION_JSON_VALUE);
|
||||
@@ -366,8 +322,7 @@ public class SpringMvcContractTests {
|
||||
MethodMetadata data = contract
|
||||
.parseAndValidateMetadata(method.getDeclaringClass(), method);
|
||||
|
||||
assertThat(data.template().url())
|
||||
.isEqualTo("/advanced/test2?amount=" + "{amount}");
|
||||
assertThat(data.template().url()).isEqualTo("/test2?amount=" + "{amount}");
|
||||
assertThat(data.template().method()).isEqualTo("PUT");
|
||||
assertThat(data.template().headers().get("Accept").iterator().next())
|
||||
.isEqualTo(MediaType.APPLICATION_JSON_VALUE);
|
||||
@@ -429,7 +384,7 @@ public class SpringMvcContractTests {
|
||||
MethodMetadata data = contract
|
||||
.parseAndValidateMetadata(method.getDeclaringClass(), method);
|
||||
|
||||
assertThat(data.template().url()).isEqualTo("/advanced");
|
||||
assertThat(data.template().url()).isEqualTo("/");
|
||||
assertThat(data.template().method()).isEqualTo("GET");
|
||||
assertThat(data.template().headers().get("Accept").iterator().next())
|
||||
.isEqualTo(MediaType.APPLICATION_JSON_VALUE);
|
||||
@@ -539,7 +494,7 @@ public class SpringMvcContractTests {
|
||||
.parseAndValidateMetadata(method.getDeclaringClass(), method);
|
||||
|
||||
assertThat(data.template().url())
|
||||
.isEqualTo("/advanced/testfallback/{id}?amount=" + "{amount}");
|
||||
.isEqualTo("/testfallback/{id}?amount=" + "{amount}");
|
||||
assertThat(data.template().method()).isEqualTo("PUT");
|
||||
assertThat(data.template().headers().get("Accept").iterator().next())
|
||||
.isEqualTo(MediaType.APPLICATION_JSON_VALUE);
|
||||
@@ -708,6 +663,14 @@ public class SpringMvcContractTests {
|
||||
}
|
||||
|
||||
@RequestMapping("/prepend/{classId}")
|
||||
public interface TestTemplate_Class_RequestMapping {
|
||||
|
||||
@RequestMapping(value = "/test/{testId}", method = RequestMethod.GET)
|
||||
TestObject getSpecificTest(@PathVariable("classId") String classId,
|
||||
@PathVariable("testId") String testId);
|
||||
|
||||
}
|
||||
|
||||
public interface TestTemplate_Class_Annotations {
|
||||
|
||||
@RequestMapping(value = "/test/{testId}", method = RequestMethod.GET)
|
||||
@@ -812,7 +775,6 @@ public class SpringMvcContractTests {
|
||||
}
|
||||
|
||||
@JsonAutoDetect
|
||||
@RequestMapping("/advanced")
|
||||
public interface TestTemplate_Advanced {
|
||||
|
||||
@CollectionFormat(SSV)
|
||||
|
||||
+87
-79
@@ -17,7 +17,6 @@
|
||||
package org.springframework.cloud.openfeign.valid;
|
||||
|
||||
import java.lang.reflect.InvocationHandler;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.text.ParseException;
|
||||
import java.time.LocalDate;
|
||||
@@ -49,7 +48,6 @@ import feign.Feign;
|
||||
import feign.Logger;
|
||||
import feign.RequestInterceptor;
|
||||
import feign.RequestTemplate;
|
||||
import feign.Target;
|
||||
import feign.codec.EncodeException;
|
||||
import feign.hystrix.FallbackFactory;
|
||||
import feign.hystrix.SetterFactory;
|
||||
@@ -91,6 +89,7 @@ import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.mock.web.MockMultipartFile;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestHeader;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
@@ -102,6 +101,7 @@ import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.hamcrest.core.IsInstanceOf.instanceOf;
|
||||
import static org.springframework.http.MediaType.APPLICATION_FORM_URLENCODED_VALUE;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
@@ -110,6 +110,7 @@ import static org.hamcrest.core.IsInstanceOf.instanceOf;
|
||||
* @author Halvdan Hoem Grelland
|
||||
* @author Aaron Whiteside
|
||||
* @author Darren Foong
|
||||
* @author Can Bezmen
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@SpringBootTest(classes = FeignClientTests.Application.class,
|
||||
@@ -178,23 +179,23 @@ public class FeignClientTests {
|
||||
|
||||
@Test
|
||||
public void testClient() {
|
||||
assertThat(this.testClient).as("testClient was null").isNotNull();
|
||||
assertThat(Proxy.isProxyClass(this.testClient.getClass()))
|
||||
assertThat(testClient).as("testClient was null").isNotNull();
|
||||
assertThat(Proxy.isProxyClass(testClient.getClass()))
|
||||
.as("testClient is not a java Proxy").isTrue();
|
||||
InvocationHandler invocationHandler = Proxy.getInvocationHandler(this.testClient);
|
||||
InvocationHandler invocationHandler = Proxy.getInvocationHandler(testClient);
|
||||
assertThat(invocationHandler).as("invocationHandler was null").isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRequestMappingClassLevelPropertyReplacement() {
|
||||
Hello hello = this.testClient.getHelloUsingPropertyPlaceHolder();
|
||||
Hello hello = testClient.getHelloUsingPropertyPlaceHolder();
|
||||
assertThat(hello).as("hello was null").isNotNull();
|
||||
assertThat(hello).as("first hello didn't match").isEqualTo(new Hello(OI_TERRA_2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSimpleType() {
|
||||
Hello hello = this.testClient.getHello();
|
||||
Hello hello = testClient.getHello();
|
||||
assertThat(hello).as("hello was null").isNotNull();
|
||||
assertThat(hello).as("first hello didn't match")
|
||||
.isEqualTo(new Hello(HELLO_WORLD_1));
|
||||
@@ -202,20 +203,20 @@ public class FeignClientTests {
|
||||
|
||||
@Test
|
||||
public void testOptional() {
|
||||
Optional<Hello> hello = this.testClient.getOptionalHello();
|
||||
Optional<Hello> hello = testClient.getOptionalHello();
|
||||
assertThat(hello).isNotNull().isPresent().contains(new Hello(HELLO_WORLD_1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGenericType() {
|
||||
List<Hello> hellos = this.testClient.getHellos();
|
||||
List<Hello> hellos = testClient.getHellos();
|
||||
assertThat(hellos).as("hellos was null").isNotNull();
|
||||
assertThat(getHelloList()).as("hellos didn't match").isEqualTo(hellos);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRequestInterceptors() {
|
||||
List<String> headers = this.testClient.getHelloHeaders();
|
||||
List<String> headers = testClient.getHelloHeaders();
|
||||
assertThat(headers).as("headers was null").isNotNull();
|
||||
assertThat(headers.contains("myheader1value"))
|
||||
.as("headers didn't contain myheader1value").isTrue();
|
||||
@@ -225,24 +226,23 @@ public class FeignClientTests {
|
||||
|
||||
@Test
|
||||
public void testHeaderPlaceholders() {
|
||||
String header = this.testClient.getHelloHeadersPlaceholders();
|
||||
String header = testClient.getHelloHeadersPlaceholders();
|
||||
assertThat(header).as("header was null").isNotNull();
|
||||
assertThat(header).as("header was wrong").isEqualTo("myPlaceholderHeaderValue");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFeignClientType() throws IllegalAccessException {
|
||||
assertThat(this.feignClient).isInstanceOf(LoadBalancerFeignClient.class);
|
||||
LoadBalancerFeignClient client = (LoadBalancerFeignClient) this.feignClient;
|
||||
public void testFeignClientType() {
|
||||
assertThat(feignClient).isInstanceOf(LoadBalancerFeignClient.class);
|
||||
LoadBalancerFeignClient client = (LoadBalancerFeignClient) feignClient;
|
||||
Client delegate = client.getDelegate();
|
||||
assertThat(delegate).isInstanceOf(Client.Default.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testServiceId() {
|
||||
assertThat(this.testClientServiceId).as("testClientServiceId was null")
|
||||
.isNotNull();
|
||||
final Hello hello = this.testClientServiceId.getHello();
|
||||
assertThat(testClientServiceId).as("testClientServiceId was null").isNotNull();
|
||||
final Hello hello = testClientServiceId.getHello();
|
||||
assertThat(hello).as("The hello response was null").isNotNull();
|
||||
assertThat(hello).as("first hello didn't match")
|
||||
.isEqualTo(new Hello(HELLO_WORLD_1));
|
||||
@@ -251,7 +251,7 @@ public class FeignClientTests {
|
||||
@Test
|
||||
public void testParams() {
|
||||
List<String> list = Arrays.asList("a", "1", "test");
|
||||
List<String> params = this.testClient.getParams(list);
|
||||
List<String> params = testClient.getParams(list);
|
||||
assertThat(params).as("params was null").isNotNull();
|
||||
assertThat(params.size()).as("params size was wrong").isEqualTo(list.size());
|
||||
}
|
||||
@@ -260,14 +260,14 @@ public class FeignClientTests {
|
||||
public void testFormattedParams() {
|
||||
List<LocalDate> list = Arrays.asList(LocalDate.of(2001, 1, 1),
|
||||
LocalDate.of(2018, 6, 10));
|
||||
List<LocalDate> params = this.testClient.getFormattedParams(list);
|
||||
List<LocalDate> params = testClient.getFormattedParams(list);
|
||||
assertThat(params).as("params was null").isNotNull();
|
||||
assertThat(params).as("params not converted correctly").isEqualTo(list);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHystrixCommand() throws NoSuchMethodException {
|
||||
HystrixCommand<List<Hello>> command = this.testClient.getHellosHystrix();
|
||||
HystrixCommand<List<Hello>> command = testClient.getHellosHystrix();
|
||||
assertThat(command).as("command was null").isNotNull();
|
||||
assertThat(command.getCommandGroup().name()).as(
|
||||
"Hystrix command group name should match the name of the feign client")
|
||||
@@ -284,7 +284,7 @@ public class FeignClientTests {
|
||||
|
||||
@Test
|
||||
public void testSingle() {
|
||||
Single<Hello> single = this.testClient.getHelloSingle();
|
||||
Single<Hello> single = testClient.getHelloSingle();
|
||||
assertThat(single).as("single was null").isNotNull();
|
||||
Hello hello = single.toBlocking().value();
|
||||
assertThat(hello).as("hello was null").isNotNull();
|
||||
@@ -294,7 +294,7 @@ public class FeignClientTests {
|
||||
|
||||
@Test
|
||||
public void testNoContentResponse() {
|
||||
ResponseEntity<Void> response = this.testClient.noContent();
|
||||
ResponseEntity<Void> response = testClient.noContent();
|
||||
assertThat(response).as("response was null").isNotNull();
|
||||
assertThat(response.getStatusCode()).as("status code was wrong")
|
||||
.isEqualTo(HttpStatus.NO_CONTENT);
|
||||
@@ -302,7 +302,7 @@ public class FeignClientTests {
|
||||
|
||||
@Test
|
||||
public void testHeadResponse() {
|
||||
ResponseEntity<Void> response = this.testClient.head();
|
||||
ResponseEntity<Void> response = testClient.head();
|
||||
assertThat(response).as("response was null").isNotNull();
|
||||
assertThat(response.getStatusCode()).as("status code was wrong")
|
||||
.isEqualTo(HttpStatus.OK);
|
||||
@@ -310,7 +310,7 @@ public class FeignClientTests {
|
||||
|
||||
@Test
|
||||
public void testHttpEntity() {
|
||||
HttpEntity<Hello> entity = this.testClient.getHelloEntity();
|
||||
HttpEntity<Hello> entity = testClient.getHelloEntity();
|
||||
assertThat(entity).as("entity was null").isNotNull();
|
||||
Hello hello = entity.getBody();
|
||||
assertThat(hello).as("hello was null").isNotNull();
|
||||
@@ -320,7 +320,7 @@ public class FeignClientTests {
|
||||
|
||||
@Test
|
||||
public void testMoreComplexHeader() {
|
||||
String response = this.testClient.moreComplexContentType("{\"value\":\"OK\"}");
|
||||
String response = testClient.moreComplexContentType("{\"value\":\"OK\"}");
|
||||
assertThat(response).as("response was null").isNotNull();
|
||||
assertThat(response).as("didn't respond with {\"value\":\"OK\"}")
|
||||
.isEqualTo("{\"value\":\"OK\"}");
|
||||
@@ -328,7 +328,7 @@ public class FeignClientTests {
|
||||
|
||||
@Test
|
||||
public void testDecodeNotFound() {
|
||||
ResponseEntity<String> response = this.decodingTestClient.notFound();
|
||||
ResponseEntity<String> response = decodingTestClient.notFound();
|
||||
assertThat(response).as("response was null").isNotNull();
|
||||
assertThat(response.getStatusCode()).as("status code was wrong")
|
||||
.isEqualTo(HttpStatus.NOT_FOUND);
|
||||
@@ -337,35 +337,35 @@ public class FeignClientTests {
|
||||
|
||||
@Test
|
||||
public void testOptionalNotFound() {
|
||||
Optional<String> s = this.decodingTestClient.optional();
|
||||
Optional<String> s = decodingTestClient.optional();
|
||||
assertThat(s).isNotPresent();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConvertingExpander() {
|
||||
assertThat(this.testClient.getToString(Arg.A)).isEqualTo(Arg.A.toString());
|
||||
assertThat(this.testClient.getToString(Arg.B)).isEqualTo(Arg.B.toString());
|
||||
assertThat(testClient.getToString(Arg.A)).isEqualTo(Arg.A.toString());
|
||||
assertThat(testClient.getToString(Arg.B)).isEqualTo(Arg.B.toString());
|
||||
|
||||
assertThat(this.testClient.getToString(new OtherArg("foo"))).isEqualTo("bar");
|
||||
assertThat(testClient.getToString(new OtherArg("foo"))).isEqualTo("bar");
|
||||
List<OtherArg> args = new ArrayList<>();
|
||||
args.add(new OtherArg("foo"));
|
||||
args.add(new OtherArg("goo"));
|
||||
List<String> expectedResult = new ArrayList<>();
|
||||
expectedResult.add("bar");
|
||||
expectedResult.add("goo");
|
||||
assertThat(this.testClient.getToString(args)).isEqualTo(expectedResult);
|
||||
assertThat(testClient.getToString(args)).isEqualTo(expectedResult);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHystrixFallbackWorks() {
|
||||
Hello hello = this.hystrixClient.fail();
|
||||
Hello hello = hystrixClient.fail();
|
||||
assertThat(hello).as("hello was null").isNotNull();
|
||||
assertThat(hello.getMessage()).as("message was wrong").isEqualTo("fallback");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHystrixFallbackSingle() {
|
||||
Single<Hello> single = this.hystrixClient.failSingle();
|
||||
Single<Hello> single = hystrixClient.failSingle();
|
||||
assertThat(single).as("single was null").isNotNull();
|
||||
Hello hello = single.toBlocking().value();
|
||||
assertThat(hello).as("hello was null").isNotNull();
|
||||
@@ -375,7 +375,7 @@ public class FeignClientTests {
|
||||
|
||||
@Test
|
||||
public void testHystrixFallbackCommand() {
|
||||
HystrixCommand<Hello> command = this.hystrixClient.failCommand();
|
||||
HystrixCommand<Hello> command = hystrixClient.failCommand();
|
||||
assertThat(command).as("command was null").isNotNull();
|
||||
Hello hello = command.execute();
|
||||
assertThat(hello).as("hello was null").isNotNull();
|
||||
@@ -385,7 +385,7 @@ public class FeignClientTests {
|
||||
|
||||
@Test
|
||||
public void testHystrixFallbackObservable() {
|
||||
Observable<Hello> observable = this.hystrixClient.failObservable();
|
||||
Observable<Hello> observable = hystrixClient.failObservable();
|
||||
assertThat(observable).as("observable was null").isNotNull();
|
||||
Hello hello = observable.toBlocking().first();
|
||||
assertThat(hello).as("hello was null").isNotNull();
|
||||
@@ -395,7 +395,7 @@ public class FeignClientTests {
|
||||
|
||||
@Test
|
||||
public void testHystrixFallbackFuture() throws Exception {
|
||||
Future<Hello> future = this.hystrixClient.failFuture();
|
||||
Future<Hello> future = hystrixClient.failFuture();
|
||||
assertThat(future).as("future was null").isNotNull();
|
||||
Hello hello = future.get(1, TimeUnit.SECONDS);
|
||||
assertThat(hello).as("hello was null").isNotNull();
|
||||
@@ -405,7 +405,7 @@ public class FeignClientTests {
|
||||
|
||||
@Test
|
||||
public void testHystrixClientWithFallBackFactory() throws Exception {
|
||||
Hello hello = this.hystrixClientWithFallBackFactory.fail();
|
||||
Hello hello = hystrixClientWithFallBackFactory.fail();
|
||||
assertThat(hello).as("hello was null").isNotNull();
|
||||
assertThat(hello.getMessage()).as("hello#message was null").isNotNull();
|
||||
assertThat(hello.getMessage().contains("500")).as(
|
||||
@@ -415,22 +415,29 @@ public class FeignClientTests {
|
||||
|
||||
@Test(expected = HystrixRuntimeException.class)
|
||||
public void testInvalidTypeHystrixFallbackFactory() throws Exception {
|
||||
this.invalidTypeHystrixClientWithFallBackFactory.fail();
|
||||
invalidTypeHystrixClientWithFallBackFactory.fail();
|
||||
}
|
||||
|
||||
@Test(expected = HystrixRuntimeException.class)
|
||||
public void testNullHystrixFallbackFactory() throws Exception {
|
||||
this.nullHystrixClientWithFallBackFactory.fail();
|
||||
nullHystrixClientWithFallBackFactory.fail();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFormURLEncoded() {
|
||||
Hello hello = new Hello(HELLO_WORLD_1);
|
||||
Hello response = testClient.postFormUrlEncoded(hello);
|
||||
assertThat(response).isEqualTo(hello);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void namedFeignClientWorks() {
|
||||
assertThat(this.namedHystrixClient).as("namedHystrixClient was null").isNotNull();
|
||||
assertThat(namedHystrixClient).as("namedHystrixClient was null").isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHystrixSetterFactory() {
|
||||
HystrixCommand<List<Hello>> command = this.hystrixSetterFactoryClient
|
||||
HystrixCommand<List<Hello>> command = hystrixSetterFactoryClient
|
||||
.getHellosHystrix();
|
||||
assertThat(command).as("command was null").isNotNull();
|
||||
String setterPrefix = TestHystrixSetterFactoryClientConfig.SETTER_PREFIX;
|
||||
@@ -449,13 +456,13 @@ public class FeignClientTests {
|
||||
|
||||
@Test
|
||||
public void testSingleRequestPart() {
|
||||
String response = this.multipartClient.singlePart("abc");
|
||||
String response = multipartClient.singlePart("abc");
|
||||
assertThat(response).isEqualTo("abc");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSinglePojoRequestPart() {
|
||||
String response = this.multipartClient.singlePojoPart(new Hello(HELLO_WORLD_1));
|
||||
String response = multipartClient.singlePojoPart(new Hello(HELLO_WORLD_1));
|
||||
assertThat(response).isEqualTo(HELLO_WORLD_1);
|
||||
}
|
||||
|
||||
@@ -463,7 +470,7 @@ public class FeignClientTests {
|
||||
public void testMultipleRequestParts() {
|
||||
MockMultipartFile file = new MockMultipartFile("file", "hello.bin", null,
|
||||
"hello".getBytes());
|
||||
String response = this.multipartClient.multipart("abc", "123", file);
|
||||
String response = multipartClient.multipart("abc", "123", file);
|
||||
assertThat(response).isEqualTo("abc123hello.bin");
|
||||
}
|
||||
|
||||
@@ -473,8 +480,7 @@ public class FeignClientTests {
|
||||
Hello pojo2 = new Hello(OI_TERRA_2);
|
||||
MockMultipartFile file = new MockMultipartFile("file", "hello.bin", null,
|
||||
"hello".getBytes());
|
||||
String response = this.multipartClient.multipartPojo("abc", "123", pojo1, pojo2,
|
||||
file);
|
||||
String response = multipartClient.multipartPojo("abc", "123", pojo1, pojo2, file);
|
||||
assertThat(response).isEqualTo("abc123hello world 1oi terra 2hello.bin");
|
||||
}
|
||||
|
||||
@@ -483,10 +489,10 @@ public class FeignClientTests {
|
||||
List<MultipartFile> multipartFiles = Arrays.asList(
|
||||
new MockMultipartFile("file1", "hello1.bin", null, "hello".getBytes()),
|
||||
new MockMultipartFile("file2", "hello2.bin", null, "hello".getBytes()));
|
||||
String partNames = this.multipartClient
|
||||
String partNames = multipartClient
|
||||
.requestPartListOfMultipartFilesReturnsPartNames(multipartFiles);
|
||||
assertThat(partNames).isEqualTo("files,files");
|
||||
String fileNames = this.multipartClient
|
||||
String fileNames = multipartClient
|
||||
.requestPartListOfMultipartFilesReturnsFileNames(multipartFiles);
|
||||
assertThat(fileNames).contains("hello1.bin", "hello2.bin");
|
||||
}
|
||||
@@ -499,9 +505,8 @@ public class FeignClientTests {
|
||||
"hello".getBytes());
|
||||
MockMultipartFile file2 = new MockMultipartFile("file2", "hello2.bin", null,
|
||||
"hello".getBytes());
|
||||
String response = this.multipartClient
|
||||
.requestPartListOfPojosAndListOfMultipartFiles(
|
||||
Arrays.asList(pojo1, pojo2), Arrays.asList(file1, file2));
|
||||
String response = multipartClient.requestPartListOfPojosAndListOfMultipartFiles(
|
||||
Arrays.asList(pojo1, pojo2), Arrays.asList(file1, file2));
|
||||
assertThat(response).isEqualTo("hello world 1oi terra 2hello1.binhello2.bin");
|
||||
}
|
||||
|
||||
@@ -510,7 +515,7 @@ public class FeignClientTests {
|
||||
String partName = UUID.randomUUID().toString();
|
||||
MockMultipartFile file1 = new MockMultipartFile(partName, "hello1.bin", null,
|
||||
"hello".getBytes());
|
||||
String response = this.multipartClient.requestBodySingleMultipartFile(file1);
|
||||
String response = multipartClient.requestBodySingleMultipartFile(file1);
|
||||
assertThat(response).isEqualTo(partName);
|
||||
}
|
||||
|
||||
@@ -520,7 +525,7 @@ public class FeignClientTests {
|
||||
"hello".getBytes());
|
||||
MockMultipartFile file2 = new MockMultipartFile("file2", "hello2.bin", null,
|
||||
"hello".getBytes());
|
||||
String response = this.multipartClient
|
||||
String response = multipartClient
|
||||
.requestBodyListOfMultipartFiles(Arrays.asList(file1, file2));
|
||||
assertThat(response).contains("file1", "file2");
|
||||
}
|
||||
@@ -535,7 +540,7 @@ public class FeignClientTests {
|
||||
form.put("file1", file1);
|
||||
form.put("file2", file2);
|
||||
form.put("hello", "world");
|
||||
String response = this.multipartClient.requestBodyMap(form);
|
||||
String response = multipartClient.requestBodyMap(form);
|
||||
assertThat(response).contains("file1", "file2", "hello");
|
||||
}
|
||||
|
||||
@@ -544,7 +549,7 @@ public class FeignClientTests {
|
||||
MockMultipartFile file = new MockMultipartFile("file1", "hello1.bin", null,
|
||||
"hello".getBytes());
|
||||
expected.expectCause(instanceOf(EncodeException.class));
|
||||
this.multipartClient.invalid(file);
|
||||
multipartClient.invalid(file);
|
||||
}
|
||||
|
||||
protected enum Arg {
|
||||
@@ -615,6 +620,10 @@ public class FeignClientTests {
|
||||
@RequestMapping(method = RequestMethod.GET, path = "/tostring")
|
||||
String getToString(@RequestParam("arg") Arg arg);
|
||||
|
||||
@PostMapping(path = "/form-urlencoded",
|
||||
consumes = APPLICATION_FORM_URLENCODED_VALUE)
|
||||
Hello postFormUrlEncoded(Hello hello);
|
||||
|
||||
@RequestMapping(method = RequestMethod.GET, path = "/tostring2")
|
||||
String getToString(@RequestParam("arg") OtherArg arg);
|
||||
|
||||
@@ -776,7 +785,7 @@ public class FeignClientTests {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.value;
|
||||
return value;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -810,13 +819,9 @@ public class FeignClientTests {
|
||||
|
||||
@Override
|
||||
public HystrixClientWithFallBackFactory create(final Throwable cause) {
|
||||
return new HystrixClientWithFallBackFactory() {
|
||||
@Override
|
||||
public Hello fail() {
|
||||
assertThat(cause).isNotNull().as("Cause was null");
|
||||
return new Hello(
|
||||
"Hello from the fallback side: " + cause.getMessage());
|
||||
}
|
||||
return () -> {
|
||||
assertThat(cause).isNotNull().as("Cause was null");
|
||||
return new Hello("Hello from the fallback side: " + cause.getMessage());
|
||||
};
|
||||
}
|
||||
|
||||
@@ -876,18 +881,15 @@ public class FeignClientTests {
|
||||
|
||||
@Bean
|
||||
public SetterFactory commandKeyIsRequestLineSetterFactory() {
|
||||
return new SetterFactory() {
|
||||
@Override
|
||||
public HystrixCommand.Setter create(Target<?> target, Method method) {
|
||||
String groupKey = SETTER_PREFIX + target.name();
|
||||
RequestMapping requestMapping = method
|
||||
.getAnnotation(RequestMapping.class);
|
||||
String commandKey = SETTER_PREFIX + requestMapping.method()[0] + " "
|
||||
+ requestMapping.path()[0];
|
||||
return HystrixCommand.Setter
|
||||
.withGroupKey(HystrixCommandGroupKey.Factory.asKey(groupKey))
|
||||
.andCommandKey(HystrixCommandKey.Factory.asKey(commandKey));
|
||||
}
|
||||
return (target, method) -> {
|
||||
String groupKey = SETTER_PREFIX + target.name();
|
||||
RequestMapping requestMapping = method
|
||||
.getAnnotation(RequestMapping.class);
|
||||
String commandKey = SETTER_PREFIX + requestMapping.method()[0] + " "
|
||||
+ requestMapping.path()[0];
|
||||
return HystrixCommand.Setter
|
||||
.withGroupKey(HystrixCommandGroupKey.Factory.asKey(groupKey))
|
||||
.andCommandKey(HystrixCommandKey.Factory.asKey(commandKey));
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1154,6 +1156,12 @@ public class FeignClientTests {
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
@PostMapping(path = "/form-urlencoded",
|
||||
consumes = APPLICATION_FORM_URLENCODED_VALUE)
|
||||
Hello postFormUrlEncoded(Hello hello) {
|
||||
return hello;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class Hello {
|
||||
@@ -1168,7 +1176,7 @@ public class FeignClientTests {
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return this.message;
|
||||
return message;
|
||||
}
|
||||
|
||||
public void setMessage(String message) {
|
||||
@@ -1184,12 +1192,12 @@ public class FeignClientTests {
|
||||
return false;
|
||||
}
|
||||
Hello that = (Hello) o;
|
||||
return Objects.equals(this.message, that.message);
|
||||
return Objects.equals(message, that.message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(this.message);
|
||||
return Objects.hash(message);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1213,7 +1221,7 @@ public class FeignClientTests {
|
||||
|
||||
@Bean
|
||||
public ServerList<Server> ribbonServerList() {
|
||||
return new StaticServerList<>(new Server("localhost", this.port));
|
||||
return new StaticServerList<>(new Server("localhost", port));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -6,16 +6,16 @@
|
||||
<parent>
|
||||
<artifactId>spring-cloud-dependencies-parent</artifactId>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<version>2.3.3.BUILD-SNAPSHOT</version>
|
||||
<version>2.3.6.BUILD-SNAPSHOT</version>
|
||||
<relativePath/>
|
||||
</parent>
|
||||
<artifactId>spring-cloud-openfeign-dependencies</artifactId>
|
||||
<version>2.2.8.BUILD-SNAPSHOT</version>
|
||||
<version>2.2.11.BUILD-SNAPSHOT</version>
|
||||
<packaging>pom</packaging>
|
||||
<name>spring-cloud-openfeign-dependencies</name>
|
||||
<description>Spring Cloud OpenFeign Dependencies</description>
|
||||
<properties>
|
||||
<feign.version>10.10.1</feign.version>
|
||||
<feign.version>10.12</feign.version>
|
||||
<feign-form.version>3.8.0</feign-form.version>
|
||||
</properties>
|
||||
<dependencyManagement>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-openfeign</artifactId>
|
||||
<version>2.2.8.BUILD-SNAPSHOT</version>
|
||||
<version>2.2.11.BUILD-SNAPSHOT</version>
|
||||
<relativePath>..</relativePath>
|
||||
</parent>
|
||||
<artifactId>spring-cloud-starter-openfeign</artifactId>
|
||||
|
||||
Reference in New Issue
Block a user