Can you convert this reactive method to an Arrow Fx Project Reactor monad comprehension?
class ApplicationServiceImpl(private val applicationRepository: ApplicationRepository,
private val clientRepository: ClientRepository) : ApplicationService {
override fun findByProjectId(clientId: String, projectId: String): Flux<ApplicationOut> {
return clientRepository.findById(clientId)
.switchIfEmpty(ClientDoesntExistException(clientId).toMono())
.flatMapMany { client ->
applicationRepository.findByProjectIdOrderByNameAsc(projectId).map {
it.convertToApplicationOut(client.timeZone)
}
}
}
}
I've tried something like this but it isn't valid.
The first problem I've found is, originally I converted a Mono to a Flux using flatMapMany. If I use FluxK.monad().fx.monad
clientRepository.findById(clientId).k().bind()
doesn't have a .bind()
function available.
If I use MonoK.monad().fx.monad
instead I don't know how to convert the output to a Flux:
override fun findByProjectId(clientId: String, projectId: String): Flux<out ApplicationOut> {
return FluxK.monad().fx.monad {
val client = clientRepository.findById(clientId).k().bind()
if (client != null) {
!applicationRepository.findByProjectIdOrderByNameAsc(projectId)
.map { it.convertToApplicationOut(client.timeZone) }.k()
} else {
throw !ClientDoesntExistException(clientId).toMono<ApplicationOut>().k()
}
}.fix().flux
UPDATE
Following El Paco's answer I've modified the code to:
return FluxK.monad().fx.monad {
val client = !clientRepository.findById(clientId).toFlux().k()
if (client != null) {
!applicationRepository.findByProjectIdOrderByNameAsc(projectId)
.map { it.convertToApplicationOut(client.timeZone) }.k()
} else {
!ClientDoesntExistException(clientId).toFlux<ApplicationOut>().k()
}
}.fix().flux
It works fine when clientRepository.findById(clientId)
exists. When it doesn't, instead of assigning null to val client, it exits the comprehension (else isn't executed) so I can't launch my exception from inside the comprehension, which I guess is normal.
Consider the following method, in which I need to control two situations where the client or the project don't exist:
return clientRepository.findById(clientId)
.switchIfEmpty(ClientDoesntExistException(clientId).toMono())
.flatMapMany { client ->
applicationRepository.findByProjectIdOrderByNameAsc(projectId)
.switchIfEmpty(ClientDoesntExistException(projectId).toMono())
.map { it.convertToApplicationOut(client.timeZone) }
}
How can I deal with those non-existence cases when using comprehensions, taking into account that the if/else approach doesnt work?
I can use the switchIfEmpty operator outside the comprehension but I wouldn't know the cause (client doesn't exist vs project doesn't exist)
return FluxK.monad().fx.monad {
val client = !clientRepository.findById(clientId).toFlux().k()
!applicationRepository.findByProjectIdOrderByNameAsc(projectId)
.map { it.convertToApplicationOut(client.timeZone) }.k()
}.fix().flux
.switchIfEmpty(XXX)
You should convert all operations to Flux then, in your case clientRepository.findById(clientId).toFlux().k()
Also, you don't need to throw that exception to break the chain.
!ClientDoesntExistException(clientId).toMono<ApplicationOut>().k()