Reputation: 3442
Basically what I am trying to achieve is to call a second repository (a ReactiveCrudRepository
) or throw an exception, depending on the result of a call to a first repository.
My original idea looks like this:
/** Reactive with blocking code */
public Flux<SecondThing> getThings(String firstThingName) {
FirstThing firstThing = firstRepo
.findByName(firstThingName)
// Warning: "Inappropriate blocking method call"
.blockOptional() // this fails in test-context
.orElseThrow(() -> new FirstThingNotFound(firstThingName));
return secondRepo.findAllByFirstThingId(firstThing.getId());
}
Which would correspond to the following non-reactive approach:
/** Non-reactive */
public List<SecondThing> getThings(String firstThingName) {
FirstThing firstThing = firstRepo
.findByName(firstThingName)
.orElseThrow(() -> new FirstThingNotFound(firstThingName));
return secondRepo.findAllByFirstThingId(firstThing.getId());
}
I haven't found a way to do this in a reactive non-blocking way. All I need is to throw an error if an empty Mono
comes out of the first call, and continue the pipeline if not empty; but I could not seem to use onErrorStop
or doOnError
correctly here, and map
does not help as it skips the empty Mono
.
What I have is a workaround if I use id
instead of name
, but I'm not quite satisfied with it as it shows a different behaviour in the case where is an instance of FirstThing
but no SecondThing
linked to it:
/** Reactive workaround 1 */
public Flux<SecondThing> getThings(Long firstThingId) {
return secondRepo
.findAllByFirstThingId(firstThingId)
.switchIfEmpty(
Flux.error(() -> new FirstThingNotFound(firstThingName))
);
}
Another workaround I've found is the following, that replaces the empty Mono
with a null
value, but it doesn't look right and throws a warning too:
/** Reactive workaround 2 */
public Flux<SecondThing> getThings(String firstThingName) {
return firstRepo
.findByName(firstThingName)
// Warning: "Passing 'null' argument to parameter annotated as @NotNull"
.defaultIfEmpty(null)
.flatMapMany(
firstThing -> secondRepo.findAllByFirstThingId(firstThing.getId()
)
.onErrorMap(
NullPointerException.class, e -> new FirstThingNotFound(firstThingName)
);
}
What is the correct way to chain the calls to both repositories so that the presence or absence of a FirstThing
with the requested firstThingName
conditions the call to the second repo?
Upvotes: 1
Views: 2753
Reputation: 3442
I found a solution so simple, that I could be ashamed not to have found it earlier:
public Flux<SecondThing> getThings(String firstThingName) {
return firstRepo
.findByName(firstThingName)
.switchIfEmpty(Mono.error(() -> new FirstThingNotFound(firstThingName)))
.flatMapMany(
firstThing -> secondRepo.findAllByFirstThingId(firstThing.getId()
);
}
The trick is that switchIfEmpty
does not force you to pick a "valid" replacement value, so it is possible to use a Mono.error
to propagate the right exception directly.
Upvotes: 5