diff --git a/core/src/main/java/feign/AsyncFeign.java b/core/src/main/java/feign/AsyncFeign.java index b9ff43843..cc5f02762 100644 --- a/core/src/main/java/feign/AsyncFeign.java +++ b/core/src/main/java/feign/AsyncFeign.java @@ -110,7 +110,7 @@ public AsyncFeign build() { decoder, errorDecoder, dismiss404, - closeAfterDecode), + closeAfterDecode, responseInterceptor), AsyncResponseHandler.class, capabilities); @@ -126,6 +126,7 @@ public AsyncFeign build() { .queryMapEncoder(queryMapEncoder) .options(options) .requestInterceptors(requestInterceptors) + .responseInterceptor(responseInterceptor) .invocationHandlerFactory(invocationHandlerFactory) .build(), defaultContextSupplier, activeContextHolder); } diff --git a/core/src/main/java/feign/AsyncResponseHandler.java b/core/src/main/java/feign/AsyncResponseHandler.java index 90675e11b..b73439d36 100644 --- a/core/src/main/java/feign/AsyncResponseHandler.java +++ b/core/src/main/java/feign/AsyncResponseHandler.java @@ -15,13 +15,12 @@ import static feign.FeignException.errorReading; import static feign.Util.ensureClosed; -import java.io.IOException; -import java.lang.reflect.Type; -import java.util.concurrent.CompletableFuture; import feign.Logger.Level; -import feign.codec.DecodeException; import feign.codec.Decoder; import feign.codec.ErrorDecoder; +import java.io.IOException; +import java.lang.reflect.Type; +import java.util.concurrent.CompletableFuture; /** * The response handler that is used to provide asynchronous support on top of standard response @@ -40,8 +39,10 @@ class AsyncResponseHandler { private final boolean dismiss404; private final boolean closeAfterDecode; + private final ResponseInterceptor responseInterceptor; + AsyncResponseHandler(Level logLevel, Logger logger, Decoder decoder, ErrorDecoder errorDecoder, - boolean dismiss404, boolean closeAfterDecode) { + boolean dismiss404, boolean closeAfterDecode, ResponseInterceptor responseInterceptor) { super(); this.logLevel = logLevel; this.logger = logger; @@ -49,6 +50,7 @@ class AsyncResponseHandler { this.errorDecoder = errorDecoder; this.dismiss404 = dismiss404; this.closeAfterDecode = closeAfterDecode; + this.responseInterceptor = responseInterceptor; } boolean isVoidType(Type returnType) { @@ -111,12 +113,6 @@ void handleResponse(CompletableFuture resultFuture, } Object decode(Response response, Type type) throws IOException { - try { - return decoder.decode(response, type); - } catch (final FeignException e) { - throw e; - } catch (final RuntimeException e) { - throw new DecodeException(response.status(), e.getMessage(), response.request(), e); - } + return responseInterceptor.aroundDecode(new InvocationContext(decoder, type, response)); } } diff --git a/core/src/main/java/feign/BaseBuilder.java b/core/src/main/java/feign/BaseBuilder.java index 9fd9a2433..ae12d4f9d 100644 --- a/core/src/main/java/feign/BaseBuilder.java +++ b/core/src/main/java/feign/BaseBuilder.java @@ -35,6 +35,7 @@ public abstract class BaseBuilder> { protected final List requestInterceptors = new ArrayList<>(); + protected ResponseInterceptor responseInterceptor = ResponseInterceptor.DEFAULT; protected Logger.Level logLevel = Logger.Level.NONE; protected Contract contract = new Contract.Default(); protected Retryer retryer = new Retryer.Default(); @@ -196,6 +197,15 @@ public B requestInterceptors(Iterable requestInterceptors) { return thisB; } + /** + * Adds a single response interceptor to the builder. + */ + public B responseInterceptor(ResponseInterceptor responseInterceptor) { + this.responseInterceptor = responseInterceptor; + return thisB; + } + + /** * Allows you to override how reflective dispatch works inside of Feign. */ diff --git a/core/src/main/java/feign/Capability.java b/core/src/main/java/feign/Capability.java index ccd93ed65..f27e9a859 100644 --- a/core/src/main/java/feign/Capability.java +++ b/core/src/main/java/feign/Capability.java @@ -86,6 +86,10 @@ default RequestInterceptor enrich(RequestInterceptor requestInterceptor) { return requestInterceptor; } + default ResponseInterceptor enrich(ResponseInterceptor responseInterceptor) { + return responseInterceptor; + } + default Logger enrich(Logger logger) { return logger; } diff --git a/core/src/main/java/feign/Feign.java b/core/src/main/java/feign/Feign.java index cb77bd235..fe543fbe0 100644 --- a/core/src/main/java/feign/Feign.java +++ b/core/src/main/java/feign/Feign.java @@ -96,6 +96,7 @@ public static class Builder extends BaseBuilder { public Builder client(Client client) { this.client = client; + return this; } @@ -119,8 +120,9 @@ public Feign build() { super.enrich(); SynchronousMethodHandler.Factory synchronousMethodHandlerFactory = - new SynchronousMethodHandler.Factory(client, retryer, requestInterceptors, logger, - logLevel, dismiss404, closeAfterDecode, propagationPolicy, forceDecoding); + new SynchronousMethodHandler.Factory(client, retryer, requestInterceptors, + responseInterceptor, logger, logLevel, dismiss404, closeAfterDecode, + propagationPolicy, forceDecoding); ParseHandlersByName handlersByName = new ParseHandlersByName(contract, options, encoder, decoder, queryMapEncoder, errorDecoder, synchronousMethodHandlerFactory); diff --git a/core/src/main/java/feign/InvocationContext.java b/core/src/main/java/feign/InvocationContext.java new file mode 100644 index 000000000..f4d96e799 --- /dev/null +++ b/core/src/main/java/feign/InvocationContext.java @@ -0,0 +1,58 @@ +/* + * Copyright 2012-2022 The Feign 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 feign; + +import static feign.FeignException.errorReading; +import feign.codec.DecodeException; +import feign.codec.Decoder; +import java.io.IOException; +import java.lang.reflect.Type; + +public class InvocationContext { + + private final Decoder decoder; + private final Type returnType; + private final Response response; + + InvocationContext(Decoder decoder, Type returnType, Response response) { + this.decoder = decoder; + this.returnType = returnType; + this.response = response; + } + + public Object proceed() { + try { + return decoder.decode(response, returnType); + } catch (final FeignException e) { + throw e; + } catch (final RuntimeException e) { + throw new DecodeException(response.status(), e.getMessage(), response.request(), e); + } catch (IOException e) { + throw errorReading(response.request(), response, e); + } + } + + public Decoder decoder() { + return decoder; + } + + public Type returnType() { + return returnType; + } + + public Response response() { + return response; + } + +} diff --git a/core/src/main/java/feign/ResponseInterceptor.java b/core/src/main/java/feign/ResponseInterceptor.java new file mode 100644 index 000000000..d89127322 --- /dev/null +++ b/core/src/main/java/feign/ResponseInterceptor.java @@ -0,0 +1,38 @@ +/* + * Copyright 2012-2022 The Feign 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 feign; + +import java.io.IOException; +import java.util.function.Function; + +/** + * Zero or One {@code ResponseInterceptor} may be configured for purposes such as verify or modify + * headers of response, verify the business status of decoded object. Once interceptors are applied, + * {@link ResponseInterceptor#aroundDecode(Response, Function)} is called around decode method + * called + */ +public interface ResponseInterceptor { + + ResponseInterceptor DEFAULT = InvocationContext::proceed; + + /** + * Called for response around decode, must either manually invoke + * {@link InvocationContext#proceed} or manually create a new response object + * + * @param invocationContext information surrounding the response been decoded + * @return decoded response + */ + Object aroundDecode(InvocationContext invocationContext) throws IOException; + +} diff --git a/core/src/main/java/feign/SynchronousMethodHandler.java b/core/src/main/java/feign/SynchronousMethodHandler.java index 1d8f39a0d..b15f2dda7 100644 --- a/core/src/main/java/feign/SynchronousMethodHandler.java +++ b/core/src/main/java/feign/SynchronousMethodHandler.java @@ -13,19 +13,19 @@ */ package feign; +import static feign.ExceptionPropagationPolicy.UNWRAP; +import static feign.FeignException.errorExecuting; +import static feign.Util.checkNotNull; +import feign.InvocationHandlerFactory.MethodHandler; +import feign.Request.Options; +import feign.codec.Decoder; +import feign.codec.ErrorDecoder; import java.io.IOException; import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionException; import java.util.concurrent.TimeUnit; import java.util.stream.Stream; -import feign.InvocationHandlerFactory.MethodHandler; -import feign.Request.Options; -import feign.codec.Decoder; -import feign.codec.ErrorDecoder; -import static feign.ExceptionPropagationPolicy.UNWRAP; -import static feign.FeignException.errorExecuting; -import static feign.Util.checkNotNull; final class SynchronousMethodHandler implements MethodHandler { @@ -36,6 +36,7 @@ final class SynchronousMethodHandler implements MethodHandler { private final Client client; private final Retryer retryer; private final List requestInterceptors; + private final ResponseInterceptor responseInterceptor; private final Logger logger; private final Logger.Level logLevel; private final RequestTemplate.Factory buildTemplateFromArgs; @@ -48,8 +49,8 @@ final class SynchronousMethodHandler implements MethodHandler { private SynchronousMethodHandler(Target target, Client client, Retryer retryer, - List requestInterceptors, Logger logger, - Logger.Level logLevel, MethodMetadata metadata, + List requestInterceptors, ResponseInterceptor responseInterceptor, + Logger logger, Logger.Level logLevel, MethodMetadata metadata, RequestTemplate.Factory buildTemplateFromArgs, Options options, Decoder decoder, ErrorDecoder errorDecoder, boolean dismiss404, boolean closeAfterDecode, ExceptionPropagationPolicy propagationPolicy, @@ -66,6 +67,7 @@ private SynchronousMethodHandler(Target target, Client client, Retryer retrye this.buildTemplateFromArgs = checkNotNull(buildTemplateFromArgs, "metadata for %s", target); this.options = checkNotNull(options, "options for %s", target); this.propagationPolicy = propagationPolicy; + this.responseInterceptor = responseInterceptor; if (forceDecoding) { // internal only: usual handling will be short-circuited, and all responses will be passed to @@ -75,7 +77,7 @@ private SynchronousMethodHandler(Target target, Client client, Retryer retrye } else { this.decoder = null; this.asyncResponseHandler = new AsyncResponseHandler(logLevel, logger, decoder, errorDecoder, - dismiss404, closeAfterDecode); + dismiss404, closeAfterDecode, responseInterceptor); } } @@ -130,19 +132,18 @@ Object executeAndDecode(RequestTemplate template, Options options) throws Throwa } long elapsedTime = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start); - - if (decoder != null) - return decoder.decode(response, metadata.returnType()); + if (decoder != null) { + return responseInterceptor + .aroundDecode(new InvocationContext(decoder, metadata.returnType(), response)); + } CompletableFuture resultFuture = new CompletableFuture<>(); asyncResponseHandler.handleResponse(resultFuture, metadata.configKey(), response, - metadata.returnType(), - elapsedTime); + metadata.returnType(), elapsedTime); try { if (!resultFuture.isDone()) throw new IllegalStateException("Response handling not done"); - return resultFuture.join(); } catch (CompletionException e) { Throwable cause = e.getCause(); @@ -179,6 +180,7 @@ static class Factory { private final Client client; private final Retryer retryer; private final List requestInterceptors; + private final ResponseInterceptor responseInterceptor; private final Logger logger; private final Logger.Level logLevel; private final boolean dismiss404; @@ -187,11 +189,13 @@ static class Factory { private final boolean forceDecoding; Factory(Client client, Retryer retryer, List requestInterceptors, + ResponseInterceptor responseInterceptor, Logger logger, Logger.Level logLevel, boolean dismiss404, boolean closeAfterDecode, ExceptionPropagationPolicy propagationPolicy, boolean forceDecoding) { this.client = checkNotNull(client, "client"); this.retryer = checkNotNull(retryer, "retryer"); this.requestInterceptors = checkNotNull(requestInterceptors, "requestInterceptors"); + this.responseInterceptor = responseInterceptor; this.logger = checkNotNull(logger, "logger"); this.logLevel = checkNotNull(logLevel, "logLevel"); this.dismiss404 = dismiss404; @@ -206,8 +210,8 @@ public MethodHandler create(Target target, Options options, Decoder decoder, ErrorDecoder errorDecoder) { - return new SynchronousMethodHandler(target, client, retryer, requestInterceptors, logger, - logLevel, md, buildTemplateFromArgs, options, decoder, + return new SynchronousMethodHandler(target, client, retryer, requestInterceptors, + responseInterceptor, logger, logLevel, md, buildTemplateFromArgs, options, decoder, errorDecoder, dismiss404, closeAfterDecode, propagationPolicy, forceDecoding); } } diff --git a/core/src/test/java/feign/AsyncFeignTest.java b/core/src/test/java/feign/AsyncFeignTest.java index ce7f603b4..d89ca1cd7 100644 --- a/core/src/test/java/feign/AsyncFeignTest.java +++ b/core/src/test/java/feign/AsyncFeignTest.java @@ -13,13 +13,26 @@ */ package feign; -import static feign.Util.*; +import static feign.Util.UTF_8; import static feign.assertj.MockWebServerAssertions.assertThat; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.data.MapEntry.entry; -import static org.hamcrest.CoreMatchers.*; -import static org.junit.Assert.*; +import static org.hamcrest.CoreMatchers.isA; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; +import feign.Feign.ResponseMappingDecoder; +import feign.Request.HttpMethod; +import feign.Target.HardCodedTarget; +import feign.codec.DecodeException; +import feign.codec.Decoder; +import feign.codec.EncodeException; +import feign.codec.Encoder; +import feign.codec.ErrorDecoder; +import feign.codec.StringDecoder; +import feign.querymap.BeanQueryMapEncoder; +import feign.querymap.FieldQueryMapEncoder; import java.io.IOException; import java.lang.reflect.Type; import java.net.URI; @@ -39,25 +52,12 @@ import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExpectedException; -import com.google.gson.Gson; -import com.google.gson.reflect.TypeToken; -import feign.Feign.ResponseMappingDecoder; -import feign.Request.HttpMethod; -import feign.Target.HardCodedTarget; -import feign.codec.DecodeException; -import feign.codec.Decoder; -import feign.codec.EncodeException; -import feign.codec.Encoder; -import feign.codec.ErrorDecoder; -import feign.codec.StringDecoder; -import feign.querymap.BeanQueryMapEncoder; -import feign.querymap.FieldQueryMapEncoder; import okhttp3.mockwebserver.MockResponse; import okhttp3.mockwebserver.MockWebServer; import okio.Buffer; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; public class AsyncFeignTest { @@ -131,7 +131,7 @@ public void postBodyParam() throws Exception { public void bodyTypeCorrespondsWithParameterType() throws Exception { server.enqueue(new MockResponse().setBody("foo")); - final AtomicReference encodedType = new AtomicReference(); + final AtomicReference encodedType = new AtomicReference<>(); TestInterfaceAsync api = new TestInterfaceAsyncBuilder().encoder(new Encoder.Default() { @Override public void encode(Object object, Type bodyType, RequestTemplate template) { @@ -261,7 +261,7 @@ public void headerMap() throws Exception { TestInterfaceAsync api = new TestInterfaceAsyncBuilder().target("http://localhost:" + server.getPort()); - Map headerMap = new LinkedHashMap(); + Map headerMap = new LinkedHashMap<>(); headerMap.put("Content-Type", "myContent"); headerMap.put("Custom-Header", "fooValue"); CompletableFuture cf = api.headerMap(headerMap); @@ -279,7 +279,7 @@ public void headerMapWithHeaderAnnotations() throws Exception { TestInterfaceAsync api = new TestInterfaceAsyncBuilder().target("http://localhost:" + server.getPort()); - Map headerMap = new LinkedHashMap(); + Map headerMap = new LinkedHashMap<>(); headerMap.put("Custom-Header", "fooValue"); api.headerMapWithHeaderAnnotations(headerMap); @@ -310,7 +310,7 @@ public void queryMap() throws Exception { TestInterfaceAsync api = new TestInterfaceAsyncBuilder().target("http://localhost:" + server.getPort()); - Map queryMap = new LinkedHashMap(); + Map queryMap = new LinkedHashMap<>(); queryMap.put("name", "alice"); queryMap.put("fooKey", "fooValue"); CompletableFuture cf = api.queryMap(queryMap); @@ -327,7 +327,7 @@ public void queryMapIterableValuesExpanded() throws Exception { TestInterfaceAsync api = new TestInterfaceAsyncBuilder().target("http://localhost:" + server.getPort()); - Map queryMap = new LinkedHashMap(); + Map queryMap = new LinkedHashMap<>(); queryMap.put("name", Arrays.asList("Alice", "Bob")); queryMap.put("fooKey", "fooValue"); queryMap.put("emptyListKey", new ArrayList()); @@ -346,21 +346,21 @@ public void queryMapWithQueryParams() throws Exception { new TestInterfaceAsyncBuilder().target("http://localhost:" + server.getPort()); server.enqueue(new MockResponse()); - Map queryMap = new LinkedHashMap(); + Map queryMap = new LinkedHashMap<>(); queryMap.put("fooKey", "fooValue"); api.queryMapWithQueryParams("alice", queryMap); // query map should be expanded after built-in parameters assertThat(server.takeRequest()).hasPath("/?name=alice&fooKey=fooValue"); server.enqueue(new MockResponse()); - queryMap = new LinkedHashMap(); + queryMap = new LinkedHashMap<>(); queryMap.put("name", "bob"); api.queryMapWithQueryParams("alice", queryMap); // queries are additive assertThat(server.takeRequest()).hasPath("/?name=alice&name=bob"); server.enqueue(new MockResponse()); - queryMap = new LinkedHashMap(); + queryMap = new LinkedHashMap<>(); queryMap.put("name", null); api.queryMapWithQueryParams("alice", queryMap); // null value for a query map key removes query parameter @@ -373,25 +373,25 @@ public void queryMapValueStartingWithBrace() throws Exception { new TestInterfaceAsyncBuilder().target("http://localhost:" + server.getPort()); server.enqueue(new MockResponse()); - Map queryMap = new LinkedHashMap(); + Map queryMap = new LinkedHashMap<>(); queryMap.put("name", "{alice"); api.queryMap(queryMap); assertThat(server.takeRequest()).hasPath("/?name=%7Balice"); server.enqueue(new MockResponse()); - queryMap = new LinkedHashMap(); + queryMap = new LinkedHashMap<>(); queryMap.put("{name", "alice"); api.queryMap(queryMap); assertThat(server.takeRequest()).hasPath("/?%7Bname=alice"); server.enqueue(new MockResponse()); - queryMap = new LinkedHashMap(); + queryMap = new LinkedHashMap<>(); queryMap.put("name", "%7Balice"); api.queryMapEncoded(queryMap); assertThat(server.takeRequest()).hasPath("/?name=%7Balice"); server.enqueue(new MockResponse()); - queryMap = new LinkedHashMap(); + queryMap = new LinkedHashMap<>(); queryMap.put("%7Bname", "%7Balice"); api.queryMapEncoded(queryMap); assertThat(server.takeRequest()).hasPath("/?%7Bname=%7Balice"); @@ -478,12 +478,8 @@ public void canOverrideErrorDecoder() throws Throwable { public void overrideTypeSpecificDecoder() throws Throwable { server.enqueue(new MockResponse().setBody("success!")); - TestInterfaceAsync api = new TestInterfaceAsyncBuilder().decoder(new Decoder() { - @Override - public Object decode(Response response, Type type) { - return "fail"; - } - }).target("http://localhost:" + server.getPort()); + TestInterfaceAsync api = new TestInterfaceAsyncBuilder().decoder((response, type) -> "fail") + .target("http://localhost:" + server.getPort()); assertEquals("fail", unwrap(api.post())); } @@ -494,11 +490,8 @@ public void doesntRetryAfterResponseIsSent() throws Throwable { thrown.expect(FeignException.class); thrown.expectMessage("timeout reading POST http://"); - TestInterfaceAsync api = new TestInterfaceAsyncBuilder().decoder(new Decoder() { - @Override - public Object decode(Response response, Type type) throws IOException { - throw new IOException("timeout"); - } + TestInterfaceAsync api = new TestInterfaceAsyncBuilder().decoder((response, type) -> { + throw new IOException("timeout"); }).target("http://localhost:" + server.getPort()); CompletableFuture cf = api.post(); @@ -520,7 +513,7 @@ public void throwsFeignExceptionIncludingBody() throws Throwable { unwrap(cf); } catch (FeignException e) { assertThat(e.getMessage()) - .isEqualTo("timeout reading POST http://localhost:" + server.getPort() + "/"); + .contains("timeout reading POST http://localhost:" + server.getPort() + "/"); assertThat(e.contentUTF8()).isEqualTo("Request body"); return; } @@ -547,7 +540,7 @@ public void throwsFeignExceptionWithoutBody() { @SuppressWarnings("deprecation") @Test public void whenReturnTypeIsResponseNoErrorHandling() throws Throwable { - Map> headers = new LinkedHashMap>(); + Map> headers = new LinkedHashMap<>(); headers.put("Location", Arrays.asList("http://bar.com")); final Response response = Response.builder().status(302).reason("Found").headers(headers) .request(Request.create(HttpMethod.GET, "/", Collections.emptyMap(), null, Util.UTF_8)) @@ -572,11 +565,8 @@ public void okIfDecodeRootCauseHasNoMessage() throws Throwable { server.enqueue(new MockResponse().setBody("success!")); thrown.expect(DecodeException.class); - TestInterfaceAsync api = new TestInterfaceAsyncBuilder().decoder(new Decoder() { - @Override - public Object decode(Response response, Type type) throws IOException { - throw new RuntimeException(); - } + TestInterfaceAsync api = new TestInterfaceAsyncBuilder().decoder((response, type) -> { + throw new RuntimeException(); }).target("http://localhost:" + server.getPort()); unwrap(api.post()); @@ -588,13 +578,11 @@ public void decodingExceptionGetWrappedInDismiss404Mode() throws Throwable { thrown.expect(DecodeException.class); thrown.expectCause(isA(NoSuchElementException.class));; - TestInterfaceAsync api = new TestInterfaceAsyncBuilder().dismiss404().decoder(new Decoder() { - @Override - public Object decode(Response response, Type type) throws IOException { - assertEquals(404, response.status()); - throw new NoSuchElementException(); - } - }).target("http://localhost:" + server.getPort()); + TestInterfaceAsync api = + new TestInterfaceAsyncBuilder().dismiss404().decoder((response, type) -> { + assertEquals(404, response.status()); + throw new NoSuchElementException(); + }).target("http://localhost:" + server.getPort()); unwrap(api.post()); } @@ -618,12 +606,10 @@ public void okIfEncodeRootCauseHasNoMessage() throws Throwable { server.enqueue(new MockResponse().setBody("success!")); thrown.expect(EncodeException.class); - TestInterfaceAsync api = new TestInterfaceAsyncBuilder().encoder(new Encoder() { - @Override - public void encode(Object object, Type bodyType, RequestTemplate template) { - throw new RuntimeException(); - } - }).target("http://localhost:" + server.getPort()); + TestInterfaceAsync api = + new TestInterfaceAsyncBuilder().encoder((object, bodyType, template) -> { + throw new RuntimeException(); + }).target("http://localhost:" + server.getPort()); unwrap(api.body(Arrays.asList("foo"))); } @@ -631,13 +617,13 @@ public void encode(Object object, Type bodyType, RequestTemplate template) { @Test public void equalsHashCodeAndToStringWork() { Target t1 = - new HardCodedTarget(TestInterfaceAsync.class, + new HardCodedTarget<>(TestInterfaceAsync.class, "http://localhost:8080"); Target t2 = - new HardCodedTarget(TestInterfaceAsync.class, + new HardCodedTarget<>(TestInterfaceAsync.class, "http://localhost:8888"); Target t3 = - new HardCodedTarget(OtherTestInterfaceAsync.class, + new HardCodedTarget<>(OtherTestInterfaceAsync.class, "http://localhost:8080"); TestInterfaceAsync i1 = AsyncFeign.asyncBuilder().target(t1); TestInterfaceAsync i2 = AsyncFeign.asyncBuilder().target(t1); diff --git a/core/src/test/java/feign/BaseBuilderTest.java b/core/src/test/java/feign/BaseBuilderTest.java index 4bc34b16d..158fe30fb 100644 --- a/core/src/test/java/feign/BaseBuilderTest.java +++ b/core/src/test/java/feign/BaseBuilderTest.java @@ -69,7 +69,7 @@ private List getExclusiveMethods(Class clazz) { public void checkEnrichTouchesAllAsyncBuilderFields() throws IllegalArgumentException, IllegalAccessException { test(AsyncFeign.asyncBuilder().requestInterceptor(template -> { - }), 12); + }), 13); } private void test(BaseBuilder builder, int expectedFieldsCount) @@ -96,7 +96,7 @@ private void test(BaseBuilder builder, int expectedFieldsCount) public void checkEnrichTouchesAllBuilderFields() throws IllegalArgumentException, IllegalAccessException { test(Feign.builder().requestInterceptor(template -> { - }), 11); + }), 12); } }