Mono.zip with null - mono

My code:
Mono.zip(
credentialService.getCredentials(connect.getACredentialsId()),
credentialService.getCredentials(connect.getBCredentialsId())
)
.flatMap(...
From the frontend we get connect object with 2 fields:
connect{
aCredentialsId : UUID //required
bCredentialsId : UUID //optional
}
So sometimes the second line credentialService.getCredentials(connect.getBCredentialsId())) can return Mono.empty
How to write code to be prepared for this empty Mono when my second field bCredentialsId is null?
What should I do? In case of empty values return Mono.just(new Object) and then check if obj.getValue != null??? I need to fetch data from DB for 2 different values

The strategy I prefer here is to declare an optional() utility method like so:
public class Utils {
public static <T> Mono<Optional<T>> optional(Mono<T> in) {
return in.map(Optional::of).switchIfEmpty(Mono.just(Optional.empty()));
}
}
...which then allows you to transform your second Mono to one that will always return an optional, and thus do something like:
Mono.zip(
credentialService.getCredentials(connect.getACredentialsId()),
credentialService.getCredentials(connect.getBCredentialsId()).transform(Utils::optional)
).map(e -> new Connect(e.getT1(), e.getT2()))
(...assuming you have a Connect object that takes an Optional as the second parameter of course.)

An easier way is using mono's defaultIfEmpty method.
Mono<String> m1 = credentialService.getCredentials(connect.getACredentialsId());
Mono<String> m2 = credentialService.getCredentials(connect.getBCredentialsId()).defaultIfEmpty("");
Mono.zip(m1, m2).map(t -> connectService.connect(t.getT1(), t.getT2()));
Explanation: if m2 is null then get empty string as a default value instead of null.

Instead of using .zip here, I would work with a nullable property of Connect and use .flatMap in combination with .switchIfEmpty for it.
Kotlin-Version:
val aCredentials = credentialService.getCredentials(connect.getACredentialsId())
credentialService.getCredentials(connect.getBCredentialsId())
.flatMap { bCredentials -> aCredentials
.map { Connect(it, bCredentials)}
.switchIfEmpty(Connect(null, bCredentials))
}
.switchIfEmpty { aCredentials.map { Connect(it, null) } }

Related

How does Kotlin's data class copy idiom look for nullable types?

I have some code which looks like this, where param is of a data class type:
val options = if (param.language == null) {
param.copy(language = default())
} else {
param
}
Now, however, the language object has been moved into a hierarchy of nullable objects, so the check must look like this:
if (param.subObj?.nextObj?.language == null) { ... }
How do I use the copy idiom in this case?
One way to do this is:
val newParam = when {
param.subObj == null -> param.copy(subObj = SubObj(nextObj = NextObj(language = Language())))
param.subObj.nextObj == null -> param.copy(subObj = param.subObj.copy(nextObj = NextObj(language = Language())))
param.subObj.nextObj.language == null -> param.copy(subObj = param.subObj.copy(nextObj = param.subObj.nextObj.copy(language = Language())))
else -> param
}
I agree that this doesn't look very clean but this seems to be the only way to me, because at each step you need to check if the current property is null or not. If it is null, you need to use the default instance otherwise you need to make a copy.
Could you do something like this?
// you could create a DefaultCopyable interface if you like
data class SubObj(val prop1: Double? = null, val nextObj: NextObj? = null) {
fun copyWithDefaults() =
copy(prop1 = prop1 ?: 1.0, nextObj = nextObj?.copyWithDefaults() ?: NextObj())
}
data class NextObj(val name: String? = null) {
fun copyWithDefaults() = copy(name = name ?: "Hi")
}
I think you need a special function because you're not using the standard copy functionality exactly, you need some custom logic to define defaults for each class. But by putting that function in each of your classes, they all know how to copy themselves, and each copy function that works with other types can just call their default-copy functions.
The problem there though is:
fun main() {
val thing = SubObj(3.0)
val newThing = thing.copyWithDefaults()
println("$thing\n$newThing")
}
> SubObj(prop1=3.0, nextObj=null)
> SubObj(prop1=3.0, nextObj=NextObj(name=null))
Because nextObj was null in SubObj, it has to create one instead of copying it. But the real default value for name is null - it doesn't know how to instantiate one with the other defaults, that's an internal detail of NextObj. You could always call NextObj().copyWithDefaults() but that starts to look like a code smell to me - why isn't the default value for the parameter the actual default value you want? (There are probably good reasons, but it might mean there's a better way to architect what you're up to)

Assert that every object property matches given predicate in kotlin test

I have a collection of objects:
data class WeatherForecast(
val city: String,
val forecast: String
// ...
)
I would like to test that each and every item matches given predicate on field.
Is there any assertion in kotlintest assertions that will allow me to do so?
Something like:
forecasts.eachItemshouldMatch{ it.forecast == "SUNNY" }
What about using an inspector.
list.forAll {
it.forecast shouldBe "SUNNY"
}
https://kotest.io/docs/assertions/inspectors.html
You can simply use the all function; i.e.:
forecasts.all { it.forecast == "SUNNY" }

sort the table by column name Exposed Kotlin

Good afternoon, I want to make a universal sort for all tables. The idea is that the method will receive the name of the column as input and, through reflection, I will receive a link to the field of the same name.
val id = "id"
var a = JobSeekerTable::class
a.memberProperties.forEach { e ->
if (e.name == id) {
transaction {
JobSeeker.all().sortedBy { e.getter }
}
}
}
Unfortunately, this does not work. There was an option, through the fields field that the table has
JobSeekerTable.fields.forEach {v->
transaction {
JobSeeker.all().sortedBy { v }
}
}
but also unsuccessfully :(
If there is any way to refer to the required field through the name. Not using if and stuff like that?
First, you are probably looking for orderBy, not sortedBy. The former is to order SQL query results, the later is to sort a collection.
Second, you want to pass an instance of a column:
val id = "id"
JobSeekerTable.selectAll().orderBy(JobSeekerTable.columns.find {
it.name == id // Here I used the name you provided, although probably it should be named something like columnName
} !! to SortOrder.ASC)
Using "screaming" operator (!!) in Kotlin is a bad practice. So if all of your tables have ID column, for example, you can use "elvis" operator instead.
JobSeekerTable.selectAll().orderBy((JobSeekerTable.columns.find {
it.name == id
} ?: JobSeekerTable.id) to SortOrder.ASC)

Zip 2 lists of Observable with different return types

I'd like to wait until all the data from the API to be downloaded successfully and then do some operations on it.
The data result from observablesAPI1 and observablesAPI2 are different.
val observablesAPI1:List<Single<ApiResponse1>? = idApi1List.map{api1Repository.getData(it)}
val observablesAPI2:List<Single<ApiResponse2>? = idApi2List.map{api2Repository.getData(it)}
// this is not working
Single.zip(observablesAPI1,observablesAPI2,BiFunction <List<ApiResponse1>, List<ApiResponse2>> { apiResultList1, apiResultList2 -> // operations}
I thought about using nested zips but I'm not sure if it's possible to do that.
Edit:
There is actually 2 errors
when hover on observablesAPI1(similiar error on observablesAPI2):
Type missmatch. Required: SingleSource!>!
Found: List>?
when hover on BiFunction:
3 type arguments expected for fun
BiFunction(function:(t1: T1, t2: T2) ->R):BiFunction))
I would like to suggest you change the way which you map ids to data.
val observablesAPI1 = Observable.fromIterable(idApi1List)
.flatMapSingle { id -> api1Repository.getData(id) }
.toList()
val observablesAPI2 = Observable.fromIterable(idApi2List)
.flatMapSingle { id -> api2Repository.getData(id) }
.toList()
Single.zip(observablesAPI1, observablesAPI2, BiFunction<List<ApiResponse1>, List<ApiResponse2>, String> { list1, list2 ->
//do something
}).subscribe()
Note, that in my solution in zip function you have just two easy maintainable lists of ApiResponse1 and ApiResponse2.
It looks like you are having compilation errors.
This is from the javadocs of BiFunction:
/**
* A functional interface (callback) that computes a value based on multiple input values.
* #param <T1> the first value type
* #param <T2> the second value type
* #param <R> the result type
*/
public interface BiFunction<T1, T2, R> {
Your observablesAPI1 and observablesAPI2 have the type List<Single<ApiResponse*> but you are writing List<ApiResponse*>.
You are also missing the result type. For example, if you want to return a String, this is how your code should look like:
Single.zip(
observablesAPI1,
observablesAPI2,
BiFunction<List<Single<ApiResponse1>, List<Single<ApiResponse2>, String> {
apiResultList1, apiResultList2 -> "my result!"
})

How to use jooq to update table when column names and it's values determined at runtime

I have list of table column names and it's values which will be determined # run time. Right now I am using following way to achieve the feet which requires casting Filed to TableField for every single column name. Is there any better way ?
override fun updateFields(job: Job, jsonObject: JsonObject, handler: Handler<AsyncResult<Job?>>): JobQService {
val updateFieldsDsl = dslContext.update(JOB)
var feildSetDsl: UpdateSetMoreStep<*>? = null
jsonObject.map.keys.forEach { column ->
feildSetDsl = if (feildSetDsl == null) {
updateFieldsDsl.set(JOB.field(column) as TableField<Record, Any>, jsonObject.getValue(column))
} else {
feildSetDsl!!.set(JOB.field(column) as TableField<Record, Any>, jsonObject.getValue(column))
}
}
val queryDsl = feildSetDsl!!.where(JOB.ID.eq(job.id))
jdbcClient.rxUpdateWithParams(queryDsl.sql, JsonArray(queryDsl.bindValues)).subscribeBy(
onSuccess = { handler.handle(Future.succeededFuture(job)) },
onError = { handler.handle(Future.failedFuture(it)) }
)
return this;
}
I'm not sure what you mean by "better" but there is a method UpdateSetStep.set(Map), which seems to be helpful for what you're trying to do. See the javadoc:
UpdateSetMoreStep set(Map<?,?> map)
Set a value for a field in the UPDATE statement.
Keys can either be of type String, Name, or Field.
Values can either be of type <T> or Field<T>. jOOQ will attempt to convert values to their corresponding field's type.