Compare commits
23
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fb6676d634 | ||
|
|
c1bbb9cbb3 | ||
|
|
e65f78f06b | ||
|
|
a1a45125ea | ||
|
|
b00abe7b68 | ||
|
|
b9cb792820 | ||
|
|
a38ebe40c8 | ||
|
|
a064530141 | ||
|
|
61afb5dcd6 | ||
|
|
20017afdca | ||
|
|
f267c2255b | ||
|
|
dd7b869a12 | ||
|
|
d46fb3a92e | ||
|
|
cc2fe823c3 | ||
|
|
f1975e5200 | ||
|
|
e85e74e311 | ||
|
|
71675455a3 | ||
|
|
a39e6316b5 | ||
|
|
3e3e486648 | ||
|
|
15bda96e91 | ||
|
|
52bea35160 | ||
|
|
7148cce790 | ||
|
|
88fae41f02 |
+1
-1
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-openfeign</artifactId>
|
||||
<version>2.0.3.BUILD-SNAPSHOT</version>
|
||||
<version>2.1.0.RC3</version>
|
||||
</parent>
|
||||
<artifactId>spring-cloud-openfeign-docs</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
@@ -425,4 +425,38 @@ public class FooConfiguration {
|
||||
return Logger.Level.FULL;
|
||||
}
|
||||
}
|
||||
----
|
||||
----
|
||||
|
||||
=== Feign @QueryMap support
|
||||
|
||||
The OpenFeign `@QueryMap` annotation provides support for POJOs to be used as
|
||||
GET parameter maps. Unfortunately, the default OpenFeign QueryMap annotation is
|
||||
incompatible with Spring because it lacks a `value` property.
|
||||
|
||||
Spring Cloud OpenFeign provides an equivalent `@SpringQueryMap` annotation, which
|
||||
is used to annotate a POJO or Map parameter as a query parameter map.
|
||||
|
||||
For example, the `Params` class defines parameters `param1` and `param2`:
|
||||
|
||||
[source,java,indent=0]
|
||||
----
|
||||
// Params.java
|
||||
public class Params {
|
||||
private String param1;
|
||||
private String param2;
|
||||
|
||||
// [Getters and setters omitted for brevity]
|
||||
}
|
||||
----
|
||||
|
||||
The following feign client uses the `Params` class by using the `@SpringQueryMap` annotation:
|
||||
|
||||
[source,java,indent=0]
|
||||
----
|
||||
@FeignClient("demo")
|
||||
public class DemoTemplate {
|
||||
|
||||
@GetMapping(path = "/demo")
|
||||
String demoEndpoint(@SpringQueryMap Params params);
|
||||
}
|
||||
----
|
||||
|
||||
@@ -3,14 +3,14 @@
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>spring-cloud-openfeign</artifactId>
|
||||
<version>2.0.3.BUILD-SNAPSHOT</version>
|
||||
<version>2.1.0.RC3</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.0.4.RELEASE</version>
|
||||
<version>2.1.0.RC3</version>
|
||||
<relativePath />
|
||||
</parent>
|
||||
<scm>
|
||||
@@ -22,8 +22,8 @@
|
||||
<properties>
|
||||
<main.basedir>${basedir}</main.basedir>
|
||||
<jackson.version>2.7.3</jackson.version>
|
||||
<spring-cloud-commons.version>2.0.2.BUILD-SNAPSHOT</spring-cloud-commons.version>
|
||||
<spring-cloud-netflix.version>2.0.2.BUILD-SNAPSHOT</spring-cloud-netflix.version>
|
||||
<spring-cloud-commons.version>2.1.0.RC2</spring-cloud-commons.version>
|
||||
<spring-cloud-netflix.version>2.1.0.RC3</spring-cloud-netflix.version>
|
||||
|
||||
<!-- Plugin versions -->
|
||||
<maven-compiler-plugin.version>3.6.1</maven-compiler-plugin.version>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-openfeign</artifactId>
|
||||
<version>2.0.3.BUILD-SNAPSHOT</version>
|
||||
<version>2.1.0.RC3</version>
|
||||
<relativePath>..</relativePath> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<artifactId>spring-cloud-openfeign-core</artifactId>
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2015 the original author or authors.
|
||||
* Copyright 2013-2018 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.
|
||||
@@ -82,7 +82,7 @@ public @interface EnableFeignClients {
|
||||
|
||||
/**
|
||||
* List of classes annotated with @FeignClient. If not empty, disables classpath scanning.
|
||||
* @return
|
||||
* @return list of FeignClient classes
|
||||
*/
|
||||
Class<?>[] clients() default {};
|
||||
}
|
||||
|
||||
+1
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2016 the original author or authors.
|
||||
* Copyright 2013-2018 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.
|
||||
@@ -17,8 +17,6 @@
|
||||
|
||||
package org.springframework.cloud.openfeign;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import feign.Feign;
|
||||
import feign.Target;
|
||||
import feign.hystrix.FallbackFactory;
|
||||
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright 2013-2018 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
|
||||
*
|
||||
* http://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 feign.QueryMap;
|
||||
import org.springframework.core.annotation.AliasFor;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Spring MVC equivalent of OpenFeign's {@link feign.QueryMap} parameter annotation.
|
||||
*
|
||||
* @author Aram Peres
|
||||
* @see feign.QueryMap
|
||||
* @see org.springframework.cloud.openfeign.annotation.QueryMapParameterProcessor
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.PARAMETER})
|
||||
public @interface SpringQueryMap {
|
||||
|
||||
/**
|
||||
* Alias for {@link #encoded()}.
|
||||
*
|
||||
* @see QueryMap#encoded()
|
||||
*/
|
||||
@AliasFor("encoded")
|
||||
boolean value() default false;
|
||||
|
||||
/**
|
||||
* Specifies whether parameter names and values are already encoded.
|
||||
*
|
||||
* @see QueryMap#encoded()
|
||||
*/
|
||||
@AliasFor("value")
|
||||
boolean encoded() default false;
|
||||
}
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright 2013-2018 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
|
||||
*
|
||||
* http://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.annotation;
|
||||
|
||||
import feign.MethodMetadata;
|
||||
import org.springframework.cloud.openfeign.AnnotatedParameterProcessor;
|
||||
import org.springframework.cloud.openfeign.SpringQueryMap;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* {@link SpringQueryMap} parameter processor.
|
||||
*
|
||||
* @author Aram Peres
|
||||
* @see AnnotatedParameterProcessor
|
||||
*/
|
||||
public class QueryMapParameterProcessor implements AnnotatedParameterProcessor {
|
||||
|
||||
private static final Class<SpringQueryMap> ANNOTATION = SpringQueryMap.class;
|
||||
|
||||
@Override
|
||||
public Class<? extends Annotation> getAnnotationType() {
|
||||
return ANNOTATION;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean processArgument(AnnotatedParameterContext context, Annotation annotation, Method method) {
|
||||
int paramIndex = context.getParameterIndex();
|
||||
MethodMetadata metadata = context.getMethodMetadata();
|
||||
if (metadata.queryMapIndex() == null) {
|
||||
metadata.queryMapIndex(paramIndex);
|
||||
metadata.queryMapEncoded(SpringQueryMap.class.cast(annotation).encoded());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -36,8 +36,8 @@ import com.netflix.loadbalancer.ILoadBalancer;
|
||||
*/
|
||||
public class CachingSpringLoadBalancerFactory {
|
||||
|
||||
private final SpringClientFactory factory;
|
||||
private LoadBalancedRetryFactory loadBalancedRetryFactory = null;
|
||||
protected final SpringClientFactory factory;
|
||||
protected LoadBalancedRetryFactory loadBalancedRetryFactory = null;
|
||||
|
||||
private volatile Map<String, FeignLoadBalancer> cache = new ConcurrentReferenceHashMap<>();
|
||||
|
||||
|
||||
+25
-15
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2015 the original author or authors.
|
||||
* Copyright 2015-2018 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,10 +16,6 @@
|
||||
|
||||
package org.springframework.cloud.openfeign.ribbon;
|
||||
|
||||
import feign.Client;
|
||||
import feign.Request;
|
||||
import feign.Response;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
@@ -29,11 +25,6 @@ import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonProperties;
|
||||
import org.springframework.cloud.netflix.ribbon.ServerIntrospector;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpRequest;
|
||||
import com.netflix.client.AbstractLoadBalancerAwareClient;
|
||||
import com.netflix.client.ClientException;
|
||||
import com.netflix.client.ClientRequest;
|
||||
@@ -43,6 +34,15 @@ import com.netflix.client.RetryHandler;
|
||||
import com.netflix.client.config.IClientConfig;
|
||||
import com.netflix.loadbalancer.ILoadBalancer;
|
||||
import com.netflix.loadbalancer.Server;
|
||||
import feign.Client;
|
||||
import feign.Request;
|
||||
import feign.Response;
|
||||
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonProperties;
|
||||
import org.springframework.cloud.netflix.ribbon.ServerIntrospector;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpRequest;
|
||||
|
||||
import static org.springframework.cloud.netflix.ribbon.RibbonUtils.updateToSecureConnectionIfNeeded;
|
||||
|
||||
@@ -51,6 +51,7 @@ import static org.springframework.cloud.netflix.ribbon.RibbonUtils.updateToSecur
|
||||
* @author Spencer Gibb
|
||||
* @author Ryan Baxter
|
||||
* @author Tim Ysewyn
|
||||
* @author Olga Maciaszek-Sharma
|
||||
*/
|
||||
public class FeignLoadBalancer extends
|
||||
AbstractLoadBalancerAwareClient<FeignLoadBalancer.RibbonRequest, FeignLoadBalancer.RibbonResponse> {
|
||||
@@ -97,7 +98,7 @@ public class FeignLoadBalancer extends
|
||||
return new RequestSpecificRetryHandler(true, true, this.getRetryHandler(),
|
||||
requestConfig);
|
||||
}
|
||||
if (!request.toRequest().method().equals("GET")) {
|
||||
if (!request.toRequest().httpMethod().name().equals("GET")) {
|
||||
return new RequestSpecificRetryHandler(true, false, this.getRetryHandler(),
|
||||
requestConfig);
|
||||
}
|
||||
@@ -118,7 +119,7 @@ public class FeignLoadBalancer extends
|
||||
private final Request request;
|
||||
private final Client client;
|
||||
|
||||
RibbonRequest(Client client, Request request, URI uri) {
|
||||
protected RibbonRequest(Client client, Request request, URI uri) {
|
||||
this.client = client;
|
||||
setUri(uri);
|
||||
this.request = toRequest(request);
|
||||
@@ -127,7 +128,8 @@ public class FeignLoadBalancer extends
|
||||
private Request toRequest(Request request) {
|
||||
Map<String, Collection<String>> headers = new LinkedHashMap<>(
|
||||
request.headers());
|
||||
return Request.create(request.method(),getUri().toASCIIString(),headers,request.body(),request.charset());
|
||||
return Request.create(request.httpMethod(), getUri().toASCIIString(), headers,
|
||||
request.requestBody());
|
||||
}
|
||||
|
||||
Request toRequest() {
|
||||
@@ -142,7 +144,8 @@ public class FeignLoadBalancer extends
|
||||
return new HttpRequest() {
|
||||
@Override
|
||||
public HttpMethod getMethod() {
|
||||
return HttpMethod.resolve(RibbonRequest.this.toRequest().method());
|
||||
return HttpMethod
|
||||
.resolve(RibbonRequest.this.toRequest().httpMethod().name());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -170,6 +173,13 @@ public class FeignLoadBalancer extends
|
||||
};
|
||||
}
|
||||
|
||||
public Request getRequest() {
|
||||
return request;
|
||||
}
|
||||
|
||||
public Client getClient() {
|
||||
return client;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object clone() {
|
||||
@@ -182,7 +192,7 @@ public class FeignLoadBalancer extends
|
||||
private final URI uri;
|
||||
private final Response response;
|
||||
|
||||
RibbonResponse(URI uri, Response response) {
|
||||
protected RibbonResponse(URI uri, Response response) {
|
||||
this.uri = uri;
|
||||
this.response = response;
|
||||
}
|
||||
|
||||
+3
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2015 the original author or authors.
|
||||
* Copyright 2015-2018 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.
|
||||
@@ -30,6 +30,8 @@ import feign.Client;
|
||||
import feign.Request;
|
||||
import feign.Response;
|
||||
|
||||
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
|
||||
+3
-4
@@ -15,13 +15,12 @@
|
||||
*/
|
||||
package org.springframework.cloud.openfeign.ribbon;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.net.URI;
|
||||
|
||||
import feign.Response;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import org.springframework.cloud.client.loadbalancer.RetryableStatusCodeException;
|
||||
import org.springframework.util.StreamUtils;
|
||||
|
||||
/**
|
||||
* A {@link RetryableStatusCodeException} for {@link Response}s
|
||||
|
||||
+11
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2015 the original author or authors.
|
||||
* Copyright 2013-2018 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.
|
||||
@@ -24,8 +24,11 @@ import java.util.Map;
|
||||
|
||||
import org.springframework.http.HttpHeaders;
|
||||
|
||||
import static java.util.Optional.ofNullable;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
* @author Olga Maciaszek-Sharma
|
||||
*/
|
||||
public class FeignUtils {
|
||||
|
||||
@@ -47,4 +50,11 @@ public class FeignUtils {
|
||||
return headers;
|
||||
}
|
||||
|
||||
static Collection<String> addTemplateParameter(Collection<String> possiblyNull,
|
||||
String paramName) {
|
||||
Collection<String> params = ofNullable(possiblyNull).orElse(new ArrayList<>());
|
||||
params.add(String.format("{%s}", paramName));
|
||||
return params;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+9
-7
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
* Copyright 2013-2018 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.
|
||||
@@ -26,8 +26,14 @@ import java.nio.charset.StandardCharsets;
|
||||
import java.util.Collection;
|
||||
import java.util.Objects;
|
||||
|
||||
import feign.Request;
|
||||
import feign.RequestTemplate;
|
||||
import feign.codec.EncodeException;
|
||||
import feign.codec.Encoder;
|
||||
import feign.form.spring.SpringFormEncoder;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.factory.ObjectFactory;
|
||||
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
@@ -38,11 +44,6 @@ import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.http.converter.protobuf.ProtobufHttpMessageConverter;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import feign.RequestTemplate;
|
||||
import feign.codec.EncodeException;
|
||||
import feign.codec.Encoder;
|
||||
import feign.form.spring.SpringFormEncoder;
|
||||
|
||||
import static org.springframework.cloud.openfeign.support.FeignUtils.getHeaders;
|
||||
import static org.springframework.cloud.openfeign.support.FeignUtils.getHttpHeaders;
|
||||
|
||||
@@ -129,7 +130,8 @@ public class SpringEncoder implements Encoder {
|
||||
} else {
|
||||
charset = StandardCharsets.UTF_8;
|
||||
}
|
||||
request.body(outputMessage.getOutputStream().toByteArray(), charset);
|
||||
request.body(Request.Body.encoded(outputMessage.getOutputStream()
|
||||
.toByteArray(), charset));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
+79
-17
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2016 the original author or authors.
|
||||
* Copyright 2013-2018 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.
|
||||
@@ -18,6 +18,7 @@ package org.springframework.cloud.openfeign.support;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Parameter;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
@@ -28,16 +29,25 @@ import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import feign.Contract;
|
||||
import feign.Feign;
|
||||
import feign.MethodMetadata;
|
||||
import feign.Param;
|
||||
import feign.Request;
|
||||
|
||||
import org.springframework.cloud.openfeign.AnnotatedParameterProcessor;
|
||||
import org.springframework.cloud.openfeign.annotation.PathVariableParameterProcessor;
|
||||
import org.springframework.cloud.openfeign.annotation.QueryMapParameterProcessor;
|
||||
import org.springframework.cloud.openfeign.annotation.RequestHeaderParameterProcessor;
|
||||
import org.springframework.cloud.openfeign.annotation.RequestParamParameterProcessor;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.ResourceLoaderAware;
|
||||
import org.springframework.core.DefaultParameterNameDiscoverer;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.ParameterNameDiscoverer;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
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;
|
||||
@@ -48,16 +58,15 @@ import org.springframework.web.bind.annotation.RequestMethod;
|
||||
|
||||
import static feign.Util.checkState;
|
||||
import static feign.Util.emptyToNull;
|
||||
import static org.springframework.cloud.openfeign.support.FeignUtils.addTemplateParameter;
|
||||
import static org.springframework.core.annotation.AnnotatedElementUtils.findMergedAnnotation;
|
||||
|
||||
import feign.Contract;
|
||||
import feign.Feign;
|
||||
import feign.MethodMetadata;
|
||||
import feign.Param;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
* @author Abhijit Sarkar
|
||||
* @author Halvdan Hoem Grelland
|
||||
* @author Aram Peres
|
||||
* @author Olga Maciaszek-Sharma
|
||||
*/
|
||||
public class SpringMvcContract extends Contract.BaseContract
|
||||
implements ResourceLoaderAware {
|
||||
@@ -66,13 +75,18 @@ public class SpringMvcContract extends Contract.BaseContract
|
||||
|
||||
private static final String CONTENT_TYPE = "Content-Type";
|
||||
|
||||
private static final TypeDescriptor STRING_TYPE_DESCRIPTOR =
|
||||
TypeDescriptor.valueOf(String.class);
|
||||
private static final TypeDescriptor ITERABLE_TYPE_DESCRIPTOR =
|
||||
TypeDescriptor.valueOf(Iterable.class);
|
||||
|
||||
private static final ParameterNameDiscoverer PARAMETER_NAME_DISCOVERER = new DefaultParameterNameDiscoverer();
|
||||
|
||||
private final Map<Class<? extends Annotation>, AnnotatedParameterProcessor> annotatedArgumentProcessors;
|
||||
private final Map<String, Method> processedMethods = new HashMap<>();
|
||||
|
||||
private final ConversionService conversionService;
|
||||
private final Param.Expander expander;
|
||||
private final ConvertingExpanderFactory convertingExpanderFactory;
|
||||
private ResourceLoader resourceLoader = new DefaultResourceLoader();
|
||||
|
||||
public SpringMvcContract() {
|
||||
@@ -100,7 +114,7 @@ public class SpringMvcContract extends Contract.BaseContract
|
||||
}
|
||||
this.annotatedArgumentProcessors = toAnnotatedArgumentProcessorMap(processors);
|
||||
this.conversionService = conversionService;
|
||||
this.expander = new ConvertingExpander(conversionService);
|
||||
this.convertingExpanderFactory = new ConvertingExpanderFactory(conversionService);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -121,7 +135,7 @@ public class SpringMvcContract extends Contract.BaseContract
|
||||
if (!pathValue.startsWith("/")) {
|
||||
pathValue = "/" + pathValue;
|
||||
}
|
||||
data.template().insert(0, pathValue);
|
||||
data.template().uri(pathValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -167,7 +181,7 @@ public class SpringMvcContract extends Contract.BaseContract
|
||||
methods = new RequestMethod[] { RequestMethod.GET };
|
||||
}
|
||||
checkOne(method, methods, "method");
|
||||
data.template().method(methods[0].name());
|
||||
data.template().method(Request.HttpMethod.valueOf(methods[0].name()));
|
||||
|
||||
// path
|
||||
checkAtMostOne(method, methodMapping.value(), "value");
|
||||
@@ -177,10 +191,10 @@ public class SpringMvcContract extends Contract.BaseContract
|
||||
pathValue = resolve(pathValue);
|
||||
// Append path from @RequestMapping if value is present on method
|
||||
if (!pathValue.startsWith("/")
|
||||
&& !data.template().toString().endsWith("/")) {
|
||||
&& !data.template().path().endsWith("/")) {
|
||||
pathValue = "/" + pathValue;
|
||||
}
|
||||
data.template().append(pathValue);
|
||||
data.template().uri(pathValue, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -239,14 +253,40 @@ public class SpringMvcContract extends Contract.BaseContract
|
||||
processParameterAnnotation, method);
|
||||
}
|
||||
}
|
||||
if (isHttpAnnotation && data.indexToExpander().get(paramIndex) == null
|
||||
&& this.conversionService.canConvert(
|
||||
method.getParameterTypes()[paramIndex], String.class)) {
|
||||
data.indexToExpander().put(paramIndex, this.expander);
|
||||
|
||||
if (isHttpAnnotation && data.indexToExpander().get(paramIndex) == null) {
|
||||
TypeDescriptor typeDescriptor = createTypeDescriptor(method, paramIndex);
|
||||
if (conversionService.canConvert(typeDescriptor, STRING_TYPE_DESCRIPTOR)) {
|
||||
Param.Expander expander =
|
||||
convertingExpanderFactory.getExpander(typeDescriptor);
|
||||
if (expander != null) {
|
||||
data.indexToExpander().put(paramIndex, expander);
|
||||
}
|
||||
}
|
||||
}
|
||||
return isHttpAnnotation;
|
||||
}
|
||||
|
||||
private static TypeDescriptor createTypeDescriptor(Method method, int paramIndex) {
|
||||
Parameter parameter = method.getParameters()[paramIndex];
|
||||
MethodParameter methodParameter = MethodParameter.forParameter(parameter);
|
||||
TypeDescriptor typeDescriptor = new TypeDescriptor(methodParameter);
|
||||
|
||||
// Feign applies the Param.Expander to each element of an Iterable, so in those
|
||||
// cases we need to provide a TypeDescriptor of the element.
|
||||
if (typeDescriptor.isAssignableTo(ITERABLE_TYPE_DESCRIPTOR)) {
|
||||
TypeDescriptor elementTypeDescriptor =
|
||||
typeDescriptor.getElementTypeDescriptor();
|
||||
|
||||
checkState(elementTypeDescriptor != null,
|
||||
"Could not resolve element type of Iterable type %s. Not declared?",
|
||||
typeDescriptor);
|
||||
|
||||
typeDescriptor = elementTypeDescriptor;
|
||||
}
|
||||
return typeDescriptor;
|
||||
}
|
||||
|
||||
private void parseProduces(MethodMetadata md, Method method,
|
||||
RequestMapping annotation) {
|
||||
String[] serverProduces = annotation.produces();
|
||||
@@ -297,6 +337,7 @@ public class SpringMvcContract extends Contract.BaseContract
|
||||
annotatedArgumentResolvers.add(new PathVariableParameterProcessor());
|
||||
annotatedArgumentResolvers.add(new RequestParamParameterProcessor());
|
||||
annotatedArgumentResolvers.add(new RequestHeaderParameterProcessor());
|
||||
annotatedArgumentResolvers.add(new QueryMapParameterProcessor());
|
||||
|
||||
return annotatedArgumentResolvers;
|
||||
}
|
||||
@@ -357,10 +398,14 @@ public class SpringMvcContract extends Contract.BaseContract
|
||||
@Override
|
||||
public Collection<String> setTemplateParameter(String name,
|
||||
Collection<String> rest) {
|
||||
return addTemplatedParam(rest, name);
|
||||
return addTemplateParameter(rest, name);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Not used internally anymore. Will be removed in the future.
|
||||
*/
|
||||
@Deprecated
|
||||
public static class ConvertingExpander implements Param.Expander {
|
||||
|
||||
private final ConversionService conversionService;
|
||||
@@ -375,4 +420,21 @@ public class SpringMvcContract extends Contract.BaseContract
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class ConvertingExpanderFactory {
|
||||
|
||||
private final ConversionService conversionService;
|
||||
|
||||
ConvertingExpanderFactory(ConversionService conversionService) {
|
||||
this.conversionService = conversionService;
|
||||
}
|
||||
|
||||
Param.Expander getExpander(TypeDescriptor typeDescriptor) {
|
||||
return value -> {
|
||||
Object converted = this.conversionService.convert(
|
||||
value, typeDescriptor, STRING_TYPE_DESCRIPTOR);
|
||||
return (String) converted;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+13
-6
@@ -16,6 +16,13 @@
|
||||
|
||||
package org.springframework.cloud.openfeign;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import feign.Request;
|
||||
import feign.RequestInterceptor;
|
||||
import feign.RequestTemplate;
|
||||
import feign.RetryableException;
|
||||
@@ -25,13 +32,16 @@ import feign.codec.Encoder;
|
||||
import feign.codec.ErrorDecoder;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
|
||||
import org.springframework.cloud.openfeign.test.NoSecurityConfiguration;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
@@ -41,11 +51,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
@@ -144,6 +150,7 @@ public class FeignClientUsingPropertiesTests {
|
||||
@Configuration
|
||||
@EnableAutoConfiguration
|
||||
@RestController
|
||||
@Import(NoSecurityConfiguration.class)
|
||||
protected static class Application {
|
||||
|
||||
@RequestMapping(method = RequestMethod.GET, value = "/foo")
|
||||
@@ -211,7 +218,7 @@ public class FeignClientUsingPropertiesTests {
|
||||
});
|
||||
|
||||
requestTemplate.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE);
|
||||
requestTemplate.body(builder.toString());
|
||||
requestTemplate.body(Request.Body.bodyTemplate(builder.toString(), UTF_8));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+6
-2
@@ -28,6 +28,7 @@ import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.boot.WebApplicationType;
|
||||
import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||
import org.springframework.cloud.commons.httpclient.HttpClientConfiguration;
|
||||
import org.springframework.cloud.test.ClassPathExclusions;
|
||||
@@ -49,8 +50,11 @@ public class FeignHttpClientConfigurationTests {
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
context = new SpringApplicationBuilder().properties("debug=true","feign.httpclient.disableSslValidation=true").web(false)
|
||||
.sources(HttpClientConfiguration.class, FeignAutoConfiguration.class).run();
|
||||
context = new SpringApplicationBuilder()
|
||||
.properties("debug=true","feign.httpclient.disableSslValidation=true")
|
||||
.web(WebApplicationType.NONE)
|
||||
.sources(HttpClientConfiguration.class, FeignAutoConfiguration.class)
|
||||
.run();
|
||||
}
|
||||
|
||||
@After
|
||||
|
||||
+3
@@ -34,8 +34,10 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
|
||||
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.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
@@ -103,6 +105,7 @@ public class FeignHttpClientUrlTests {
|
||||
@EnableAutoConfiguration
|
||||
@RestController
|
||||
@EnableFeignClients(clients = { UrlClient.class, BeanUrlClient.class, BeanUrlClientNoProtocol.class })
|
||||
@Import(NoSecurityConfiguration.class)
|
||||
protected static class TestConfig {
|
||||
|
||||
@RequestMapping(method = RequestMethod.GET, value = "/hello")
|
||||
|
||||
+2
-1
@@ -25,6 +25,7 @@ import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.boot.WebApplicationType;
|
||||
import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||
import org.springframework.cloud.commons.httpclient.HttpClientConfiguration;
|
||||
import org.springframework.cloud.commons.httpclient.OkHttpClientFactory;
|
||||
@@ -45,7 +46,7 @@ public class FeignOkHttpConfigurationTests {
|
||||
@Before
|
||||
public void setUp() {
|
||||
context = new SpringApplicationBuilder().properties("debug=true","feign.httpclient.disableSslValidation=true",
|
||||
"feign.okhttp.enabled=true", "feign.httpclient.enabled=false").web(false)
|
||||
"feign.okhttp.enabled=true", "feign.httpclient.enabled=false").web(WebApplicationType.NONE)
|
||||
.sources(HttpClientConfiguration.class, FeignAutoConfiguration.class).run();
|
||||
}
|
||||
|
||||
|
||||
+3
@@ -28,7 +28,9 @@ import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
|
||||
import org.springframework.cloud.openfeign.test.NoSecurityConfiguration;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
@@ -201,6 +203,7 @@ public class SpringDecoderTests extends FeignClientFactoryBean {
|
||||
@Configuration
|
||||
@EnableAutoConfiguration
|
||||
@RestController
|
||||
@Import(NoSecurityConfiguration.class)
|
||||
protected static class Application implements TestClient {
|
||||
|
||||
@Override
|
||||
|
||||
+2
-1
@@ -22,6 +22,7 @@ import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.boot.WebApplicationType;
|
||||
import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration;
|
||||
import org.springframework.cloud.openfeign.ribbon.CachingSpringLoadBalancerFactory;
|
||||
@@ -51,7 +52,7 @@ public class SpringRetryDisabledTests {
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
context = new SpringApplicationBuilder().web(false)
|
||||
context = new SpringApplicationBuilder().web(WebApplicationType.NONE)
|
||||
.sources(RibbonAutoConfiguration.class, LoadBalancerAutoConfiguration.class, RibbonClientConfiguration.class,
|
||||
FeignRibbonClientAutoConfiguration.class).run();
|
||||
}
|
||||
|
||||
+3
@@ -35,8 +35,10 @@ import org.springframework.cloud.openfeign.EnableFeignClients;
|
||||
import org.springframework.cloud.openfeign.encoding.app.client.InvoiceClient;
|
||||
import org.springframework.cloud.openfeign.encoding.app.domain.Invoice;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonClient;
|
||||
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.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
@@ -76,6 +78,7 @@ public class FeignAcceptEncodingTests {
|
||||
@EnableFeignClients(clients = InvoiceClient.class)
|
||||
@RibbonClient(name = "local", configuration = LocalRibbonClientConfiguration.class)
|
||||
@SpringBootApplication(scanBasePackages = "org.springframework.cloud.openfeign.encoding.app")
|
||||
@Import(NoSecurityConfiguration.class)
|
||||
public static class Application {
|
||||
}
|
||||
|
||||
|
||||
+3
@@ -33,8 +33,10 @@ import org.springframework.cloud.openfeign.EnableFeignClients;
|
||||
import org.springframework.cloud.openfeign.encoding.app.client.InvoiceClient;
|
||||
import org.springframework.cloud.openfeign.encoding.app.domain.Invoice;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonClient;
|
||||
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.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
@@ -79,6 +81,7 @@ public class FeignContentEncodingTests {
|
||||
@EnableFeignClients(clients = InvoiceClient.class)
|
||||
@RibbonClient(name = "local", configuration = LocalRibbonClientConfiguration.class)
|
||||
@SpringBootApplication(scanBasePackages = "org.springframework.cloud.openfeign.encoding.app")
|
||||
@Import(NoSecurityConfiguration.class)
|
||||
public static class Application {
|
||||
}
|
||||
|
||||
|
||||
+5
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2013 the original author or authors.
|
||||
* Copyright 2012-2018 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.
|
||||
@@ -19,6 +19,7 @@ package org.springframework.cloud.openfeign.encoding.proto;
|
||||
import feign.RequestTemplate;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.ObjectFactory;
|
||||
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
|
||||
@@ -27,6 +28,8 @@ import org.springframework.cloud.test.ClassPathExclusions;
|
||||
import org.springframework.cloud.test.ModifiedClassPathRunner;
|
||||
import org.springframework.http.converter.StringHttpMessageConverter;
|
||||
|
||||
import static feign.Request.HttpMethod.POST;
|
||||
|
||||
/**
|
||||
* Test {@link SpringEncoder} when protobuf is not in classpath
|
||||
*
|
||||
@@ -45,7 +48,7 @@ public class ProtobufNotInClasspathTest {
|
||||
}
|
||||
};
|
||||
RequestTemplate requestTemplate = new RequestTemplate();
|
||||
requestTemplate.method("POST");
|
||||
requestTemplate.method(POST);
|
||||
new SpringEncoder(converters).encode("a=b", String.class, requestTemplate);
|
||||
}
|
||||
|
||||
|
||||
+22
-13
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2013 the original author or authors.
|
||||
* Copyright 2012-2018 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,6 +16,14 @@
|
||||
|
||||
package org.springframework.cloud.openfeign.encoding.proto;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URISyntaxException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
import com.google.protobuf.InvalidProtocolBufferException;
|
||||
import feign.RequestTemplate;
|
||||
import feign.httpclient.ApacheHttpClient;
|
||||
@@ -30,24 +38,21 @@ import org.apache.http.message.BasicStatusLine;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.ArgumentMatchers;
|
||||
import org.mockito.BDDMockito;
|
||||
import org.mockito.Matchers;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.invocation.InvocationOnMock;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
import org.mockito.stubbing.Answer;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.ObjectFactory;
|
||||
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
|
||||
import org.springframework.cloud.openfeign.support.SpringEncoder;
|
||||
import org.springframework.http.converter.protobuf.ProtobufHttpMessageConverter;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URISyntaxException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import static feign.Request.Body.encoded;
|
||||
import static feign.Request.HttpMethod.POST;
|
||||
|
||||
/**
|
||||
* Test {@link SpringEncoder} with {@link ProtobufHttpMessageConverter}
|
||||
@@ -86,7 +91,9 @@ public class ProtobufSpringEncoderTest {
|
||||
RequestTemplate requestTemplate = newRequestTemplate();
|
||||
newEncoder().encode(request, Request.class, requestTemplate);
|
||||
// set a charset
|
||||
requestTemplate.body(requestTemplate.body(), StandardCharsets.UTF_8);
|
||||
requestTemplate
|
||||
.body(encoded(requestTemplate.requestBody()
|
||||
.asBytes(), StandardCharsets.UTF_8));
|
||||
HttpEntity entity = toApacheHttpEntity(requestTemplate);
|
||||
byte[] bytes = read(entity.getContent(), (int) entity.getContentLength());
|
||||
|
||||
@@ -112,20 +119,22 @@ public class ProtobufSpringEncoderTest {
|
||||
|
||||
private RequestTemplate newRequestTemplate() {
|
||||
RequestTemplate requestTemplate = new RequestTemplate();
|
||||
requestTemplate.method("POST");
|
||||
requestTemplate.method(POST);
|
||||
return requestTemplate;
|
||||
}
|
||||
|
||||
private HttpEntity toApacheHttpEntity(RequestTemplate requestTemplate) throws IOException, URISyntaxException {
|
||||
final List<HttpUriRequest> request = new ArrayList<>(1);
|
||||
BDDMockito.given(httpClient.execute(Matchers.<HttpUriRequest>any())).will(new Answer<HttpResponse>() {
|
||||
BDDMockito.given(httpClient.execute(ArgumentMatchers.<HttpUriRequest>any()))
|
||||
.will(new Answer<HttpResponse>() {
|
||||
@Override
|
||||
public HttpResponse answer(InvocationOnMock invocationOnMock) throws Throwable {
|
||||
request.add((HttpUriRequest) invocationOnMock.getArguments()[0]);
|
||||
return new BasicHttpResponse(new BasicStatusLine(new ProtocolVersion("http", 1, 1), 200, null));
|
||||
}
|
||||
});
|
||||
new ApacheHttpClient(httpClient).execute(requestTemplate.request(), new feign.Request.Options());
|
||||
new ApacheHttpClient(httpClient).execute(requestTemplate.resolve(new HashMap<>())
|
||||
.request(), new feign.Request.Options());
|
||||
HttpUriRequest httpUriRequest = request.get(0);
|
||||
return ((HttpEntityEnclosingRequestBase)httpUriRequest).getEntity();
|
||||
}
|
||||
|
||||
+30
-2
@@ -16,17 +16,45 @@
|
||||
|
||||
package org.springframework.cloud.openfeign.hystrix.security;
|
||||
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.cloud.openfeign.EnableFeignClients;
|
||||
import org.springframework.cloud.openfeign.hystrix.security.app.CustomConcurrenyStrategy;
|
||||
import org.springframework.cloud.openfeign.hystrix.security.app.ProxyUsernameController;
|
||||
import org.springframework.cloud.openfeign.hystrix.security.app.TestInterceptor;
|
||||
import org.springframework.cloud.openfeign.hystrix.security.app.UsernameClient;
|
||||
import org.springframework.cloud.openfeign.hystrix.security.app.UsernameController;
|
||||
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;
|
||||
|
||||
/**
|
||||
* @author Daniel Lavoie
|
||||
*/
|
||||
@Configuration
|
||||
@SpringBootApplication
|
||||
@EnableAutoConfiguration
|
||||
@EnableFeignClients(clients = UsernameClient.class)
|
||||
@Import(NoSecurityConfiguration.class)
|
||||
public class HystrixSecurityApplication {
|
||||
|
||||
@Bean
|
||||
public CustomConcurrenyStrategy customConcurrenyStrategy() {
|
||||
return new CustomConcurrenyStrategy();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public TestInterceptor testInterceptor() {
|
||||
return new TestInterceptor();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ProxyUsernameController proxyUsernameController() {
|
||||
return new ProxyUsernameController();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public UsernameController usernameController() {
|
||||
return new UsernameController();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+45
-17
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2016 the original author or authors.
|
||||
* Copyright 2013-2018 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.
|
||||
@@ -17,24 +17,34 @@
|
||||
package org.springframework.cloud.openfeign.hystrix.security;
|
||||
|
||||
import java.util.Base64;
|
||||
import org.junit.Assert;
|
||||
|
||||
import com.netflix.hystrix.strategy.HystrixPlugins;
|
||||
import com.netflix.hystrix.strategy.concurrency.HystrixConcurrencyStrategy;
|
||||
import com.netflix.loadbalancer.Server;
|
||||
import com.netflix.loadbalancer.ServerList;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.SpringBootConfiguration;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
|
||||
import org.springframework.boot.web.server.LocalServerPort;
|
||||
import org.springframework.cloud.netflix.hystrix.security.SecurityContextConcurrencyStrategy;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonClient;
|
||||
import org.springframework.cloud.netflix.ribbon.StaticServerList;
|
||||
import org.springframework.cloud.openfeign.hystrix.security.app.CustomConcurrenyStrategy;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.ActiveProfiles;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
import com.netflix.hystrix.strategy.HystrixPlugins;
|
||||
import com.netflix.hystrix.strategy.concurrency.HystrixConcurrencyStrategy;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@@ -45,9 +55,8 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@DirtiesContext
|
||||
@SpringBootTest(classes = HystrixSecurityApplication.class, webEnvironment = WebEnvironment.RANDOM_PORT,
|
||||
properties = { "username.ribbon.listOfServers=localhost:${local.server.port}",
|
||||
"feign.hystrix.enabled=true"})
|
||||
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT,
|
||||
properties = { "feign.hystrix.enabled=true"})
|
||||
@ActiveProfiles("proxysecurity")
|
||||
public class HystrixSecurityTests {
|
||||
@Autowired
|
||||
@@ -56,7 +65,7 @@ public class HystrixSecurityTests {
|
||||
@LocalServerPort
|
||||
private String serverPort;
|
||||
|
||||
//TODOO: move to constants in TestAutoConfiguration
|
||||
//TODO: move to constants in TestAutoConfiguration
|
||||
private String username = "user";
|
||||
|
||||
private String password = "password";
|
||||
@@ -69,19 +78,21 @@ public class HystrixSecurityTests {
|
||||
|
||||
@Test
|
||||
public void testFeignHystrixSecurity() {
|
||||
HttpHeaders headers = HystrixSecurityTests.createBasicAuthHeader(username,
|
||||
password);
|
||||
HttpHeaders headers = createBasicAuthHeader(username, password);
|
||||
|
||||
String usernameResult = new RestTemplate()
|
||||
ResponseEntity<String> entity = new RestTemplate()
|
||||
.exchange("http://localhost:" + serverPort + "/proxy-username",
|
||||
HttpMethod.GET, new HttpEntity<Void>(headers), String.class)
|
||||
.getBody();
|
||||
HttpMethod.GET, new HttpEntity<Void>(headers), String.class);
|
||||
|
||||
Assert.assertTrue("Username should have been intercepted by feign interceptor.",
|
||||
username.equals(usernameResult));
|
||||
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||
|
||||
Assert.assertTrue("Custom hook should have been called.",
|
||||
customConcurrenyStrategy.isHookCalled());
|
||||
assertThat(entity.getBody())
|
||||
.as("Username should have been intercepted by feign interceptor.")
|
||||
.isEqualTo(username);
|
||||
|
||||
assertThat(customConcurrenyStrategy.isHookCalled())
|
||||
.as("Custom hook should have been called.")
|
||||
.isTrue();
|
||||
}
|
||||
|
||||
public static HttpHeaders createBasicAuthHeader(final String username,
|
||||
@@ -97,4 +108,21 @@ public class HystrixSecurityTests {
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@SpringBootConfiguration
|
||||
@Import(HystrixSecurityApplication.class)
|
||||
@RibbonClient(name = "username", configuration = LocalRibbonClientConfiguration.class)
|
||||
protected static class TestConfig { }
|
||||
|
||||
protected static class LocalRibbonClientConfiguration {
|
||||
|
||||
@LocalServerPort
|
||||
private int port = 0;
|
||||
|
||||
@Bean
|
||||
public ServerList<Server> ribbonServerList() {
|
||||
return new StaticServerList<>(new Server("localhost", this.port));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
+2
-3
@@ -1,10 +1,9 @@
|
||||
package org.springframework.cloud.openfeign.hystrix.security.app;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
import org.springframework.stereotype.Component;
|
||||
import com.netflix.hystrix.strategy.concurrency.HystrixConcurrencyStrategy;
|
||||
|
||||
@Component
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
public class CustomConcurrenyStrategy extends HystrixConcurrencyStrategy {
|
||||
private boolean hookCalled;
|
||||
|
||||
|
||||
+1
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2016 the original author or authors.
|
||||
* Copyright 2013-2018 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.
|
||||
@@ -20,7 +20,6 @@ import feign.RequestInterceptor;
|
||||
import feign.RequestTemplate;
|
||||
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* This interceptor should be called from an Hyxtrix command execution thread. It is
|
||||
@@ -28,7 +27,6 @@ import org.springframework.stereotype.Component;
|
||||
*
|
||||
* @author Daniel Lavoie
|
||||
*/
|
||||
@Component
|
||||
public class TestInterceptor implements RequestInterceptor {
|
||||
|
||||
@Override
|
||||
|
||||
+1
-1
@@ -26,5 +26,5 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
||||
public interface UsernameClient {
|
||||
|
||||
@RequestMapping("/username")
|
||||
public String getUsername();
|
||||
String getUsername();
|
||||
}
|
||||
|
||||
+66
-22
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
* Copyright 2013-2018 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.
|
||||
@@ -18,30 +18,31 @@
|
||||
package org.springframework.cloud.openfeign.ribbon;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import com.netflix.client.config.IClientConfig;
|
||||
import com.netflix.loadbalancer.BaseLoadBalancer;
|
||||
import com.netflix.loadbalancer.ILoadBalancer;
|
||||
import com.netflix.loadbalancer.RoundRobinRule;
|
||||
import com.netflix.loadbalancer.Server;
|
||||
import com.netflix.loadbalancer.reactive.LoadBalancerCommand;
|
||||
import feign.Client;
|
||||
import feign.Request;
|
||||
import feign.Request.Options;
|
||||
import feign.RequestTemplate;
|
||||
import feign.Response;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.springframework.cloud.openfeign.ribbon.FeignLoadBalancer.RibbonRequest;
|
||||
import org.springframework.cloud.openfeign.ribbon.FeignLoadBalancer.RibbonResponse;
|
||||
|
||||
import org.springframework.cloud.netflix.ribbon.DefaultServerIntrospector;
|
||||
import org.springframework.cloud.netflix.ribbon.ServerIntrospector;
|
||||
|
||||
import com.netflix.client.config.IClientConfig;
|
||||
import com.netflix.loadbalancer.ILoadBalancer;
|
||||
import com.netflix.loadbalancer.Server;
|
||||
|
||||
import feign.Client;
|
||||
import feign.Request;
|
||||
import feign.RequestTemplate;
|
||||
import feign.Response;
|
||||
import feign.Request.Options;
|
||||
import org.springframework.cloud.openfeign.ribbon.FeignLoadBalancer.RibbonRequest;
|
||||
import org.springframework.cloud.openfeign.ribbon.FeignLoadBalancer.RibbonResponse;
|
||||
|
||||
import static com.netflix.client.config.CommonClientConfigKey.ConnectTimeout;
|
||||
import static com.netflix.client.config.CommonClientConfigKey.IsSecure;
|
||||
@@ -51,11 +52,12 @@ import static com.netflix.client.config.CommonClientConfigKey.OkToRetryOnAllOper
|
||||
import static com.netflix.client.config.CommonClientConfigKey.ReadTimeout;
|
||||
import static com.netflix.client.config.DefaultClientConfigImpl.DEFAULT_MAX_AUTO_RETRIES;
|
||||
import static com.netflix.client.config.DefaultClientConfigImpl.DEFAULT_MAX_AUTO_RETRIES_NEXT_SERVER;
|
||||
import static feign.Request.HttpMethod.GET;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Matchers.anyBoolean;
|
||||
import static org.mockito.Matchers.eq;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyBoolean;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
public class FeignLoadBalancerTests {
|
||||
@@ -93,19 +95,27 @@ public class FeignLoadBalancerTests {
|
||||
|
||||
this.feignLoadBalancer = new FeignLoadBalancer(this.lb, this.config,
|
||||
this.inspector);
|
||||
Request request = new RequestTemplate().method("GET").append("http://foo/")
|
||||
Request request = new RequestTemplate()
|
||||
.method(GET)
|
||||
.target("http://foo/")
|
||||
.resolve(new HashMap<>())
|
||||
.request();
|
||||
RibbonRequest ribbonRequest = new RibbonRequest(this.delegate, request,
|
||||
new URI(request.url()));
|
||||
|
||||
Response response = Response.create(200, "Test",
|
||||
Collections.<String, Collection<String>> emptyMap(), new byte[0]);
|
||||
Response response = Response.builder()
|
||||
.request(request)
|
||||
.status(200)
|
||||
.reason("Test")
|
||||
.headers(Collections.emptyMap())
|
||||
.body(new byte[0])
|
||||
.build();
|
||||
when(this.delegate.execute(any(Request.class), any(Options.class)))
|
||||
.thenReturn(response);
|
||||
|
||||
RibbonResponse resp = this.feignLoadBalancer.execute(ribbonRequest, null);
|
||||
|
||||
assertThat(resp.getRequestedURI(), is(new URI("http://foo/")));
|
||||
assertThat(resp.getRequestedURI(), is(new URI("http://foo")));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -156,7 +166,7 @@ public class FeignLoadBalancerTests {
|
||||
@Test
|
||||
public void testRibbonRequestURLEncode() throws Exception {
|
||||
String url = "http://foo/?name=%7bcookie";//name={cookie
|
||||
Request request = Request.create("GET",url,new HashMap(),null,null);
|
||||
Request request = Request.create(GET, url, new HashMap<>(), null, null);
|
||||
|
||||
assertThat(request.url(),is(url));
|
||||
|
||||
@@ -168,4 +178,38 @@ public class FeignLoadBalancerTests {
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOverrideFeignLoadBalancer() throws Exception {
|
||||
when(this.config.get(IsSecure)).thenReturn(false);
|
||||
Server server1 = new Server("foo", 6666);
|
||||
Server server2 = new Server("foo", 7777);
|
||||
BaseLoadBalancer baseLoadBalancer = new BaseLoadBalancer();
|
||||
baseLoadBalancer.setRule(new RoundRobinRule() {
|
||||
@Override
|
||||
public Server choose(Object loadBalancerKey) {
|
||||
return loadBalancerKey == null ? server2 : server1;
|
||||
}
|
||||
});
|
||||
|
||||
this.feignLoadBalancer = new FeignLoadBalancer(baseLoadBalancer, this.config,
|
||||
this.inspector) {
|
||||
protected void customizeLoadBalancerCommandBuilder(final FeignLoadBalancer.RibbonRequest request, final IClientConfig config,
|
||||
final LoadBalancerCommand.Builder<FeignLoadBalancer.RibbonResponse> builder) {
|
||||
builder.withServerLocator(request.getRequest().headers().get("c_ip"));
|
||||
}
|
||||
};
|
||||
Request request = new RequestTemplate().method(GET).resolve(new HashMap<>()).request();
|
||||
RibbonResponse resp = this.feignLoadBalancer.executeWithLoadBalancer(new RibbonRequest(this.delegate, request,
|
||||
new URI(request.url())), null);
|
||||
assertThat(resp.getRequestedURI().getPort(), is(7777));
|
||||
request = new RequestTemplate()
|
||||
.method(GET)
|
||||
.header("c_ip", "666")
|
||||
.resolve(new HashMap<>())
|
||||
.request();
|
||||
resp = this.feignLoadBalancer.executeWithLoadBalancer(new RibbonRequest(this.delegate, request,
|
||||
new URI(request.url())), null);
|
||||
assertThat(resp.getRequestedURI().getPort(), is(6666));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+16
-13
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2016 the original author or authors.
|
||||
* Copyright 2016-2018 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,30 +16,32 @@
|
||||
|
||||
package org.springframework.cloud.openfeign.ribbon;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
||||
import com.netflix.loadbalancer.Server;
|
||||
import com.netflix.loadbalancer.ServerList;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonClients;
|
||||
import org.springframework.cloud.netflix.ribbon.StaticServerList;
|
||||
import org.springframework.cloud.openfeign.EnableFeignClients;
|
||||
import org.springframework.cloud.openfeign.FeignClient;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonClient;
|
||||
import org.springframework.cloud.netflix.ribbon.StaticServerList;
|
||||
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.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import com.netflix.loadbalancer.Server;
|
||||
import com.netflix.loadbalancer.ServerList;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
||||
/**
|
||||
* @author Venil Noronha
|
||||
@@ -84,16 +86,16 @@ public class FeignRibbonClientPathTests {
|
||||
@FeignClient(name = "localapp", path = "/base/path")
|
||||
protected interface TestClient1 extends TestClient { }
|
||||
|
||||
@FeignClient(name = "localapp", path = "base/path")
|
||||
@FeignClient(name = "localapp1", path = "base/path")
|
||||
protected interface TestClient2 extends TestClient { }
|
||||
|
||||
@FeignClient(name = "localapp", path = "base/path/")
|
||||
@FeignClient(name = "localapp2", path = "base/path/")
|
||||
protected interface TestClient3 extends TestClient { }
|
||||
|
||||
@FeignClient(name = "localapp", path = "/base/path/")
|
||||
@FeignClient(name = "localapp3", path = "/base/path/")
|
||||
protected interface TestClient4 extends TestClient { }
|
||||
|
||||
@FeignClient(name = "localapp", path = "${test.path.prefix}")
|
||||
@FeignClient(name = "localapp4", path = "${test.path.prefix}")
|
||||
protected interface TestClient5 extends TestClient { }
|
||||
|
||||
@Configuration
|
||||
@@ -104,7 +106,8 @@ public class FeignRibbonClientPathTests {
|
||||
TestClient1.class, TestClient2.class, TestClient3.class, TestClient4.class,
|
||||
TestClient5.class
|
||||
})
|
||||
@RibbonClient(name = "localapp", configuration = LocalRibbonClientConfiguration.class)
|
||||
@RibbonClients(defaultConfiguration = LocalRibbonClientConfiguration.class)
|
||||
@Import(NoSecurityConfiguration.class)
|
||||
public static class Application {
|
||||
|
||||
@RequestMapping(method = RequestMethod.GET, value = "/hello")
|
||||
|
||||
+3
@@ -31,8 +31,10 @@ import org.springframework.cloud.openfeign.EnableFeignClients;
|
||||
import org.springframework.cloud.openfeign.FeignClient;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonClient;
|
||||
import org.springframework.cloud.netflix.ribbon.StaticServerList;
|
||||
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.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
@@ -78,6 +80,7 @@ public class FeignRibbonClientRetryTests {
|
||||
@RestController
|
||||
@EnableFeignClients(clients = TestClient.class)
|
||||
@RibbonClient(name = "localapp", configuration = LocalRibbonClientConfiguration.class)
|
||||
@Import(NoSecurityConfiguration.class)
|
||||
public static class Application {
|
||||
|
||||
private AtomicInteger retries = new AtomicInteger(1);
|
||||
|
||||
+22
-6
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2015 the original author or authors.
|
||||
* Copyright 2013-2018 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,6 +16,8 @@
|
||||
|
||||
package org.springframework.cloud.openfeign.ribbon;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
import com.netflix.client.config.CommonClientConfigKey;
|
||||
import com.netflix.client.config.DefaultClientConfigImpl;
|
||||
import com.netflix.client.config.IClientConfig;
|
||||
@@ -31,11 +33,13 @@ import feign.RequestTemplate;
|
||||
import org.hamcrest.CustomMatcher;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.cloud.netflix.ribbon.DefaultServerIntrospector;
|
||||
import org.springframework.cloud.netflix.ribbon.ServerIntrospector;
|
||||
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
|
||||
|
||||
import static org.mockito.Matchers.any;
|
||||
import static feign.Request.HttpMethod.GET;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
@@ -91,7 +95,10 @@ public class FeignRibbonClientTests {
|
||||
|
||||
@Test
|
||||
public void remoteRequestIsSentAtRoot() throws Exception {
|
||||
Request request = new RequestTemplate().method("GET").append("http://foo")
|
||||
Request request = new RequestTemplate()
|
||||
.method(GET)
|
||||
.target("http://foo")
|
||||
.resolve(new HashMap<>())
|
||||
.request();
|
||||
this.client.execute(request, new Options());
|
||||
RequestMatcher matcher = new RequestMatcher("http://foo.com:8000/");
|
||||
@@ -101,7 +108,10 @@ public class FeignRibbonClientTests {
|
||||
|
||||
@Test
|
||||
public void remoteRequestIsSent() throws Exception {
|
||||
Request request = new RequestTemplate().method("GET").append("http://foo/")
|
||||
Request request = new RequestTemplate()
|
||||
.method(GET)
|
||||
.target("http://foo/")
|
||||
.resolve(new HashMap<>())
|
||||
.request();
|
||||
this.client.execute(request, new Options());
|
||||
RequestMatcher matcher = new RequestMatcher("http://foo.com:8000/");
|
||||
@@ -111,7 +121,10 @@ public class FeignRibbonClientTests {
|
||||
|
||||
@Test
|
||||
public void verifyCleanUrl() throws Exception {
|
||||
Request request = new RequestTemplate().method("GET").append("http://tp/abc/bcd.json")
|
||||
Request request = new RequestTemplate()
|
||||
.method(GET)
|
||||
.target("http://tp/abc/bcd.json")
|
||||
.resolve(new HashMap<>())
|
||||
.request();
|
||||
this.client.execute(request, new Options());
|
||||
RequestMatcher matcher = new RequestMatcher("http://foo.com:8000/abc/bcd.json");
|
||||
@@ -121,7 +134,10 @@ public class FeignRibbonClientTests {
|
||||
|
||||
@Test
|
||||
public void remoteRequestIsSecure() throws Exception {
|
||||
Request request = new RequestTemplate().method("GET").append("https://foo/")
|
||||
Request request = new RequestTemplate()
|
||||
.method(GET)
|
||||
.target("https://foo/")
|
||||
.resolve(new HashMap<>())
|
||||
.request();
|
||||
this.client.execute(request, new Options());
|
||||
RequestMatcher matcher = new RequestMatcher("https://foo.com:8000/");
|
||||
|
||||
+104
-68
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
* Copyright 2013-2018 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.
|
||||
@@ -17,25 +17,31 @@
|
||||
|
||||
package org.springframework.cloud.openfeign.ribbon;
|
||||
|
||||
import feign.Client;
|
||||
import feign.Request;
|
||||
import feign.Response;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.Reader;
|
||||
import java.net.URI;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Collection;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
import com.netflix.client.DefaultLoadBalancerRetryHandler;
|
||||
import com.netflix.client.RequestSpecificRetryHandler;
|
||||
import com.netflix.client.config.CommonClientConfigKey;
|
||||
import com.netflix.client.config.IClientConfig;
|
||||
import com.netflix.loadbalancer.ILoadBalancer;
|
||||
import com.netflix.loadbalancer.Server;
|
||||
import feign.Client;
|
||||
import feign.Request;
|
||||
import feign.Response;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
import org.springframework.cloud.client.ServiceInstance;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryFactory;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryPolicy;
|
||||
@@ -46,7 +52,6 @@ import org.springframework.cloud.netflix.ribbon.RibbonLoadBalancedRetryPolicy;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerContext;
|
||||
import org.springframework.cloud.netflix.ribbon.ServerIntrospector;
|
||||
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
|
||||
import org.springframework.http.HttpRequest;
|
||||
import org.springframework.retry.RetryCallback;
|
||||
import org.springframework.retry.RetryContext;
|
||||
import org.springframework.retry.RetryListener;
|
||||
@@ -55,13 +60,6 @@ import org.springframework.retry.backoff.BackOffContext;
|
||||
import org.springframework.retry.backoff.BackOffInterruptedException;
|
||||
import org.springframework.retry.backoff.BackOffPolicy;
|
||||
|
||||
import com.netflix.client.DefaultLoadBalancerRetryHandler;
|
||||
import com.netflix.client.RequestSpecificRetryHandler;
|
||||
import com.netflix.client.config.CommonClientConfigKey;
|
||||
import com.netflix.client.config.IClientConfig;
|
||||
import com.netflix.loadbalancer.ILoadBalancer;
|
||||
import com.netflix.loadbalancer.Server;
|
||||
|
||||
import static com.netflix.client.config.CommonClientConfigKey.ConnectTimeout;
|
||||
import static com.netflix.client.config.CommonClientConfigKey.MaxAutoRetries;
|
||||
import static com.netflix.client.config.CommonClientConfigKey.MaxAutoRetriesNextServer;
|
||||
@@ -69,15 +67,16 @@ import static com.netflix.client.config.CommonClientConfigKey.OkToRetryOnAllOper
|
||||
import static com.netflix.client.config.CommonClientConfigKey.ReadTimeout;
|
||||
import static com.netflix.client.config.DefaultClientConfigImpl.DEFAULT_MAX_AUTO_RETRIES;
|
||||
import static com.netflix.client.config.DefaultClientConfigImpl.DEFAULT_MAX_AUTO_RETRIES_NEXT_SERVER;
|
||||
import static feign.Request.HttpMethod.GET;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.instanceOf;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Matchers.anyBoolean;
|
||||
import static org.mockito.Matchers.anyInt;
|
||||
import static org.mockito.Matchers.anyObject;
|
||||
import static org.mockito.Matchers.eq;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyBoolean;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.doThrow;
|
||||
import static org.mockito.Mockito.mock;
|
||||
@@ -88,6 +87,7 @@ import static org.mockito.Mockito.when;
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
* @author Gang Li
|
||||
* @author Olga Maciaszek-Sharma
|
||||
*/
|
||||
public class RetryableFeignLoadBalancerTests {
|
||||
@Mock
|
||||
@@ -126,12 +126,15 @@ public class RetryableFeignLoadBalancerTests {
|
||||
doReturn("404,502,foo, ,").when(config).getPropertyAsString(eq(RibbonLoadBalancedRetryPolicy.RETRYABLE_STATUS_CODES),eq(""));
|
||||
doReturn(config).when(clientFactory).getClientConfig(eq("default"));
|
||||
RibbonLoadBalancedRetryFactory loadBalancedRetryFactory = new RibbonLoadBalancedRetryFactory(clientFactory);
|
||||
HttpRequest springRequest = mock(HttpRequest.class);
|
||||
Request feignRequest = Request.create("GET", "http://foo", new HashMap<String, Collection<String>>(),
|
||||
new byte[]{}, StandardCharsets.UTF_8);
|
||||
Request feignRequest = Request.create(GET, "http://foo", new HashMap<>(),
|
||||
new byte[] {}, UTF_8);
|
||||
Client client = mock(Client.class);
|
||||
FeignLoadBalancer.RibbonRequest request = new FeignLoadBalancer.RibbonRequest(client, feignRequest, new URI("http://foo"));
|
||||
Response response = Response.builder().status(200).headers(new HashMap<String, Collection<String>>()).build();
|
||||
Response response = Response.builder()
|
||||
.status(200)
|
||||
.request(feignRequest)
|
||||
.headers(new HashMap<>())
|
||||
.build();
|
||||
doReturn(response).when(client).execute(any(Request.class), any(Request.Options.class));
|
||||
RetryableFeignLoadBalancer feignLb = new RetryableFeignLoadBalancer(lb, config, inspector, loadBalancedRetryFactory);
|
||||
FeignLoadBalancer.RibbonResponse ribbonResponse = feignLb.execute(request, null);
|
||||
@@ -141,9 +144,8 @@ public class RetryableFeignLoadBalancerTests {
|
||||
|
||||
@Test
|
||||
public void executeNeverRetry() throws Exception {
|
||||
HttpRequest springRequest = mock(HttpRequest.class);
|
||||
Request feignRequest = Request.create("GET", "http://foo", new HashMap<String, Collection<String>>(),
|
||||
new byte[]{}, StandardCharsets.UTF_8);
|
||||
Request feignRequest = Request.create(GET, "http://foo", new HashMap<>(),
|
||||
new byte[] {}, UTF_8);
|
||||
Client client = mock(Client.class);
|
||||
FeignLoadBalancer.RibbonRequest request = new FeignLoadBalancer.RibbonRequest(client, feignRequest, new URI("http://foo"));
|
||||
doThrow(new IOException("boom")).when(client).execute(any(Request.class), any(Request.Options.class));
|
||||
@@ -192,12 +194,15 @@ public class RetryableFeignLoadBalancerTests {
|
||||
return backOffPolicy;
|
||||
}
|
||||
};
|
||||
HttpRequest springRequest = mock(HttpRequest.class);
|
||||
Request feignRequest = Request.create("GET", "http://foo", new HashMap<String, Collection<String>>(),
|
||||
new byte[]{}, StandardCharsets.UTF_8);
|
||||
Request feignRequest = Request.create(GET, "http://foo", new HashMap<>(),
|
||||
new byte[] {}, UTF_8);
|
||||
Client client = mock(Client.class);
|
||||
FeignLoadBalancer.RibbonRequest request = new FeignLoadBalancer.RibbonRequest(client, feignRequest, new URI("http://foo"));
|
||||
Response response = Response.builder().status(200).headers(new HashMap<String, Collection<String>>()).build();
|
||||
Response response = Response.builder()
|
||||
.status(200)
|
||||
.request(feignRequest)
|
||||
.headers(new HashMap<>())
|
||||
.build();
|
||||
doThrow(new IOException("boom")).doReturn(response).when(client).execute(any(Request.class), any(Request.Options.class));
|
||||
|
||||
RetryableFeignLoadBalancer feignLb = new RetryableFeignLoadBalancer(lb, config, inspector, loadBalancedRetryFactory);
|
||||
@@ -227,13 +232,20 @@ public class RetryableFeignLoadBalancerTests {
|
||||
return backOffPolicy;
|
||||
}
|
||||
};
|
||||
HttpRequest springRequest = mock(HttpRequest.class);
|
||||
Request feignRequest = Request.create("GET", "http://foo", new HashMap<String, Collection<String>>(),
|
||||
new byte[]{}, StandardCharsets.UTF_8);
|
||||
Request feignRequest = Request.create(GET, "http://foo", new HashMap<>(),
|
||||
new byte[] {}, UTF_8);
|
||||
Client client = mock(Client.class);
|
||||
FeignLoadBalancer.RibbonRequest request = new FeignLoadBalancer.RibbonRequest(client, feignRequest, new URI("http://foo"));
|
||||
Response response = Response.builder().status(200).headers(new HashMap<String, Collection<String>>()).build();
|
||||
Response fourOFourResponse = Response.builder().status(404).headers(new HashMap<String, Collection<String>>()).build();
|
||||
Response response = Response
|
||||
.builder()
|
||||
.request(feignRequest)
|
||||
.status(200)
|
||||
.headers(new HashMap<>())
|
||||
.build();
|
||||
Response fourOFourResponse = Response.builder()
|
||||
.request(feignRequest)
|
||||
.status(404)
|
||||
.headers(new HashMap<>()).build();
|
||||
doReturn(fourOFourResponse).doReturn(response).when(client).execute(any(Request.class), any(Request.Options.class));
|
||||
RetryableFeignLoadBalancer feignLb = new RetryableFeignLoadBalancer(lb, config, inspector, loadBalancedRetryFactory);
|
||||
FeignLoadBalancer.RibbonResponse ribbonResponse = feignLb.execute(request, null);
|
||||
@@ -247,7 +259,7 @@ public class RetryableFeignLoadBalancerTests {
|
||||
int retriesNextServer = 0;
|
||||
when(this.config.get(MaxAutoRetriesNextServer,
|
||||
DEFAULT_MAX_AUTO_RETRIES_NEXT_SERVER)).thenReturn(retriesNextServer);
|
||||
doReturn(new Server("foo", 80)).when(lb).chooseServer(anyObject());
|
||||
doReturn(new Server("foo", 80)).when(lb).chooseServer(any());
|
||||
RibbonLoadBalancerContext lbContext = new RibbonLoadBalancerContext(lb, config);
|
||||
SpringClientFactory clientFactory = mock(SpringClientFactory.class);
|
||||
IClientConfig config = mock(IClientConfig.class);
|
||||
@@ -266,18 +278,25 @@ public class RetryableFeignLoadBalancerTests {
|
||||
return backOffPolicy;
|
||||
}
|
||||
};
|
||||
HttpRequest springRequest = mock(HttpRequest.class);
|
||||
Request feignRequest = Request.create("GET", "http://foo", new HashMap<String, Collection<String>>(),
|
||||
new byte[]{}, StandardCharsets.UTF_8);
|
||||
Request feignRequest = Request.create(GET, "http://foo", new HashMap<>(),
|
||||
new byte[] {}, UTF_8);
|
||||
Client client = mock(Client.class);
|
||||
FeignLoadBalancer.RibbonRequest request = new FeignLoadBalancer.RibbonRequest(client, feignRequest, new URI("http://foo"));
|
||||
Response response = Response.builder().status(404).headers(new HashMap<String, Collection<String>>()).build();
|
||||
Response fourOFourResponse = Response.builder().status(404).headers(new HashMap<String, Collection<String>>()).build();
|
||||
Response response = Response.builder()
|
||||
.request(feignRequest)
|
||||
.status(404)
|
||||
.headers(new HashMap<>())
|
||||
.build();
|
||||
Response fourOFourResponse = Response.builder()
|
||||
.request(feignRequest)
|
||||
.status(404)
|
||||
.headers(new HashMap<>())
|
||||
.build();
|
||||
doReturn(fourOFourResponse).doReturn(response).when(client).execute(any(Request.class), any(Request.Options.class));
|
||||
RetryableFeignLoadBalancer feignLb = new RetryableFeignLoadBalancer(lb, config, inspector, loadBalancedRetryFactory);
|
||||
FeignLoadBalancer.RibbonResponse ribbonResponse = feignLb.execute(request, null);
|
||||
assertEquals(404, ribbonResponse.toResponse().status());
|
||||
assertEquals(new Integer(0), ribbonResponse.toResponse().body().length());
|
||||
assertEquals(Integer.valueOf(0), ribbonResponse.toResponse().body().length());
|
||||
verify(client, times(2)).execute(any(Request.class), any(Request.Options.class));
|
||||
assertEquals(1, backOffPolicy.getCount());
|
||||
}
|
||||
@@ -288,12 +307,15 @@ public class RetryableFeignLoadBalancerTests {
|
||||
SpringClientFactory clientFactory = mock(SpringClientFactory.class);
|
||||
doReturn(lbContext).when(clientFactory).getLoadBalancerContext(any(String.class));
|
||||
RibbonLoadBalancedRetryFactory loadBalancedRetryFactory = new RibbonLoadBalancedRetryFactory(clientFactory);
|
||||
HttpRequest springRequest = mock(HttpRequest.class);
|
||||
Request feignRequest = Request.create("GET", "http://foo", new HashMap<String, Collection<String>>(),
|
||||
new byte[]{}, StandardCharsets.UTF_8);
|
||||
Request feignRequest = Request.create(GET, "http://foo", new HashMap<>(),
|
||||
new byte[] {}, UTF_8);
|
||||
Client client = mock(Client.class);
|
||||
FeignLoadBalancer.RibbonRequest request = new FeignLoadBalancer.RibbonRequest(client, feignRequest, new URI("http://foo"));
|
||||
Response response = Response.builder().status(200).headers(new HashMap<String, Collection<String>>()).build();
|
||||
Response response = Response.builder()
|
||||
.request(feignRequest)
|
||||
.status(200)
|
||||
.headers(new HashMap<>())
|
||||
.build();
|
||||
doReturn(response).when(client).execute(any(Request.class), any(Request.Options.class));
|
||||
RetryableFeignLoadBalancer feignLb = new RetryableFeignLoadBalancer(lb, config, inspector, loadBalancedRetryFactory);
|
||||
RequestSpecificRetryHandler retryHandler = feignLb.getRequestSpecificRetryHandler(request, config);
|
||||
@@ -308,12 +330,15 @@ public class RetryableFeignLoadBalancerTests {
|
||||
SpringClientFactory clientFactory = mock(SpringClientFactory.class);
|
||||
doReturn(lbContext).when(clientFactory).getLoadBalancerContext(any(String.class));
|
||||
RibbonLoadBalancedRetryFactory loadBalancedRetryFactory = new RibbonLoadBalancedRetryFactory(clientFactory);
|
||||
HttpRequest springRequest = mock(HttpRequest.class);
|
||||
Request feignRequest = Request.create("GET", "http://foo", new HashMap<String, Collection<String>>(),
|
||||
new byte[]{}, StandardCharsets.UTF_8);
|
||||
Request feignRequest = Request
|
||||
.create(GET, "http://foo", new HashMap<>(),
|
||||
new byte[] {}, UTF_8);
|
||||
Client client = mock(Client.class);
|
||||
FeignLoadBalancer.RibbonRequest request = new FeignLoadBalancer.RibbonRequest(client, feignRequest, new URI("http://foo"));
|
||||
Response response = Response.builder().status(200).headers(new HashMap<String, Collection<String>>()).build();
|
||||
Response response = Response.builder()
|
||||
.request(feignRequest)
|
||||
.status(200).headers(new HashMap<>())
|
||||
.build();
|
||||
doReturn(response).when(client).execute(any(Request.class), any(Request.Options.class));
|
||||
final Server server = new Server("foo", 80);
|
||||
RetryableFeignLoadBalancer feignLb = new RetryableFeignLoadBalancer(new ILoadBalancer() {
|
||||
@@ -379,12 +404,15 @@ public class RetryableFeignLoadBalancerTests {
|
||||
return backOffPolicy;
|
||||
}
|
||||
};
|
||||
HttpRequest springRequest = mock(HttpRequest.class);
|
||||
Request feignRequest = Request.create("GET", "http://listener", new HashMap<String, Collection<String>>(),
|
||||
new byte[]{}, StandardCharsets.UTF_8);
|
||||
Request feignRequest = Request.create(GET, "http://listener", new HashMap<>(),
|
||||
new byte[] {}, UTF_8);
|
||||
Client client = mock(Client.class);
|
||||
FeignLoadBalancer.RibbonRequest request = new FeignLoadBalancer.RibbonRequest(client, feignRequest, new URI("http://listener"));
|
||||
Response response = Response.builder().status(200).headers(new HashMap<String, Collection<String>>()).build();
|
||||
Response response = Response.builder()
|
||||
.request(feignRequest)
|
||||
.status(200)
|
||||
.headers(new HashMap<>())
|
||||
.build();
|
||||
doThrow(new IOException("boom")).doReturn(response).when(client).execute(any(Request.class), any(Request.Options.class));
|
||||
|
||||
RetryableFeignLoadBalancer feignLb = new RetryableFeignLoadBalancer(lb, config, inspector, loadBalancedRetryFactory);
|
||||
@@ -408,7 +436,6 @@ public class RetryableFeignLoadBalancerTests {
|
||||
doReturn("").when(config).getPropertyAsString(eq(RibbonLoadBalancedRetryPolicy.RETRYABLE_STATUS_CODES),eq(""));
|
||||
doReturn(config).when(clientFactory).getClientConfig(eq("default"));
|
||||
doReturn(lbContext).when(clientFactory).getLoadBalancerContext(any(String.class));
|
||||
Response response = Response.builder().status(200).headers(new HashMap<String, Collection<String>>()).build();
|
||||
MyBackOffPolicy backOffPolicy = new MyBackOffPolicy();
|
||||
MyRetryListenerNotRetry myRetryListenerNotRetry = new MyRetryListenerNotRetry();
|
||||
RibbonLoadBalancedRetryFactory loadBalancedRetryFactory = new RibbonLoadBalancedRetryFactory(clientFactory){
|
||||
@@ -422,9 +449,8 @@ public class RetryableFeignLoadBalancerTests {
|
||||
return backOffPolicy;
|
||||
}
|
||||
};
|
||||
HttpRequest springRequest = mock(HttpRequest.class);
|
||||
Request feignRequest = Request.create("GET", "http://listener", new HashMap<String, Collection<String>>(),
|
||||
new byte[]{}, StandardCharsets.UTF_8);
|
||||
Request feignRequest = Request.create(GET, "http://listener", new HashMap<>(),
|
||||
new byte[] {}, UTF_8);
|
||||
Client client = mock(Client.class);
|
||||
FeignLoadBalancer.RibbonRequest request = new FeignLoadBalancer.RibbonRequest(client, feignRequest, new URI("http://listener"));
|
||||
RetryableFeignLoadBalancer feignLb = new RetryableFeignLoadBalancer(lb, config, inspector, loadBalancedRetryFactory);
|
||||
@@ -451,12 +477,15 @@ public class RetryableFeignLoadBalancerTests {
|
||||
return backOffPolicy;
|
||||
}
|
||||
};
|
||||
HttpRequest springRequest = mock(HttpRequest.class);
|
||||
Request feignRequest = Request.create("GET", "http://listener", new HashMap<String, Collection<String>>(),
|
||||
new byte[]{}, StandardCharsets.UTF_8);
|
||||
Request feignRequest = Request.create(GET, "http://listener", new HashMap<>(),
|
||||
new byte[] {}, UTF_8);
|
||||
Client client = mock(Client.class);
|
||||
FeignLoadBalancer.RibbonRequest request = new FeignLoadBalancer.RibbonRequest(client, feignRequest, new URI("http://listener"));
|
||||
Response response = Response.builder().status(200).headers(new HashMap<String, Collection<String>>()).build();
|
||||
Response response = Response.builder()
|
||||
.request(feignRequest)
|
||||
.status(200)
|
||||
.headers(new HashMap<>())
|
||||
.build();
|
||||
doThrow(new IOException("boom")).doReturn(response).when(client).execute(any(Request.class), any(Request.Options.class));
|
||||
RetryableFeignLoadBalancer feignLb = new RetryableFeignLoadBalancer(lb, config, inspector, loadBalancedRetryPolicyFactory);
|
||||
FeignLoadBalancer.RibbonResponse ribbonResponse = feignLb.execute(request, null);
|
||||
@@ -486,12 +515,14 @@ public class RetryableFeignLoadBalancerTests {
|
||||
return backOffPolicy;
|
||||
}
|
||||
};
|
||||
HttpRequest springRequest = mock(HttpRequest.class);
|
||||
Request feignRequest = Request.create("GET", "http://foo", new HashMap<String, Collection<String>>(),
|
||||
new byte[]{}, StandardCharsets.UTF_8);
|
||||
Request feignRequest = Request.create(GET, "http://foo", new HashMap<>(),
|
||||
new byte[] {}, UTF_8);
|
||||
Client client = mock(Client.class);
|
||||
FeignLoadBalancer.RibbonRequest request = new FeignLoadBalancer.RibbonRequest(client, feignRequest, new URI("http://foo"));
|
||||
Response fourOFourResponse = Response.builder().status(404).headers(new HashMap<String, Collection<String>>())
|
||||
Response fourOFourResponse = Response.builder()
|
||||
.request(feignRequest)
|
||||
.status(404)
|
||||
.headers(new HashMap<>())
|
||||
.body(new Response.Body() { //set content into response
|
||||
@Override
|
||||
public Integer length() {
|
||||
@@ -510,7 +541,12 @@ public class RetryableFeignLoadBalancerTests {
|
||||
|
||||
@Override
|
||||
public Reader asReader() throws IOException {
|
||||
return new InputStreamReader(asInputStream(), "UTF-8");
|
||||
return new InputStreamReader(asInputStream(), UTF_8);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Reader asReader(Charset charset) throws IOException {
|
||||
return new InputStreamReader(asInputStream(), charset);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+7
-5
@@ -15,9 +15,6 @@
|
||||
*/
|
||||
package org.springframework.cloud.openfeign.ribbon;
|
||||
|
||||
import feign.Request;
|
||||
import feign.Response;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.net.URI;
|
||||
import java.nio.charset.Charset;
|
||||
@@ -26,11 +23,16 @@ import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import feign.Request;
|
||||
import feign.Response;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
|
||||
import org.springframework.util.StreamUtils;
|
||||
|
||||
import static feign.Request.HttpMethod.GET;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
/**
|
||||
@@ -45,7 +47,7 @@ public class RibbonResponseStatusCodeExceptionTest {
|
||||
List<String> fooValues = new ArrayList<String>();
|
||||
fooValues.add("bar");
|
||||
headers.put("foo", fooValues);
|
||||
Request request = Request.create("GET", "http://service.com",
|
||||
Request request = Request.create(GET, "http://service.com",
|
||||
new HashMap<String, Collection<String>>(), new byte[]{}, Charset.defaultCharset());
|
||||
byte[] body = "foo".getBytes();
|
||||
ByteArrayInputStream is = new ByteArrayInputStream(body);
|
||||
|
||||
+3
-3
@@ -23,6 +23,7 @@ import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.boot.test.util.TestPropertyValues;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
@@ -32,7 +33,6 @@ import org.springframework.test.context.junit4.SpringRunner;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.springframework.boot.test.util.EnvironmentTestUtils.addEnvironment;
|
||||
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
@@ -63,12 +63,12 @@ public class FeignHttpClientPropertiesTests {
|
||||
|
||||
@Test
|
||||
public void testCustomization() {
|
||||
addEnvironment(this.context, "feign.httpclient.maxConnections=2",
|
||||
TestPropertyValues.of("feign.httpclient.maxConnections=2",
|
||||
"feign.httpclient.connectionTimeout=2",
|
||||
"feign.httpclient.maxConnectionsPerRoute=2",
|
||||
"feign.httpclient.timeToLive=2",
|
||||
"feign.httpclient.disableSslValidation=true",
|
||||
"feign.httpclient.followRedirects=false");
|
||||
"feign.httpclient.followRedirects=false").applyTo(this.context);
|
||||
setupContext();
|
||||
assertEquals(2, getProperties().getMaxConnections());
|
||||
assertEquals(2, getProperties().getConnectionTimeout());
|
||||
|
||||
+20
-13
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
* Copyright 2013-2018 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.
|
||||
@@ -21,10 +21,14 @@ import java.io.IOException;
|
||||
import java.lang.reflect.Type;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import feign.RequestTemplate;
|
||||
import feign.codec.EncodeException;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.ArgumentMatcher;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
@@ -46,16 +50,18 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.notNullValue;
|
||||
import static org.hamcrest.Matchers.nullValue;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import feign.RequestTemplate;
|
||||
import feign.codec.EncodeException;
|
||||
import static org.springframework.http.HttpHeaders.CONTENT_TYPE;
|
||||
import static org.springframework.http.MediaType.APPLICATION_OCTET_STREAM_VALUE;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
* @author Olga Maciaszek-Sharma
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@SpringBootTest(classes = SpringEncoderTests.Application.class, webEnvironment = WebEnvironment.RANDOM_PORT, value = {
|
||||
@@ -85,8 +91,8 @@ public class SpringEncoderTests {
|
||||
String header = contentTypeHeader.iterator().next();
|
||||
assertThat("content type header is wrong", header, is("application/mytype"));
|
||||
|
||||
assertThat("request charset is null", request.charset(), is(notNullValue()));
|
||||
assertThat("request charset is wrong", request.charset(), is(Charset.forName("UTF-8")));
|
||||
assertThat("request charset is null", request.requestCharset(), is(notNullValue()));
|
||||
assertThat("request charset is wrong", request.requestCharset(), is(Charset.forName("UTF-8")));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -97,7 +103,9 @@ public class SpringEncoderTests {
|
||||
|
||||
encoder.encode("hi".getBytes(), null, request);
|
||||
|
||||
assertThat("request charset is not null", request.charset(), is(nullValue()));
|
||||
assertThat("Request Content-Type is not octet-stream",
|
||||
((List) request.headers().get(CONTENT_TYPE)).get(0),
|
||||
equalTo(APPLICATION_OCTET_STREAM_VALUE));
|
||||
}
|
||||
|
||||
@Test(expected = EncodeException.class)
|
||||
@@ -109,7 +117,7 @@ public class SpringEncoderTests {
|
||||
MultipartFile multipartFile = new MockMultipartFile("test_multipart_file", "hi".getBytes());
|
||||
encoder.encode(multipartFile, MultipartFile.class, request);
|
||||
|
||||
assertThat("request charset is not null", request.charset(), is(nullValue()));
|
||||
assertThat("request charset is not null", request.requestCharset(), is(nullValue()));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -122,7 +130,9 @@ public class SpringEncoderTests {
|
||||
MultipartFile multipartFile = new MockMultipartFile("test_multipart_file", "hi".getBytes());
|
||||
encoder.encode(multipartFile, MultipartFile.class, request);
|
||||
|
||||
assertThat("request charset is not null", request.charset(), is(nullValue()));
|
||||
assertThat("Request Content-Type is not multipart/form-data",
|
||||
(String) ((List) request.headers().get(CONTENT_TYPE)).get(0),
|
||||
containsString(MediaType.MULTIPART_FORM_DATA_VALUE));
|
||||
}
|
||||
|
||||
class MediaTypeMatcher implements ArgumentMatcher<MediaType> {
|
||||
@@ -192,10 +202,7 @@ public class SpringEncoderTests {
|
||||
|
||||
@Override
|
||||
public boolean canWrite(Class<?> clazz, MediaType mediaType) {
|
||||
if (clazz == String.class) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
return clazz == String.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+123
-17
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2016 the original author or authors.
|
||||
* Copyright 2013-2018 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.
|
||||
@@ -18,12 +18,27 @@ package org.springframework.cloud.openfeign.support;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||
import feign.MethodMetadata;
|
||||
import feign.Param;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.cloud.openfeign.SpringQueryMap;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
import org.springframework.format.annotation.NumberFormat;
|
||||
import org.springframework.format.number.NumberStyleFormatter;
|
||||
import org.springframework.format.support.FormattingConversionServiceFactoryBean;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
@@ -38,16 +53,17 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assume.assumeTrue;
|
||||
|
||||
import feign.MethodMetadata;
|
||||
import static org.springframework.web.util.UriUtils.encode;
|
||||
|
||||
/**
|
||||
* @author chadjaros
|
||||
* @author Halvdan Hoem Grelland
|
||||
* @author Aram Peres
|
||||
*/
|
||||
public class SpringMvcContractTests {
|
||||
private static final Class<?> EXECUTABLE_TYPE;
|
||||
@@ -67,7 +83,12 @@ public class SpringMvcContractTests {
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
this.contract = new SpringMvcContract();
|
||||
FormattingConversionServiceFactoryBean conversionServiceFactoryBean
|
||||
= new FormattingConversionServiceFactoryBean();
|
||||
conversionServiceFactoryBean.afterPropertiesSet();
|
||||
ConversionService conversionService = conversionServiceFactoryBean.getObject();
|
||||
|
||||
this.contract = new SpringMvcContract(Collections.emptyList(), conversionService);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -167,7 +188,7 @@ public class SpringMvcContractTests {
|
||||
MethodMetadata data = this.contract
|
||||
.parseAndValidateMetadata(method.getDeclaringClass(), method);
|
||||
|
||||
assertEquals("", data.template().url());
|
||||
assertEquals("/", data.template().url());
|
||||
assertEquals("POST", data.template().method());
|
||||
assertEquals(MediaType.APPLICATION_JSON_VALUE,
|
||||
data.template().headers().get("Accept").iterator().next());
|
||||
@@ -181,7 +202,7 @@ public class SpringMvcContractTests {
|
||||
MethodMetadata data = this.contract
|
||||
.parseAndValidateMetadata(method.getDeclaringClass(), method);
|
||||
|
||||
assertEquals("", data.template().url());
|
||||
assertEquals("/", data.template().url());
|
||||
assertEquals("POST", data.template().method());
|
||||
assertEquals(MediaType.APPLICATION_JSON_VALUE,
|
||||
data.template().headers().get("Accept").iterator().next());
|
||||
@@ -195,7 +216,8 @@ public class SpringMvcContractTests {
|
||||
MethodMetadata data = this.contract
|
||||
.parseAndValidateMetadata(method.getDeclaringClass(), method);
|
||||
|
||||
assertEquals("/advanced/test/{id}", data.template().url());
|
||||
assertEquals("/advanced/test/{id}?amount=" + encode("{amount}", UTF_8),
|
||||
data.template().url());
|
||||
assertEquals("PUT", data.template().method());
|
||||
assertEquals(MediaType.APPLICATION_JSON_VALUE,
|
||||
data.template().headers().get("Accept").iterator().next());
|
||||
@@ -218,7 +240,8 @@ public class SpringMvcContractTests {
|
||||
MethodMetadata data = this.contract
|
||||
.parseAndValidateMetadata(method.getDeclaringClass(), method);
|
||||
|
||||
assertEquals("/advanced/test/{id}", data.template().url());
|
||||
assertEquals("/advanced/test/{id}?amount=" + encode("{amount}", UTF_8),
|
||||
data.template().url());
|
||||
assertEquals("PUT", data.template().method());
|
||||
assertEquals(MediaType.APPLICATION_JSON_VALUE,
|
||||
data.template().headers().get("Accept").iterator().next());
|
||||
@@ -241,7 +264,8 @@ public class SpringMvcContractTests {
|
||||
MethodMetadata data = this.contract
|
||||
.parseAndValidateMetadata(method.getDeclaringClass(), method);
|
||||
|
||||
assertEquals("/advanced/test2", data.template().url());
|
||||
assertEquals("/advanced/test2?amount=" + encode("{amount}", UTF_8),
|
||||
data.template().url());
|
||||
assertEquals("PUT", data.template().method());
|
||||
assertEquals(MediaType.APPLICATION_JSON_VALUE,
|
||||
data.template().headers().get("Accept").iterator().next());
|
||||
@@ -255,6 +279,47 @@ public class SpringMvcContractTests {
|
||||
data.template().queries().get("amount").iterator().next());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testProcessAnnotations_DateTimeFormatParam() throws Exception {
|
||||
Method method = TestTemplate_DateTimeFormatParameter.class.getDeclaredMethod(
|
||||
"getTest", LocalDateTime.class);
|
||||
MethodMetadata data = this.contract
|
||||
.parseAndValidateMetadata(method.getDeclaringClass(), method);
|
||||
|
||||
Param.Expander expander = data.indexToExpander().get(0);
|
||||
assertNotNull(expander);
|
||||
|
||||
LocalDateTime input = LocalDateTime.of(2001, 10, 12, 23, 56, 3);
|
||||
|
||||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(
|
||||
TestTemplate_DateTimeFormatParameter.CUSTOM_PATTERN);
|
||||
|
||||
String expected = formatter.format(input);
|
||||
|
||||
assertEquals(expected, expander.expand(input));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testProcessAnnotations_NumberFormatParam() throws Exception {
|
||||
Method method = TestTemplate_NumberFormatParameter.class.getDeclaredMethod(
|
||||
"getTest", BigDecimal.class);
|
||||
MethodMetadata data = this.contract
|
||||
.parseAndValidateMetadata(method.getDeclaringClass(), method);
|
||||
|
||||
Param.Expander expander = data.indexToExpander().get(0);
|
||||
assertNotNull(expander);
|
||||
|
||||
NumberStyleFormatter formatter = new NumberStyleFormatter(
|
||||
TestTemplate_NumberFormatParameter.CUSTOM_PATTERN);
|
||||
|
||||
BigDecimal input = BigDecimal.valueOf(1220.345);
|
||||
|
||||
String expected = formatter.print(input, Locale.getDefault());
|
||||
String actual = expander.expand(input);
|
||||
|
||||
assertEquals(expected, actual);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testProcessAnnotations_Advanced2() throws Exception {
|
||||
Method method = TestTemplate_Advanced.class.getDeclaredMethod("getTest");
|
||||
@@ -273,7 +338,7 @@ public class SpringMvcContractTests {
|
||||
MethodMetadata data = this.contract
|
||||
.parseAndValidateMetadata(method.getDeclaringClass(), method);
|
||||
|
||||
assertEquals("", data.template().url());
|
||||
assertEquals("/", data.template().url());
|
||||
assertEquals("GET", data.template().method());
|
||||
assertEquals(MediaType.APPLICATION_JSON_VALUE,
|
||||
data.template().headers().get("Accept").iterator().next());
|
||||
@@ -286,7 +351,8 @@ public class SpringMvcContractTests {
|
||||
MethodMetadata data = this.contract
|
||||
.parseAndValidateMetadata(method.getDeclaringClass(), method);
|
||||
|
||||
assertEquals("/test", data.template().url());
|
||||
assertEquals("/test?id=" + encode("{id}", UTF_8),
|
||||
data.template().url());
|
||||
assertEquals("GET", data.template().method());
|
||||
assertEquals("[{id}]", data.template().queries().get("id").toString());
|
||||
assertNotNull(data.indexToExpander().get(0));
|
||||
@@ -299,7 +365,7 @@ public class SpringMvcContractTests {
|
||||
MethodMetadata data = this.contract
|
||||
.parseAndValidateMetadata(method.getDeclaringClass(), method);
|
||||
|
||||
assertEquals("/test", data.template().url());
|
||||
assertEquals("/test?id=" + encode("{id}", UTF_8), data.template().url());
|
||||
assertEquals("GET", data.template().method());
|
||||
assertEquals("[{id}]", data.template().queries().get("id").toString());
|
||||
assertNotNull(data.indexToExpander().get(0));
|
||||
@@ -339,7 +405,7 @@ public class SpringMvcContractTests {
|
||||
|
||||
assertEquals("/test/{id}", data.template().url());
|
||||
assertEquals("GET", data.template().method());
|
||||
assertEquals(true, data.template().headers().isEmpty());
|
||||
assertTrue(data.template().headers().isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -352,7 +418,8 @@ public class SpringMvcContractTests {
|
||||
MethodMetadata data = this.contract
|
||||
.parseAndValidateMetadata(method.getDeclaringClass(), method);
|
||||
|
||||
assertEquals("/advanced/testfallback/{id}", data.template().url());
|
||||
assertEquals("/advanced/testfallback/{id}?amount=" + encode("{amount}", UTF_8), data
|
||||
.template().url());
|
||||
assertEquals("PUT", data.template().method());
|
||||
assertEquals(MediaType.APPLICATION_JSON_VALUE,
|
||||
data.template().headers().get("Accept").iterator().next());
|
||||
@@ -422,7 +489,23 @@ public class SpringMvcContractTests {
|
||||
MethodMetadata data = this.contract
|
||||
.parseAndValidateMetadata(method.getDeclaringClass(), method);
|
||||
|
||||
assertEquals("/queryMap", data.template().url());
|
||||
assertEquals("/queryMap?aParam=" + encode("{aParam}", UTF_8),
|
||||
data.template().url());
|
||||
assertEquals("GET", data.template().method());
|
||||
assertEquals(0, data.queryMapIndex().intValue());
|
||||
Map<String, Collection<String>> params = data.template().queries();
|
||||
assertEquals("{aParam}", params.get("aParam").iterator().next());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testProcessQueryMapObject() throws Exception {
|
||||
Method method = TestTemplate_QueryMap.class.getDeclaredMethod("queryMapObject",
|
||||
TestObject.class, String.class);
|
||||
MethodMetadata data = this.contract
|
||||
.parseAndValidateMetadata(method.getDeclaringClass(), method);
|
||||
|
||||
assertEquals("/queryMapObject?aParam=" + encode("{aParam}", UTF_8),
|
||||
data.template().url());
|
||||
assertEquals("GET", data.template().method());
|
||||
assertEquals(0, data.queryMapIndex().intValue());
|
||||
Map<String, Collection<String>> params = data.template().queries();
|
||||
@@ -514,6 +597,11 @@ public class SpringMvcContractTests {
|
||||
String queryMapMoreThanOnce(
|
||||
@RequestParam MultiValueMap<String, String> queryMap1,
|
||||
@RequestParam MultiValueMap<String, String> queryMap2);
|
||||
|
||||
@RequestMapping(path = "/queryMapObject")
|
||||
String queryMapObject(
|
||||
@SpringQueryMap TestObject queryMap,
|
||||
@RequestParam(name = "aParam") String aParam);
|
||||
}
|
||||
|
||||
@JsonAutoDetect
|
||||
@@ -539,6 +627,24 @@ public class SpringMvcContractTests {
|
||||
TestObject getTest();
|
||||
}
|
||||
|
||||
public interface TestTemplate_DateTimeFormatParameter {
|
||||
|
||||
String CUSTOM_PATTERN = "dd-MM-yyyy HH:mm";
|
||||
|
||||
@RequestMapping(method = RequestMethod.GET)
|
||||
String getTest(@RequestParam(name = "localDateTime")
|
||||
@DateTimeFormat(pattern = CUSTOM_PATTERN) LocalDateTime localDateTime);
|
||||
}
|
||||
|
||||
public interface TestTemplate_NumberFormatParameter {
|
||||
|
||||
String CUSTOM_PATTERN = "$###,###.###";
|
||||
|
||||
@RequestMapping(method = RequestMethod.GET)
|
||||
String getTest(@RequestParam("amount")
|
||||
@NumberFormat(pattern = CUSTOM_PATTERN) BigDecimal amount);
|
||||
}
|
||||
|
||||
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE, setterVisibility = JsonAutoDetect.Visibility.NONE)
|
||||
public class TestObject {
|
||||
|
||||
|
||||
+5
-5
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
* Copyright 2013-2018 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.
|
||||
@@ -21,6 +21,8 @@ import java.io.IOException;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import feign.Client;
|
||||
import feign.httpclient.ApacheHttpClient;
|
||||
import org.apache.http.Header;
|
||||
import org.apache.http.StatusLine;
|
||||
import org.apache.http.client.HttpClient;
|
||||
@@ -36,6 +38,7 @@ import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.MockingDetails;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.SpringBootConfiguration;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
@@ -55,14 +58,11 @@ import org.springframework.util.ReflectionUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.mockingDetails;
|
||||
|
||||
import feign.Client;
|
||||
import feign.httpclient.ApacheHttpClient;
|
||||
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright 2013-2018 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
|
||||
*
|
||||
* http://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.test;
|
||||
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
|
||||
@Configuration
|
||||
public class NoSecurityConfiguration extends WebSecurityConfigurerAdapter {
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http.authorizeRequests()
|
||||
.anyRequest().permitAll()
|
||||
.and()
|
||||
.csrf().disable();
|
||||
}
|
||||
}
|
||||
+27
@@ -20,6 +20,7 @@ import java.lang.reflect.InvocationHandler;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.text.ParseException;
|
||||
import java.time.LocalDate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
@@ -47,10 +48,13 @@ import org.springframework.cloud.openfeign.support.FallbackCommand;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonClient;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonClients;
|
||||
import org.springframework.cloud.netflix.ribbon.StaticServerList;
|
||||
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.format.Formatter;
|
||||
import org.springframework.format.FormatterRegistry;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
@@ -94,6 +98,7 @@ import rx.Single;
|
||||
* @author Spencer Gibb
|
||||
* @author Jakub Narloch
|
||||
* @author Erik Kringen
|
||||
* @author Halvdan Hoem Grelland
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@SpringBootTest(classes = FeignClientTests.Application.class, webEnvironment = WebEnvironment.RANDOM_PORT, value = {
|
||||
@@ -194,6 +199,11 @@ public class FeignClientTests {
|
||||
@RequestMapping(method = RequestMethod.GET, path = "/helloparams")
|
||||
List<String> getParams(@RequestParam("params") List<String> params);
|
||||
|
||||
@RequestMapping(method = RequestMethod.GET, path = "/formattedparams")
|
||||
List<LocalDate> getFormattedParams(
|
||||
@RequestParam("params")
|
||||
@DateTimeFormat(pattern = "dd-MM-yyyy") List<LocalDate> params);
|
||||
|
||||
@RequestMapping(method = RequestMethod.GET, path = "/hellos")
|
||||
HystrixCommand<List<Hello>> getHellosHystrix();
|
||||
|
||||
@@ -398,6 +408,7 @@ public class FeignClientTests {
|
||||
@RibbonClient(name = "localapp6", configuration = LocalRibbonClientConfiguration.class),
|
||||
@RibbonClient(name = "localapp7", configuration = LocalRibbonClientConfiguration.class)
|
||||
})
|
||||
@Import(NoSecurityConfiguration.class)
|
||||
protected static class Application {
|
||||
|
||||
// needs to be in parent context to test multiple HystrixClient beans
|
||||
@@ -491,6 +502,13 @@ public class FeignClientTests {
|
||||
return params;
|
||||
}
|
||||
|
||||
@RequestMapping(method = RequestMethod.GET, path = "/formattedparams")
|
||||
public List<LocalDate> getFormattedParams(
|
||||
@RequestParam("params")
|
||||
@DateTimeFormat(pattern = "dd-MM-yyyy") List<LocalDate> params) {
|
||||
return params;
|
||||
}
|
||||
|
||||
@RequestMapping(method = RequestMethod.GET, path = "/noContent")
|
||||
ResponseEntity<Void> noContent() {
|
||||
return ResponseEntity.noContent().build();
|
||||
@@ -633,6 +651,15 @@ public class FeignClientTests {
|
||||
assertEquals("params size was wrong", list.size(), params.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
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);
|
||||
assertNotNull("params was null", params);
|
||||
assertEquals("params not converted correctly", list, params);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHystrixCommand() throws NoSuchMethodException {
|
||||
HystrixCommand<List<Hello>> command = this.testClient.getHellosHystrix();
|
||||
|
||||
+9
-2
@@ -25,13 +25,16 @@ import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
|
||||
import org.springframework.boot.web.server.LocalServerPort;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonClients;
|
||||
import org.springframework.cloud.openfeign.EnableFeignClients;
|
||||
import org.springframework.cloud.openfeign.FeignClient;
|
||||
import org.springframework.cloud.openfeign.ribbon.LoadBalancerFeignClient;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonClient;
|
||||
import org.springframework.cloud.netflix.ribbon.StaticServerList;
|
||||
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.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
@@ -95,7 +98,7 @@ public class FeignHttpClientTests {
|
||||
User getUser(@PathVariable("id") long id);
|
||||
}
|
||||
|
||||
@FeignClient("localapp")
|
||||
@FeignClient("localapp1")
|
||||
protected interface UserClient extends UserService {
|
||||
}
|
||||
|
||||
@@ -103,7 +106,11 @@ public class FeignHttpClientTests {
|
||||
@EnableAutoConfiguration
|
||||
@RestController
|
||||
@EnableFeignClients(clients = { TestClient.class, UserClient.class })
|
||||
@RibbonClient(name = "localapp", configuration = LocalRibbonClientConfiguration.class)
|
||||
@RibbonClients({
|
||||
@RibbonClient(name = "localapp", configuration = LocalRibbonClientConfiguration.class),
|
||||
@RibbonClient(name = "localapp1", configuration = LocalRibbonClientConfiguration.class)
|
||||
})
|
||||
@Import(NoSecurityConfiguration.class)
|
||||
protected static class Application implements UserService {
|
||||
|
||||
@RequestMapping(method = RequestMethod.GET, value = "/hello")
|
||||
|
||||
+9
-2
@@ -24,13 +24,16 @@ import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonClients;
|
||||
import org.springframework.cloud.openfeign.EnableFeignClients;
|
||||
import org.springframework.cloud.openfeign.FeignClient;
|
||||
import org.springframework.cloud.openfeign.ribbon.LoadBalancerFeignClient;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonClient;
|
||||
import org.springframework.cloud.netflix.ribbon.StaticServerList;
|
||||
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.ResponseEntity;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
@@ -96,7 +99,7 @@ public class FeignOkHttpTests {
|
||||
User getUser(@PathVariable("id") long id);
|
||||
}
|
||||
|
||||
@FeignClient("localapp")
|
||||
@FeignClient("localapp1")
|
||||
protected interface UserClient extends UserService {
|
||||
}
|
||||
|
||||
@@ -104,7 +107,11 @@ public class FeignOkHttpTests {
|
||||
@EnableAutoConfiguration
|
||||
@RestController
|
||||
@EnableFeignClients(clients = { TestClient.class, UserClient.class })
|
||||
@RibbonClient(name = "localapp", configuration = LocalRibbonClientConfiguration.class)
|
||||
@RibbonClients({
|
||||
@RibbonClient(name = "localapp", configuration = LocalRibbonClientConfiguration.class),
|
||||
@RibbonClient(name = "localapp1", configuration = LocalRibbonClientConfiguration.class)
|
||||
})
|
||||
@Import(NoSecurityConfiguration.class)
|
||||
protected static class Application implements UserService {
|
||||
|
||||
@RequestMapping(method = RequestMethod.GET, value = "/hello")
|
||||
|
||||
+3
@@ -24,11 +24,13 @@ import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.cloud.openfeign.EnableFeignClients;
|
||||
import org.springframework.cloud.openfeign.test.NoSecurityConfiguration;
|
||||
import org.springframework.cloud.openfeign.testclients.TestClient;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonClient;
|
||||
import org.springframework.cloud.netflix.ribbon.StaticServerList;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
@@ -66,6 +68,7 @@ public class FeignClientEnvVarTests {
|
||||
@RestController
|
||||
@EnableFeignClients(basePackages = {"${basepackage}"})
|
||||
@RibbonClient(name = "localapp", configuration = LocalRibbonClientConfiguration.class)
|
||||
@Import(NoSecurityConfiguration.class)
|
||||
protected static class Application {
|
||||
@RequestMapping(method = RequestMethod.GET, value = "/hello")
|
||||
public String getHello() {
|
||||
|
||||
+12
-10
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2015 the original author or authors.
|
||||
* Copyright 2013-2018 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,30 +16,31 @@
|
||||
|
||||
package org.springframework.cloud.openfeign.valid.scanning;
|
||||
|
||||
import com.netflix.loadbalancer.Server;
|
||||
import com.netflix.loadbalancer.ServerList;
|
||||
import feign.Client;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonClients;
|
||||
import org.springframework.cloud.netflix.ribbon.StaticServerList;
|
||||
import org.springframework.cloud.openfeign.EnableFeignClients;
|
||||
import org.springframework.cloud.openfeign.FeignClient;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonClient;
|
||||
import org.springframework.cloud.netflix.ribbon.StaticServerList;
|
||||
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.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import com.netflix.loadbalancer.Server;
|
||||
import com.netflix.loadbalancer.ServerList;
|
||||
|
||||
import feign.Client;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
||||
@@ -65,7 +66,7 @@ public class FeignClientScanningTests {
|
||||
@SuppressWarnings("unused")
|
||||
private Client feignClient;
|
||||
|
||||
@FeignClient("localapp")
|
||||
@FeignClient("localapp123")
|
||||
protected interface TestClient {
|
||||
@RequestMapping(method = RequestMethod.GET, value = "/hello")
|
||||
String getHello();
|
||||
@@ -81,7 +82,8 @@ public class FeignClientScanningTests {
|
||||
@EnableAutoConfiguration
|
||||
@RestController
|
||||
@EnableFeignClients // NO clients attribute. That's what this class is testing!
|
||||
@RibbonClient(name = "localapp", configuration = LocalRibbonClientConfiguration.class)
|
||||
@RibbonClients(defaultConfiguration = LocalRibbonClientConfiguration.class)
|
||||
@Import(NoSecurityConfiguration.class)
|
||||
protected static class Application {
|
||||
@RequestMapping(method = RequestMethod.GET, value = "/hello")
|
||||
public String getHello() {
|
||||
|
||||
@@ -5,16 +5,15 @@
|
||||
<parent>
|
||||
<artifactId>spring-cloud-dependencies-parent</artifactId>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<version>2.0.4.RELEASE</version>
|
||||
<relativePath/>
|
||||
<version>2.1.0.RC3</version> <relativePath/>
|
||||
</parent>
|
||||
<artifactId>spring-cloud-openfeign-dependencies</artifactId>
|
||||
<version>2.0.3.BUILD-SNAPSHOT</version>
|
||||
<version>2.1.0.RC3</version>
|
||||
<packaging>pom</packaging>
|
||||
<name>spring-cloud-openfeign-dependencies</name>
|
||||
<description>Spring Cloud OpenFeign Dependencies</description>
|
||||
<properties>
|
||||
<feign.version>9.7.0</feign.version>
|
||||
<feign.version>10.1.0</feign.version>
|
||||
<feign-form.version>3.3.0</feign-form.version>
|
||||
</properties>
|
||||
<dependencyManagement>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-openfeign</artifactId>
|
||||
<version>2.0.3.BUILD-SNAPSHOT</version>
|
||||
<version>2.1.0.RC3</version>
|
||||
<relativePath>..</relativePath>
|
||||
</parent>
|
||||
<artifactId>spring-cloud-starter-openfeign</artifactId>
|
||||
|
||||
Reference in New Issue
Block a user