diff --git a/src/main/java/com/blibli/oss/apiclient/aop/ApiClientMethodInterceptor.java b/src/main/java/com/blibli/oss/apiclient/aop/ApiClientMethodInterceptor.java index c408a88..4938e95 100644 --- a/src/main/java/com/blibli/oss/apiclient/aop/ApiClientMethodInterceptor.java +++ b/src/main/java/com/blibli/oss/apiclient/aop/ApiClientMethodInterceptor.java @@ -12,6 +12,7 @@ import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.core.type.AnnotationMetadata; @@ -50,6 +51,9 @@ public class ApiClientMethodInterceptor implements MethodInterceptor, Initializi @Setter private AnnotationMetadata annotationMetadata; + @Autowired + private ObjectMapper objectMapper; + private List bodyResolvers; private WebClient webClient; @@ -67,7 +71,7 @@ public void afterPropertiesSet() throws Exception { } private void prepareAttribute() { - metadata = new RequestMappingMetadataBuilder(applicationContext, type, name, annotationMetadata) + metadata = new RequestMappingMetadataBuilder(applicationContext, type, name, annotationMetadata, objectMapper) .build(); } diff --git a/src/main/java/com/blibli/oss/apiclient/aop/RequestMappingMetadataBuilder.java b/src/main/java/com/blibli/oss/apiclient/aop/RequestMappingMetadataBuilder.java index 0598ec1..264fa8c 100644 --- a/src/main/java/com/blibli/oss/apiclient/aop/RequestMappingMetadataBuilder.java +++ b/src/main/java/com/blibli/oss/apiclient/aop/RequestMappingMetadataBuilder.java @@ -2,6 +2,7 @@ import com.blibli.oss.apiclient.properties.ApiClientProperties; import com.blibli.oss.apiclient.properties.PropertiesHelper; +import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.beans.factory.BeanCreationException; import org.springframework.context.ApplicationContext; import org.springframework.core.type.AnnotationMetadata; @@ -53,11 +54,18 @@ public class RequestMappingMetadataBuilder { private AnnotationMetadata annotationMetadata; - public RequestMappingMetadataBuilder(ApplicationContext applicationContext, Class type, String name, AnnotationMetadata annotationMetadata) { + private ObjectMapper objectMapper; + + public RequestMappingMetadataBuilder(ApplicationContext applicationContext, + Class type, + String name, + AnnotationMetadata annotationMetadata, + ObjectMapper objectMapper) { this.applicationContext = applicationContext; this.type = type; this.name = name; this.annotationMetadata = annotationMetadata; + this.objectMapper = objectMapper; } private void prepareProperties() { @@ -124,7 +132,9 @@ private void prepareRequestBodyClasses() { } try { - responseBodyClasses.put(methodName, Class.forName(typeArguments[0].getTypeName())); + Type type = typeArguments[0]; + String className = objectMapper.constructType(type).getRawClass().getCanonicalName(); + responseBodyClasses.put(methodName, Class.forName(className)); } catch (ClassNotFoundException e) { throw new BeanCreationException(e.getMessage(), e); } diff --git a/src/test/java/com/blibli/oss/apiclient/ExampleClientTest.java b/src/test/java/com/blibli/oss/apiclient/ExampleClientTest.java index c2f863c..6b7d353 100644 --- a/src/test/java/com/blibli/oss/apiclient/ExampleClientTest.java +++ b/src/test/java/com/blibli/oss/apiclient/ExampleClientTest.java @@ -3,6 +3,7 @@ import com.blibli.oss.apiclient.client.ExampleClient; import com.blibli.oss.apiclient.client.model.FirstRequest; import com.blibli.oss.apiclient.client.model.FirstResponse; +import com.blibli.oss.apiclient.client.model.GenericResponse; import com.blibli.oss.apiclient.client.model.SecondResponse; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; @@ -19,6 +20,9 @@ import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; +import java.util.Collections; +import java.util.List; + import static com.github.tomakehurst.wiremock.client.WireMock.*; import static org.junit.jupiter.api.Assertions.*; @@ -30,6 +34,9 @@ public class ExampleClientTest { public static final FirstResponse FIRST_RESPONSE = FirstResponse.builder() .hello("Hello Eko Kurniawan") .build(); + public static final GenericResponse GENERIC_RESPONSE = GenericResponse.builder() + .value("Test Generics") + .build(); public static final SecondResponse SECOND_RESPONSE = SecondResponse.builder().hello("Hello").build(); private static WireMockServer wireMockServer; @@ -161,6 +168,42 @@ void testSixth() throws JsonProcessingException { assertEquals(FIRST_RESPONSE, response); } + @Test + void testGenerics() throws JsonProcessingException { + wireMockServer.stubFor( + post(urlPathEqualTo("/generics")) + .withHeader(HttpHeaders.CONTENT_TYPE, containing(MediaType.APPLICATION_JSON_VALUE)) + .withHeader(HttpHeaders.ACCEPT, equalTo(MediaType.APPLICATION_JSON_VALUE)) + .withRequestBody(equalTo("testing")) + .willReturn( + aResponse() + .withHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) + .withBody(objectMapper.writeValueAsString(GENERIC_RESPONSE)) + ) + ); + + GenericResponse response = exampleClient.generic("testing").block(); + assertEquals(GENERIC_RESPONSE, response); + } + + @Test + void testGenericsTwo() throws JsonProcessingException { + wireMockServer.stubFor( + post(urlPathEqualTo("/generics-two")) + .withHeader(HttpHeaders.CONTENT_TYPE, containing(MediaType.APPLICATION_JSON_VALUE)) + .withHeader(HttpHeaders.ACCEPT, equalTo(MediaType.APPLICATION_JSON_VALUE)) + .withRequestBody(equalTo("testing")) + .willReturn( + aResponse() + .withHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) + .withBody(objectMapper.writeValueAsString(Collections.singletonList("testing"))) + ) + ); + + List response = exampleClient.genericTwo("testing").block(); + assertEquals(Collections.singletonList("testing"), response); + } + @AfterAll static void afterAll() { wireMockServer.stop(); diff --git a/src/test/java/com/blibli/oss/apiclient/client/ExampleClient.java b/src/test/java/com/blibli/oss/apiclient/client/ExampleClient.java index fa6bfb6..9f81276 100644 --- a/src/test/java/com/blibli/oss/apiclient/client/ExampleClient.java +++ b/src/test/java/com/blibli/oss/apiclient/client/ExampleClient.java @@ -3,6 +3,7 @@ import com.blibli.oss.apiclient.annotation.ApiClient; import com.blibli.oss.apiclient.client.model.FirstRequest; import com.blibli.oss.apiclient.client.model.FirstResponse; +import com.blibli.oss.apiclient.client.model.GenericResponse; import com.blibli.oss.apiclient.client.model.SecondResponse; import org.springframework.core.io.Resource; import org.springframework.http.MediaType; @@ -10,6 +11,8 @@ import org.springframework.web.bind.annotation.*; import reactor.core.publisher.Mono; +import java.util.List; + @ApiClient( name = "exampleClient", fallback = ExampleClientFallback.class @@ -64,4 +67,20 @@ Mono forth(@PathVariable("userId") String userId, ) Mono sixth(@RequestPart("file") Resource file); + @RequestMapping( + method = RequestMethod.POST, + path = "/generics", + produces = MediaType.APPLICATION_JSON_VALUE, + consumes = MediaType.APPLICATION_JSON_VALUE + ) + Mono> generic(@RequestBody String test); + + @RequestMapping( + method = RequestMethod.POST, + path = "/generics-two", + produces = MediaType.APPLICATION_JSON_VALUE, + consumes = MediaType.APPLICATION_JSON_VALUE + ) + Mono> genericTwo(@RequestBody String test); + } diff --git a/src/test/java/com/blibli/oss/apiclient/client/ExampleClientFallback.java b/src/test/java/com/blibli/oss/apiclient/client/ExampleClientFallback.java index 04d1264..09d63a2 100644 --- a/src/test/java/com/blibli/oss/apiclient/client/ExampleClientFallback.java +++ b/src/test/java/com/blibli/oss/apiclient/client/ExampleClientFallback.java @@ -2,12 +2,16 @@ import com.blibli.oss.apiclient.client.model.FirstRequest; import com.blibli.oss.apiclient.client.model.FirstResponse; +import com.blibli.oss.apiclient.client.model.GenericResponse; import com.blibli.oss.apiclient.client.model.SecondResponse; import org.springframework.core.io.Resource; import org.springframework.stereotype.Component; import org.springframework.util.MultiValueMap; import reactor.core.publisher.Mono; +import java.util.Collections; +import java.util.List; + @Component public class ExampleClientFallback implements ExampleClient { @@ -52,4 +56,16 @@ public Mono sixth(Resource file) { .hello("Ups Sixth") .build()); } + + @Override + public Mono> generic(String test) { + return Mono.just(GenericResponse.builder() + .value("Ups Generic") + .build()); + } + + @Override + public Mono> genericTwo(String test) { + return Mono.just(Collections.singletonList("Ups Generic Two")); + } } diff --git a/src/test/java/com/blibli/oss/apiclient/client/model/GenericResponse.java b/src/test/java/com/blibli/oss/apiclient/client/model/GenericResponse.java new file mode 100644 index 0000000..cd62f4c --- /dev/null +++ b/src/test/java/com/blibli/oss/apiclient/client/model/GenericResponse.java @@ -0,0 +1,15 @@ +package com.blibli.oss.apiclient.client.model; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class GenericResponse { + + private T value; +}