How to convert Reactor Flux<Object> to Mono<Void> - spring-webflux

I defined a data provider help class to populate my local DB during testing and I'm using a ReactorCrudRepository. Te defined saveAll method is:
<S extends T> Flux<S> saveAll(Publisher<S> entityStream);
And my PostgresDataProvider.insertData is returning a Mono because I'm not interested in receive the inserted entities. How can pipeline my current method from Flux to cast into Mono?
public Mono<Void> insertData(Flux<Entity> entities) {
return repository.saveAll(entities);
}

I finally used the operator
then() → will just replay the source terminal signal, resulting in a Mono to indicate that this never signals any onNext.
That allow me to produce a Mono on the onComplete flux's signal.
public Mono<Void> insertData(Flux<RateEntity> rateEntities) {
return repository.saveAll(rateEntities).then();
}

Related

Kotlin sealed classes vs using polymorphism

I'm curious about an example given in Kotlin documentation regarding sealed classes:
fun log(e: Error) = when(e) {
is FileReadError -> { println("Error while reading file ${e.file}") }
is DatabaseError -> { println("Error while reading from database ${e.source}") }
is RuntimeError -> { println("Runtime error") }
// the `else` clause is not required because all the cases are covered
}
Let's imagine the classes are defined as follows:
sealed class Error
class FileReadError(val file: String): Error()
class DatabaseError(val source: String): Error()
class RuntimeError : Error()
Is there any benefit for using when over using polymorphism:
sealed class Error {
abstract fun log()
}
class FileReadError(val file: String): Error() {
override fun log() { println("Error while reading file $file") }
}
class DatabaseError(val source: String): Error() {
override fun log() { println("Error while reading from database $source") }
}
class RuntimeError : Error() {
override fun log() { println("Runtime error") }
}
The only reason I can think of is that we may not have access to the source code of those classes, in order to add our log method to them. Otherwise, it seems that polymorphism is a better choice over instance checking (see [1] or [2] for instance.)
This is described as "Data/Object Anti-Symmetry" in the book Clean Code: A Handbook of Agile Software Craftsmanship by Robert C. Martin.
In the first example (Data style), you are keeping your error classes dumb with an external function that handles all types. This style is in opposition to using polymorphism (Object style) but there are some advantages.
Suppose you were to add a new external function, one that returns an icon to show the user when the error happens. The first advantage is you can easily add this icon function without changing any line in any of your error classes and add it in a single place. The second advantage is in the separation. Maybe your error classes exist in the domain module of your project and you'd prefer your icon function to be in the ui module of your project to separate concerns.
So when keeping the sealed classes dumb, it's easy to add new functions and easy to separate them, but it's hard to add new classes of errors because then you need to find and update every function. On the other hand when using polymorphism, it's hard to add new functions and you can't separate them from the class, but it's easy to add new classes.
The benefit of the first (type-checking) example is that the log messages do not have to be hardcoded into the Error subclasses. In this way, clients could potentially log different messages for the same subclass of Error in different parts of an application.
The second (polymorphic) approach assumes everyone wants the same message for each error and that the developer of each subclass knows what that error message should be for all future use cases.
There is an element of flexibility in the first example that does not exist in the second. The previous answer from #Trevor examines the theoretical underpinning of this flexibility.

How to use #Cacheable with Kotlin suspend funcion

I am working in a Kotlin and Spring Boot project and I am trying to use Caffeine for caching. I have a service with a suspending function that makes an http call. Here is my config:
#Bean
open fun caffeineConfig(): #NonNull Caffeine<Any, Any> {
return Caffeine.newBuilder().expireAfterWrite(60, TimeUnit.SECONDS)
}
#Bean
open fun cacheManager(caffeine: Caffeine<Any, Any>): CacheManager {
val caffeineCacheManager = CaffeineCacheManager()
caffeineCacheManager.getCache("test")
caffeineCacheManager.setCaffeine(caffeine)
return caffeineCacheManager
}
And here is the function that I want to cache:
#Cacheable(value = ["test"])
open suspend fun getString(id: String): String {
return client.getString(id)
}
But it seems that the caching is not working since I can see from logs that the client gets called every time the service-function gets called. Does #Cacheable not work for suspending functions? Or am I missing something else?
The documentation of #Cacheable says:
Each time an advised method is invoked, caching behavior will be applied, checking whether the method has been already invoked for the given arguments. A sensible default simply uses the method parameters to compute the key, but a SpEL expression can be provided via the key() attribute, or a custom KeyGenerator implementation can replace the default one (see keyGenerator()).
The suspend modifier inserts an Continuation<String> parameter in the generated code which accepts input from the caller. This presumably means each invocation gets its own continuation and the cache detects this as a unique call.
However since the return value also gets changed depending on the continuation you cannot have the cache ignore the continuation parameter. A better approach is to not use suspend functions and instead returning a Deferred which consumers can share:
#Cacheable(value = ["test"])
open fun getString(id: String): Deferred<String> {
return someScope.async {
client.getString(id)
}
}
// Consumer side
getString(id).await()
This should work with the standard caching mechanism since Deferred is a normal object and no special parameters are required.

Problem deserialize flux/mono into Feign Spring Cloud

i develop a micro services application with Kotlin Webflux (Reactor3), Eureka, Zuul and Feign. Except that I always have an error when I make a call to an API via my micro service Feign. It looks like he can not deserialize the data. Could you please tell me if Feign is compatible with Flux and Monno?
thank you
{
"timestamp": "2019-05-29T07:39:43.998+0000",
"path": "/hobbies/",
"status": 500,
"error": "Internal Server Error",
"message": "Type definition error: [simple type, class reactor.core.publisher.Flux]; nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of reactor.core.publisher.Flux (no Creators, like default construct, exist): abstract types either need to be mapped to concrete types, have custom deserializer, or contain additional type information\n at [Source: (PushbackInputStream); line: 1, column: 1]"
}
Feign doesn't provide support for Mono/Flux deserialiazation. There exists alternative feign library who fully support it: feign-reactive.
Note though, this is a rewrite of feign which fully use reactive code, differ from OpenFeign's Feign core.
Here's a snippet on how to use it, alongside with normal Feign, taken from the sample app.
#SpringBootApplication(exclude = ReactiveLoadBalancerAutoConfiguration.class)
#RestController
#EnableReactiveFeignClients
#EnableFeignClients
public class FeignApplication {
#Autowired
private GreetingReactive reactiveFeignClient;
#Autowired
private Greeting feignClient;
public static void main(String[] args) {
SpringApplication.run(FeignApplication.class, args);
}
#GetMapping("/greetingReactive")
public Mono<String> greetingReactive() {
return reactiveFeignClient.greeting().map(s -> "reactive feign! : " + s);
}
#GetMapping("/greeting")
public String greeting() {
return "feign! : " + feignClient.greeting();
}
}
In addition to Adhika Setya Pramudita response, I would like to mention that in order to return Mono in controller, you must use Spring WebFlux instead of Spring MVC
I was not able to make #Adhika Setya Pramudita solution working and gut tells me that it cannot even run due to mixing #EnableReactiveFeignClients and
#EnableFeignClients which are require corresponding #EnableWebFlux or #EnableWebMvc and thus defining both may compile but will fail in runtime.
Since op did not mention target language I'm feeling like to share Kotlin setup that works in my case:
build.gradle.kts
implementation("org.springframework.boot:spring-boot-starter-webflux")
implementation("com.playtika.reactivefeign:feign-reactor-core:2.0.22")
implementation("com.playtika.reactivefeign:feign-reactor-spring-configuration:2.0.22")
implementation("com.playtika.reactivefeign:feign-reactor-webclient:2.0.22")
Config.kt
#Configuration
#EnableWebFlux
#EnableReactiveFeignClients
class Config {
}
MyEntity.kt
class MyEntity #JsonCreator constructor(
#param:JsonProperty("my_value") val my_value: String
)
MyFeignClient.kt
#Component
#ReactiveFeignClient(
url = "\${package.service.my-service-url}",
name = "client"
)
interface MyFeignClient {
#GetMapping(value = ["/my/url?my_param={my_value}"], consumes = ["application/json"])
fun getValues(
#PathVariable(name = "my_value") myValue: String?,
): Mono<MyEntity?>?
}
Then here goes code in some service class:
val myClient: MyFeignClient = WebReactiveFeign.builder<MyFeignClient>()
.contract(ReactiveContract(SpringMvcContract()))
.target(MyFeignClient::class.java, "http://example.com")
// feel free to add .block() to get unpacked value or just chain your logic further
val response = myClient.getValues(param)

Exception Handling Reactive

How does one use a Mono.error(<Throwable>) but attach information from the body returned from a request?
Is there a reactive object that extends Throwable that takes a Mono/Flux object, so the error being thrown will wait for the body to be accounted for?
Or is there a way to add some sort of 'flag' onto an existing Mono object to make it fail instantly (to circumvent the requirement to be Throwable)
Example scenario below:
import org.springframework.web.reactive.function.client.WebClient;
private someMethod() {
webClient.get().retrieve().onStatus(HttpStatus::isError, this::errorHandler)
}
private Mono<? extends Throwable> errorHandler(ClientResponse cr) {
Mono<String> body = cr.body(BodyExtractors.toMono(String.class));
...<do something here>...
return Mono.error(new WhateverException());
}
Thanks
return body.flatMap(str -> Mono.error(new WhateverException(str)));

Kotlin Wildcard Capture on List Callback Parameter

Java:
public class JavaClass implements ModelController.Callback {
#Override
public void onModelsLoaded(#NonNull List<? extends Model> models) {
doSomething(models);
}
private void doSomething(List<Model> models) { }
}
Kotlin:
class ModelController {
var callback = WeakReference<Callback>(null)
interface Callback {
fun onModelsLoaded(models: List<Model>)
}
fun someFunction() {
callback.get().onModelsLoaded(ArrayList<Model>())
}
}
interface Model {
}
Without the ? extends Model in the Java onModelsLoaded method, the override doesn’t match the interface made in Kotlin. With it, I get the following error:
doSomething(<java.util.List<com.yada.Model>) cannot be applied to (java.util.List<capture<? extends com.yada.Model>>)
Why is the wildcard capture required and why doesn't it allow it to be used against the non-wildcard method?
The issue stems from Kotlin collections being variant, and Java only having use-site variance which is implemented though wildcards (capture is something connected to wildcards but not exactly the ? extends ... syntax itself).
When in Kotlin we say List<Model> it means "read-only list of Model or subtypes of Model", when we say the same in Java it means "mutable list of exactly Model and nothing else". To mean roughly what Kotlin's List<Model> means, in Java we have to say List<? extends Model>, this is why for the override to work you have to add the wildcard into the Java code.
Now, your doSomething is written in Java and says that it wants "a list of exactly Model", and when you are giving it "a list of Model or its subtypes", the Java compiler complains, because it can be dangerous: doSomething might try to do something that is not legitimate for a list of, say, ModelImpl, because it thinks it's working on a list of Model.
As of now (Kotlin Beat 2), you have two options:
use MutableList<Model> in your Kotlin code - this mean exactly what Java's List<Model> means, or
define doSomething so that it takes List<? extends Model>, which is what your current Kotlin code means.
In the next update of Kotlin we'll add an annotation on types to facilitate a somewhat cleaner workaround for this problem.
To solve the problem with capture<? extends Model>
You may do something like this:
void doSomething(List<Model> models) {
new ArrayList(models)
}