Search code examples
project-reactorreactivedeclarative

Project Reactor: achieving mapping input Mono conditionally


I am trying to implement the following simple imperative logic:

boolean saveImperative(final List list) {
    final String existingList = readByNameImperative(list.getName());
    if (Objects.isNull(existingList)) {
        templateSaveImperative(existingList);
        return true;
    } else {
        templateSaveImperative(existingList);
        return false;
    }
}

using Project Reactor in declarative way and this is what I was able to achieve:

@Test
public void testDeclarative() {
    final Mono<List> list = createList("foo");
    final Boolean result = save(list).block();
    System.out.println(result);
}

private Mono<Boolean> save(final Mono<List> listMono) {
    final Mono<List> existingListMono = listMono.cache()
            .flatMap(list -> readByName(list.getName()));
    // if
    final Mono<List> savedListMono = existingListMono
            .flatMap(existingList -> templateSave(Mono.just(existingList)));
    final Mono<Boolean> trueResult = savedListMono.map(x -> true);

    // else
    return trueResult.switchIfEmpty(templateSave(existingListMono).map(x -> false));
}

private Mono<List> templateSave(final Mono<List> listMono) {
    return listMono.map(list -> {
        System.out.println("templateSave has been called");
        return list;
    });
}

private Mono<List> readByName(final String listName) {
    if (listName != "list001") {
        return Mono.empty();
    }

    return createList(listName);
}

private Mono<List> createList(final String name) {
    final List list = List.builder().name(name).build();
    return Mono.just(list);
}

@Value
@Builder
private static class List {
    private final String name;
}

If I execute the test with list001, it will print:

templateSave has been called
true

as expected, but if I call it with foo, then I got

null

What I would be missing? I would expect an output like:

templateSave has been called
false

in that case.


Solution

  • final Mono<List> existingListMono = listMono.cache()
        .flatMap(list -> readByName(list.getName()));
    

    ...in your save method, will take your existing list and flat map it using readByName().

    Your readByName() method is the following:

    private Mono<List> readByName(final String listName) {
        if (listName != "list001") {
            return Mono.empty();
        }
    
        return createList(listName);
    }
    

    (I don't believe it's related to this problem, but don't use == or != for comparing strings.)

    Since your listName is foo, not list001, it returns an empty Mono - thus existingListMono becomes an empty Mono, and by implication so do savedListMono and trueResult.

    When you call your switchIfEmpty() statement however, you pass in templateSave(existingListMono) - and since existingListMono is an empty Mono as above, the save() method returns an empty Mono.

    ...and when you block on an empty Mono you'll get null - hence the result.

    As such, you may wish to use listMono instead of existingListMono in your return statement on the save() method, which will give you the result you're after:

    trueResult.switchIfEmpty(templateSave(listMono).map(x -> false))