ReactiveMongoTemplate findOne for non-existing element does not get called - kotlin

I'm trying to obtain one document from collection. Document might not exist, and in case of null I wan to return default value.
My query and transformation:
return template.findOne(Query().addCriteria(Criteria.where("id")), DeviceSettings::class.java)
.map {
when (it) {
null -> {
defaultSettings(clock)
}
else -> {
listOf(
Instant.now(clock).toString(),
it.nextMeasurement.toString(),
it.shouldUpdateFirmware.toString()
)
}
}
}
}
Unfortunately above map transformation does not get called.
When I simplify call to simple callable it gets called:
return Mono.fromCallable({
defaultSettings(clock)
})

Reactor does not use nulls in streams and fineOne should return empty mono when there is no result. In your case you should use
switchIfEmpty operator
template.findOne(...)
.map { listOf(...) }
.switchIfEmpty(Mono.just(defaultSettings(clock))

Related

Kotlin Flow two dependant request but return the first

I have two functions, the first one that returns a Result with a model, and the second one that returns a Result with another model.
fun flow1(): Flow<Result<Model1>>
fun flow2(id: String): Flow<Result<Model2>>
What i want is went the flow1() call is success, then do the flow2() call and some logic when is success but at the end return the flow1() result.
And for the moment i just trying something like this:
flow1().flatMapLatest { flow1Result ->
flow1Result.onSuccess {
flow2(it.id).map { flow2Result ->
flow2Result.onSuccess {
//Some logic
}
}
}.onFailure {
// return error
}
}
I have two problems the first one that inside the flatMapLatest give an error because say that i return a Result instead of a Flow. And how i can return the Flow1 result?
Thank you!
Trying something similar to this response Chain kotlin flows depends on Result state
I guess you need something like this
fun main() {
flow1().flatMapLatest { flow1Result ->
// should return flow with flow1Result element emited
flow1Result
.onSuccess {
// flow<flow1Result>, that we return
flow2(it.id).map { flow2Result ->
flow2Result
.onSuccess{
TODO("some logic")
}.onFailure{
// only emit flow1Result when flow2Result = Success
throw RuntimeError()
}
// convert flow<flow2Result> to flow <flow1Result>
flow1Result
}
}
.onFailure {
// there can be other flow<flow1Result>
throw RuntimeError()
}
}
}

How to only return a flow of list when certain condition is met?

I have an overriden function that returns a flow of list and if the condition is not met, I emit an emptyList() to cover up the corner case:
override fun returnFlow(): Flow<List<Item>> {
return combine(booleanFlow()) { items, boolean ->
if (!boolean) {
items
} else {
emptyList()
}
}
}
I want to remove the else {} logic and change it to only emitting a list when boolean is false. Ideally I would like something similar to this:
override fun returnFlow(): Flow<List<Item>> {
return combine(booleanFlow()) { items, boolean ->
while (!boolean) {
items
}
}
}
Can I use something like cancelFlow() or return Nothing?
I tried how to stop Kotlin flow when certain condition occurs and it doesn't work since the return type changes to Flow<Unit>.
Use combineTransform instead of combine. Instead of always emitting the item returned by the lambda, this lets you choose whether or not to emit an item by calling emit.
override fun returnFlow(): Flow<List<Item>> {
return combineTransform(booleanFlow()) { items, boolean ->
if (!boolean) {
emit(items)
}
}
}

Kotlin - adding map function conditionally

I would like to feature toggle a map function on a list. I have a map that I would like to run only if the feature is on:
So for something like this:
items
.map { doTransformation(it) }
.map { runOnlyIfFeatureIsOn(it) }
Is there a way of adding the whole .map function conditionally in kotlin, so that it is only there if it is feature toggled?
let() is handy for doing arbitrary processing in a pipeline, e.g.:
items
.map{ doTransformation(it) }
.let{ if (someCondition) it.map{ runOnlyIfFeatureIsOn(it) } else it }
(For complex/costly conditions, this will be more efficient than putting the if inside the map call, as this'll only evaluate the condition once.)
Maybe just do if in map? There is no problem with that:
val list = listOf(1, 2, 3)
list
.map { it * 2 }
.map {
if (featureIsOn) {
runFeatureMapping(it)
} else {
it
}
}
Using sequences:
var sequence = items.asSequence()
.map { doTransformation(it) }
if (<feature_1_enabled>) {
sequence = sequence.map { runOnlyIfFeature1IsOn(it) }
}
if (<feature_2_enabled>) {
sequence = sequence.map { runOnlyIfFeature2IsOn(it) }
}
val result = sequence.toList()
Sequences are lazy-evaluated and should be used when mutliple operations (filter/map/etc) are applied

How to add condition to method "retry" in kotlin and webflux when api return error data?

How to change the parameters with retry() in kotlin and webflux ?
There is a productInfo function, the function parameter is a collection of product ids.
When I input a wrong id in the list collection ids, the upstream interface will only return the wrong id. And get failed.
What I want to achieve is when the upstream interface returns the wrong id. The product info can remove the wrong id and have a second try with the right ids.
I tried to use retry() but I don't know how to change the parameters in the second try.
fun productInfo(ids: List<Pair<String, String>>): Flux<ProductItem> {
return productWebClient
.get()
.uri("product/items/${ids.joinToString(";") { "${it.second},${it.first}" }}")
.retrieve()
.bodyToFlux(ProductItem::class.java)
.onErrorResume {
logger.error("Fetch products failed." + it.message)
Mono.empty()
}
}
What you want is not retry(). I've built a solution making minor assumptions here and there. You can refer to this solution and make changes according to your requirements. I've used recursion here (productInfo()). You can replace the recursion call with webclient call if the error occurs only once.
fun productInfo(ids: List<Pair<String, String>>): Flux<ProductItem> {
val idsString = ids.joinToString(";") { "${it.second},${it.first}" }
return webClient
.get()
.uri("product/items/${idsString}")
.exchange()
.flatMapMany { response ->
if (response.statusCode().isError) {
response.body { clientHttpResponse, _ ->
clientHttpResponse.body.cast(String::class.java).collectList()
.flatMapMany<ProductItem> { eids ->
val ids2 = ids.filter { eids.contains("${it.second},${it.first}") }
productInfo(ids2)
}
}
} else {
response.bodyToFlux(ProductItem::class.java)
}
}
}

RxJava Filter on Error

This question is loosely related to this question, but there were no answers. The answer from Bob Dalgleish is close, but doesn't support the potential error coming from a Single (which I think that OP actually wanted as well).
I'm basically looking for a way to "filter on error" - but don't think this exists when the lookup is RX based. I am trying to take a list of values, run them through a lookup, and skip any result that returns a lookup failure (throwable). I'm having trouble figuring out how to accomplish this in a reactive fashion.
I've tried various forms of error handling operators combined with mapping. Filter only works for raw values - or at least I couldn't figure out how to use it to support what I'd like to do.
In my use case, I iterate a list of IDs, requesting data for each from a remote service. If the service returns 404, then the item doesn't exist anymore. I should remove non-existing items from the local database and continue processing IDs. The stream should return the list of looked up values.
Here is a loose example. How do I write getStream() so that canFilterOnError passes?
import io.reactivex.Single
import io.reactivex.schedulers.Schedulers
import org.junit.Test
class SkipExceptionTest {
private val data: Map<Int, String> = mapOf(
Pair(1, "one"),
Pair(2, "two"),
Pair(4, "four"),
Pair(5, "five")
)
#Test
fun canFilterOnError() {
getStream(listOf(1, 2, 3, 4, 5))
.subscribeOn(Schedulers.trampoline())
.observeOn(Schedulers.trampoline())
.test()
.assertComplete()
.assertNoErrors()
.assertValueCount(1)
.assertValue {
it == listOf(
"one", "two", "four", "five"
)
}
}
fun getStream(list: List<Int>): Single<List<String>> {
// for each item in the list
// get it's value via getValue()
// if a call to getValue() results in a NotFoundException, skip that value and continue
// mutate the results using mutate()
TODO("not implemented")
}
fun getValue(id: Int): Single<String> {
return Single.fromCallable {
val value: String? = data[id]
if (value != null) {
data[id]
} else {
throw NotFoundException("dat with id $id does not exist")
}
}
}
class NotFoundException(message: String) : Exception(message)
}
First .materialize(), then .filter() on non-error events, then .dematerialize():
getStream(/* ... */)
.materialize()
.filter(notification -> { return !notification.isOnError(); })
.dematerialize()
I ended up mapping getValue() to Optional<String>, then calling onErrorResumeNext() on that and either returning Single.error() or Single.just(Optional.empty()). From there, the main stream could filter out the empty Optional.
private fun getStream(list: List<Int>): Single<List<String>> {
return Observable.fromIterable(list)
.flatMapSingle {
getValue(it)
.map {
Optional.of(it)
}
.onErrorResumeNext {
when (it) {
is NotFoundException -> Single.just(Optional.empty())
else -> Single.error(it)
}
}
}
.filter { it.isPresent }
.map { it.get() }
.toList()
}