feign-reactive
feign-reactive copied to clipboard
Generalized target-type Inference
Hi,
I do not understand why we cannot set the returned target type on a method…
This is my feign reactive interface for accessing the Fargo API
package com.gv.zeppelin.service.fargo;
import com.gv.zeppelin.service.dto.fargo.ACL247StarterProcessDTO;
import feign.Headers;
import feign.Param;
import feign.QueryMap;
import feign.RequestLine;
import org.springframework.util.MultiValueMap;
import reactor.core.publisher.Mono;
@Headers({"Accept: application/json"})
public interface FargoApi {
@RequestLine("GET /api/v1/query/{slug}")
<T> Mono<T> queryForList(@Param("slug") String slug,
@QueryMap MultiValueMap<String, Object> queryMap,
Class<T> elementClass);
@RequestLine("GET /api/v1/query/{slug}")
Mono<ACL247StarterProcessDTO> queryForList(@Param("slug") String slug,
@QueryMap MultiValueMap<String, Object> queryMap);
}
This is my test class
package com.gv.zeppelin.service.fargo;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.gv.zeppelin.service.dto.XConverterRequestDTO;
import com.gv.zeppelin.service.dto.fargo.ACL247StarterProcessDTO;
import com.gv.zeppelin.service.fargo.query.Query;
import com.gv.zeppelin.service.fargo.query.QuerySlug;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.springframework.http.MediaType;
import org.springframework.http.codec.ClientCodecConfigurer;
import org.springframework.http.codec.json.Jackson2JsonDecoder;
import org.springframework.http.codec.json.Jackson2JsonEncoder;
import org.springframework.web.reactive.function.client.ExchangeStrategies;
import org.springframework.web.reactive.function.client.WebClient;
import reactivefeign.webclient.WebReactiveFeign;
import reactor.test.StepVerifier;
import java.util.List;
class FargoApiTest {
private static ObjectMapper baseConfig = new ObjectMapper();
private static FargoApi fargoApi;
private static XConverterRequestDTO xConverterRequestDTO = new XConverterRequestDTO();
@BeforeAll
public static void beforeAll() {
var builder = fargoWebClient(baseConfig);
fargoApi = WebReactiveFeign
.<FargoApi>builder(builder)
.target(FargoApi.class, "http://172.16.9.5:50010");
xConverterRequestDTO.setWorkOrders(List.of("ZPH-V121-00122"));
xConverterRequestDTO.setWorkOrders(List.of("MFG-V121-008485", "MFG-V121-008486"));
}
private static WebClient.Builder fargoWebClient(ObjectMapper baseConfig) {
return WebClient.builder()
.exchangeStrategies(ExchangeStrategies.builder()
.codecs(FargoApiTest::configureClientCodecs)
.build()
);
}
private static void configureClientCodecs(ClientCodecConfigurer configurer) {
configurer
.defaultCodecs()
.maxInMemorySize(16 * 1024 * 1024);
configurer
.defaultCodecs()
.jackson2JsonEncoder(
new Jackson2JsonEncoder(baseConfig, MediaType.APPLICATION_JSON));
configurer
.defaultCodecs()
.jackson2JsonDecoder(
new Jackson2JsonDecoder(baseConfig, MediaType.APPLICATION_JSON));
}
/**
* Works
*/
@Test
public void queryForList() {
var mono = fargoApi.queryForList(
QuerySlug.ERP_SAGE_X3_ACL_247_STARTER_PROCESSOR.value(),
Query.toQueryMap(xConverterRequestDTO)
);
StepVerifier.create(mono)
.expectNextMatches(response -> response.getData().size() == 1)
.verifyComplete();
}
/**
* Do not works
*/
@Test
public void queryForListWithGeneralizedTargetType() {
var mono = fargoApi.queryForList(
QuerySlug.ERP_SAGE_X3_ACL_247_STARTER_PROCESSOR.value(),
Query.toQueryMap(xConverterRequestDTO),
ACL247StarterProcessDTO.class
);
StepVerifier.create(mono)
.expectNextMatches(response -> response.getData().size() == 1)
.verifyComplete();
}
}
This is the stack trace ouput by the queryForListWithGeneralizedTargetType test
2022-03-19 15:14:32.099 DEBUG --- [ main] r.client.log.DefaultReactiveLogger : [FargoApi#queryForList(String,MultiValueMap,Class)]--->GET http://172.16.9.5:50010/api/v1/query/erp-sage-x3-acl-247-starter-processor?manufacturingPlan=&workOrder=MFG-V121-008485&workOrder=MFG-V121-008486&tag= HTTP/1.1
2022-03-19 15:14:33.333 DEBUG --- [actor-tcp-nio-2] r.client.log.DefaultReactiveLogger : [FargoApi#queryForList(String,MultiValueMap,Class)]<--- headers takes 1234 milliseconds
java.lang.AssertionError: expectation failed (failed running expectation on signal [onNext({data=[{order_number=ENC-V121/00929, order_line=6000, order_sequence=6000, manufacturing_plan=ZPH-V121-00122, kit_number=KIT-V121-02113, work_order=MFG-V121-008486, planned_quantity=1.0, product=0021237, description=Porta I19V1G Favo Mdf 28 + Ptx 3 OF Faia Vap 2000.0x400.0x35.0, exterior_color=FV, interior_color=FV, opening_side=ESQR, access=IN, access_val_1=IN, access_val_2=push, ariporta_type=AB01A.LM.2, product_family_code=PI0, product_family_description=, length=2000.0, width=400.0, thickness=35.0, expected_lot=ZLT-0000001921, glass_open_type=G, hinge_quantity=3, lock_height=0, edge_shape_t=1, edge_shape_description_t=0° S/boleado (lacagem), edge_shape_r=2, edge_shape_description_r=0° C/boleado (standard), edge_shape_b=1, edge_shape_description_b=0° S/boleado (lacagem), edge_shape_l=2, edge_shape_description_l=0° C/boleado (standard), edge_shape_mechanization_t={"rebated_width":0,"rebated_depth":0}, edge_shape_mechanization_r={"rebated_width":0,"rebated_depth":0}, edge_shape_mechanization_b={"rebated_width":0,"rebated_depth":0}, edge_shape_mechanization_l={"rebated_width":0,"rebated_depth":0}, hinges=[{"type":"hinge","item":"0009195","description":"400 Rev.Esq NI","model":"Q37","macro_name":"zConcealedHinge1","macro_value":"\/\/ 400\ndouble hingePositionY = openingSide.ToUpper().StartsWith(\"ESQ\") ? DZ - (7.5 + 20 \/ 2) : (7.5 + 20 \/ 2);\ndouble hingeAngle = 0;\ndouble hingeLength1 = 100;\ndouble hingeWidth = 20;\ndouble hingeRadius = 10;\ndouble hingeDepth1 = 2.6;\ndouble hingeInputFeed = 0;\ndouble hingeFeed = 0;\ndouble hingeRotation = 0;\nint hingeHood = 0;\nstring hingeTool = \"031\";\nstring hingeHead = \"1\";\nstring hingeFace = \"Back\";"}]}]})] with [java.lang.ClassCastException]:
class java.util.LinkedHashMap cannot be cast to class com.gv.zeppelin.service.dto.fargo.ACL247StarterProcessDTO (java.util.LinkedHashMap is in module java.base of loader 'bootstrap'; com.gv.zeppelin.service.dto.fargo.ACL247StarterProcessDTO is in unnamed module of loader 'app'))
at reactor.test.DefaultStepVerifierBuilder.failPrefix(DefaultStepVerifierBuilder.java:2171)
at reactor.test.DefaultStepVerifierBuilder.fail(DefaultStepVerifierBuilder.java:2167)
at reactor.test.DefaultStepVerifierBuilder$DefaultVerifySubscriber.onExpectation(DefaultStepVerifierBuilder.java:1252)
at reactor.test.DefaultStepVerifierBuilder$DefaultVerifySubscriber.onNext(DefaultStepVerifierBuilder.java:905)
at reactor.core.publisher.Operators$MonoSubscriber.complete(Operators.java:1816)
at reactor.core.publisher.MonoFlatMap$FlatMapInner.onNext(MonoFlatMap.java:249)
at reactor.core.publisher.FluxOnAssembly$OnAssemblySubscriber.onNext(FluxOnAssembly.java:539)
at reactor.core.publisher.Operators$MonoSubscriber.complete(Operators.java:1816)
at reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:151)
at reactor.core.publisher.FluxContextWrite$ContextWriteSubscriber.onNext(FluxContextWrite.java:107)
at reactor.core.publisher.FluxMapFuseable$MapFuseableConditionalSubscriber.onNext(FluxMapFuseable.java:295)
at reactor.core.publisher.FluxFilterFuseable$FilterFuseableConditionalSubscriber.onNext(FluxFilterFuseable.java:337)
at reactor.core.publisher.Operators$MonoSubscriber.complete(Operators.java:1816)
at reactor.core.publisher.MonoCollect$CollectSubscriber.onComplete(MonoCollect.java:159)
at reactor.core.publisher.FluxMap$MapSubscriber.onComplete(FluxMap.java:142)
at reactor.core.publisher.FluxPeek$PeekSubscriber.onComplete(FluxPeek.java:260)
at reactor.core.publisher.FluxMap$MapSubscriber.onComplete(FluxMap.java:142)
at reactor.netty.channel.FluxReceive.onInboundComplete(FluxReceive.java:400)
at reactor.netty.channel.ChannelOperations.onInboundComplete(ChannelOperations.java:419)
at reactor.netty.channel.ChannelOperations.terminate(ChannelOperations.java:473)
at reactor.netty.http.client.HttpClientOperations.onInboundNext(HttpClientOperations.java:702)
at reactor.netty.channel.ChannelOperationsHandler.channelRead(ChannelOperationsHandler.java:93)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357)
at io.netty.handler.timeout.IdleStateHandler.channelRead(IdleStateHandler.java:286)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357)
at io.netty.channel.CombinedChannelDuplexHandler$DelegatingChannelHandlerContext.fireChannelRead(CombinedChannelDuplexHandler.java:436)
at io.netty.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:327)
at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:299)
at io.netty.channel.CombinedChannelDuplexHandler.channelRead(CombinedChannelDuplexHandler.java:251)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357)
at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1410)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)
at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:919)
at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:166)
at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:722)
at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:658)
at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:584)
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:496)
at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:986)
at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
at java.base/java.lang.Thread.run(Thread.java:833)
Suppressed: java.lang.ClassCastException: class java.util.LinkedHashMap cannot be cast to class com.gv.zeppelin.service.dto.fargo.ACL247StarterProcessDTO (java.util.LinkedHashMap is in module java.base of loader 'bootstrap'; com.gv.zeppelin.service.dto.fargo.ACL247StarterProcessDTO is in unnamed module of loader 'app')
at reactor.test.DefaultStepVerifierBuilder.lambda$expectNextMatches$10(DefaultStepVerifierBuilder.java:430)
at reactor.test.DefaultStepVerifierBuilder$SignalEvent.test(DefaultStepVerifierBuilder.java:1909)
at reactor.test.DefaultStepVerifierBuilder$DefaultVerifySubscriber.onSignal(DefaultStepVerifierBuilder.java:1270)
at reactor.test.DefaultStepVerifierBuilder$DefaultVerifySubscriber.onExpectation(DefaultStepVerifierBuilder.java:1215)
... 46 more
Is it possible to make it work?
Thanks in advance.
We support only features that are present in OpenFeign. I doubt if OpenFeign have this feature.