WebFlux – how to check if Mono>> is empty to return 404

Tags: , ,



My code:

public Mono<ResponseEntity<Flux<TreeItem>>> allGroups(
      @PathVariable(value = "email") String email, ServerWebExchange exchange) {

    return Mono.just(
            userGroupService
                .findGroupByEmail(email) //Flux<GroupById>
                .flatMap(getGroups(email)) //Flux<TreeItem>
                .map(TreeItem::getId) //Flux<UUID>
                .collectList() //Mono<List<UUID>>
                .flatMap(getFilteredGroupIdsForUserPrivilege())
                .flatMapMany(Flux::fromIterable) //Flux<UUID>
                .flatMap(getUserTreeGroups(email)))
        .map(ResponseEntity::ok)
        .defaultIfEmpty(ResponseEntity.notFound().build()); --> is not executed at all when no data are returned

When I pass not existing email, my code returns response 200 with empty array: []

In this case I’d like to return 404 error – why the last line is not executed?

.defaultIfEmpty(ResponseEntity.notFound().build());

A method getUserTreeGroups:

private Function<UUID, Publisher<? extends TreeItem>> getUserTreeGroups(String email) {
    return filteredGroupId -> userGroupService
        .findUserTreeGroup(filteredGroupId, email);
  }

And method findUserTreeGroup:

public Mono<GroupTreeItem> findUserTreeGroup(UUID groupId, String email) {
    return groupByIdRepo.findById(groupId)
            .flatMap(group -> findChildData(email, group));
  }

I want to return to the frontend a list of TreeItem. And to be honest – I still don’t understand when to use Mono<ResponseEntity<Flux<TreeItem>>> or maybe Mono<ResponseEntity<List<TreeItem>>>? What’s the difference?

UPDATE After applying the solution from Thomas Andolf:

public Mono<ResponseEntity<Flux<TreeItem>>> userAllTreeGroups(
    @PathVariable(value = "email") String email, ServerWebExchange exchange) {
    return userGroupService
              .findUserGroupByEmail(email) //Flux<GroupById>
              .flatMap(groupById -> userGroupService.findUserTreeGroup(groupById.getId(), email)) //Flux<TreeItem>
              .map(TreeItem::getId) //Flux<UUID>
              .collectList() //Mono<List<UUID>>
              .flatMap(groupIds ->
                  rolePrivilegesService.filterGroupIdsForUserPrivilege(groupIds, GROUP_USER_READ_PRIVILEGE))
              .flatMapMany(Flux::fromIterable) //Flux<UUID>
              .flatMap(filteredGroupId -> userGroupService.findUserTreeGroup(filteredGroupId, email)) //Flux<GroupItem>
              .collectList() //Mono<List<TreeItem>>
              .map(ResponseEntity::ok) //Mono<ResponseEntity<List<TreeItem>>>
              .defaultIfEmpty(ResponseEntity.notFound().build());

But still I have to return Mono>>.

What should me method return in your opinion? Mono>> like in your solution???

Answer

In my second comment i asked

Why are you placing everything into a Mono.just()?

And you didn’t answer, well if you actually read it, that would of solved it cause that is probably causing your problem.

final List<String> strings = Collections.emptyList();

// You are wrapping a flux in a mono for some strange reason?
final Mono<Flux<String>> wrappedFlux = Mono.just(Flux.fromIterable(strings)
        .flatMap(s -> Mono.just("This never gets run"))
).defaultIfEmpty(Flux.just("This never gets run either, because there is a flux in the mono"));

rewritten

// Can't test it but something in the lines of the following
return userGroupService
            .findGroupByEmail(email) //Flux<GroupById>
            .flatMap(getGroups(email)) //Flux<TreeItem>
            .map(TreeItem::getId) //Flux<UUID>
            .collectList() //Mono<List<UUID>>
            .flatMap(getFilteredGroupIdsForUserPrivilege())
            .flatMapMany(Flux::fromIterable) //Flux<UUID>
            .flatMap(getUserTreeGroups(email)))
            .collectList()
            .map(ResponseEntity::ok)
            .defaultIfEmpty(ResponseEntity.notFound().build());

A Mono<T> is one will hold one future computation. When someone subscribes to it, it will try to resolve what is in it, and when its inner status turns into COMPLETED it will shoot the out out of it.

A Flux<T> is the same thing but for many objects. You can think if it as many Mono<T>s and same as before, when someone subscribes to it it will try to resolve the issues inside in each and every item in the Flux<T> and eject these as soon as the status hits COMPLETED for each and every one.

Nothing happens until you subscribe on a Mono<T> or aFlux`.

If you have a Mono<Flux<T>> when you subscribe, it will try to resolve whatever is in it, and what is in it is a Flux<T> so that will get shot out directly.

The Flux<T> on the other hand, no one has subscribe to, so nothing in it is resolved. It is basically dead, empty, not used.

You are asking me:

So in your opinion its meaningless/pointless to have Flux inside Mono

I have just written above, the absolut basics of how reactive programming works. You are emitting a Flux<T> from a Mono<T> that no one has subscribed to.

and nothing happens unless you subscribe.

i sugget you read the documentation for reactor from the start, it is very helpful to understand the basic concepts of reactive programming

I don’t know what you are trying to achive since i don’t have your full codebase i have no idea what you are returning.



Source: stackoverflow