spring-projects-experimental / spring-cloud-square

Spring Cloud auto-configuration of Retrofit and OkHttp (with Spring Cloud LoadBalancer).
Apache License 2.0
143 stars 34 forks source link

Response with type ParameterizedType not working with WebClient #48

Open TopoDiFogna opened 2 years ago

TopoDiFogna commented 2 years ago

Describe the bug Using spring cloud square 0.4.1

When creating an interface for retrofit and declaring a return type which is a parameterized class doesn't allow the application to start due to a ClassCastException in WebClientCallAdapterFactory#81 or, if not wrapping inside a Mono/Flux, WebClientCallAdapterFactory#87

The ReactorCallAdapterFactory used in com.jakewharton.retrofit:retrofit2-reactor-adapter allows such things, but then we lose the feature of a fully non-blocking client.

Also trying to use spring-cloud-square-retrofit + spring-cloud-square-okhttp does not work in webflux applications since a HttpMessageConverters bean is not created due to Conditional annotations.

Sample Just create an application with spring webflux and an interface like the following:

@RetrofitClient(name ="whatever")
public interface MyClient {

@GET("/something")
Flux<MyType<MyObject>> getMyTypeWithMyObject();

}

And include in your pom

michael-wirth commented 2 years ago

Same here. I use Kotlin with the following declaration and it results in a ClassCastException.

    @GET("/api/v2/accounts")
    fun accounts(): Mono<Data<List<Account>>>
Caused by: java.lang.ClassCastException: class sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl cannot be cast to class java.lang.Class (sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl and java.lang.Class are in module java.base of loader 'bootstrap')
    at org.springframework.cloud.square.retrofit.webclient.WebClientCallAdapterFactory.get(WebClientCallAdapterFactory.java:82)
    at retrofit2.Retrofit.nextCallAdapter(Retrofit.java:253)
    at retrofit2.Retrofit.callAdapter(Retrofit.java:237)
    at retrofit2.HttpServiceMethod.createCallAdapter(HttpServiceMethod.java:114)
    ... 96 more

As an interim solution, I can wrap the response in a ResponseEntity, which solves the problem for my use-case

    @GET("/api/v2/accounts")
    fun accounts(): Mono<ResponseEntity<Data<List<Account>>>>

I just realized if I use this variation then I get a list of Map records instead of Account objects: image

michael-wirth commented 2 years ago

@OlgaMaciaszek What is the roadmap for this project? There was no update for 6 months. Is it still active or already abanded in favor of another solution? I'd like to contribute to the project, but only if it is still active.

Best Regards Michael

OlgaMaciaszek commented 2 years ago

Hi @michael-wirth, we're going to still maintain it till Spring Cloud 2021.x (Spring 5 compatible) is supported, but just for bug fixes or minor features contributed by the community. We are not going to be doing much new development here since the supported declarative client for reactive communication from Spring 6 onward is the one we've developed within the framework. That being said, it would be helpful if you would like to work on bugs or some minor enhancements - if you do, I will review them and release a new version of the project after the merge. It might take some time cause the backlog is very packed till Spring Cloud 2022.x release, but once that's done, we'll prioritise catching up with the issues on other projects. Would you like to work on this issue?

michael-wirth commented 2 years ago

Hi @OlgaMaciaszek I'd be glad to work on some bug fixes and smaller enhancements. We're using Square to define a declarative web client. I assume with the migration to Spring 6 we will also go for the new solution.

OlgaMaciaszek commented 2 years ago

Thanks, @michael-wirth - it will be very helpful. I will definitely get to your PRs, but till the end of Nov work on anything unrelated to the 2022.x release might be slow on our side. We will definitely catch up with other backlogs then, however.