-1

I have a public API with two endpoints. I have to get an Array of objects as Flux from the first GET endpoint THEN BY calling the second(internal call) GET endpoint Mono(Mono contains List of Objects ) where I have to find an internal Object by id THEN using the found object(List) filter first Flux(each contains List)

I tried in this way but it became a forever stream

public class Test {

    String baseUrl = "http://some-site.com/";
    private final WebClient webClient = WebClient.create(baseUrl);

    @SneakyThrows
    public Flux<One> getObjOneFlux(String id) {
        Flux<One> oneFlux = loadObjOnes();
        Mono<Two> twoMono = loadObjTwo();

        return oneFlux.filterWhen(one -> twoMono.map(two -> one.getOneInternalList()
                        .stream()
                        .map(One.OneInternal::getName).toList()
                        .containsAll(
                                two.getTwoInternalList().stream()
                                        .filter(s -> s.getId().equals(id))
                                        .findFirst().get().getOptions()
                        )
                )
        );
    }


    public Flux<One> loadObjOnes() {
        return webClient.get()
                .uri(uriBuilder -> uriBuilder.path("/ObjOne").build())
                .exchange()
                .flatMap(res -> res.bodyToMono(ObjOneResponse.class))
                .flatMapIterable(res -> res)
                .map(t ->
                        One.builder()
                                .id(t.getId())
                                .mark(t.getMark())
                                .oneInternalList(t.getOneInternalList())
                                .build());
    }

    public Mono<Two> loadObjTwo() {
        return webClient.get()
                .uri(uriBuilder -> uriBuilder.path("/ObjTwo").build())
                .exchange()
                .flatMap(res -> res.bodyToMono(Two.class))
                .map(s -> Two.builder()
                        .twoInternalList(s.getTwoInternalList())
                        .build()).delayElement(Duration.ofSeconds(3));
    }
}

@Data
@Builder
class One {
    private String id;
    private String mark;
    private List<OneInternal> oneInternalList;

    @Data
    @Builder
    public static final class OneInternal {
        private String name;
    }
}

@Data
@Builder
class Two {
    private List<TwoInternal> twoInternalList;

    @Data
    @Builder
    public static final class TwoInternal {
        private List<String> options;
        private String id;
    }
}

class ObjOneResponse extends ArrayList<One> {

}

1 Answers1

0

what is your question? the implementation looks ok, for the happy flow at least. Look, I've extracted the variables namesFromOne and optionsFromTwo.

oneFlux.filterWhen(one -> twoMono.map(two -> {
            List<String> namesFromOne = one.getOneInternalList()
                    .stream()
                    .map(One.OneInternal::getName).toList();
            List<String> optionsFromTwo = two.getTwoInternalList().stream()
                    .filter(s -> s.getId().equals(id))
                    .findFirst().get().getOptions();
            return namesFromOne.containsAll(optionsFromTwo);
        })
);

the only problem I see is with that .findFirst().get(). You would ideally like to return false there, not to throw an exception, so what you can do would be something like this:

Optional<Two.TwoInternal> optionalTwoInternal = two.getTwoInternalList().stream()
        .filter(s -> s.getId().equals(id))
        .findFirst();

return optionalTwoInternal
        .map(twoInternal -> namesFromOne.containsAll(twoInternal.getOptions()))
        .orElse(false);

some small / simple test:

@Test
void test() {
    Flux<One> oneFlux = Flux.just(
            createOne("id1", "a", "b"),
            createOne("id2", "c", "d"));

    Mono<Two> twoMono = Mono.just(Two.builder()
            .twoInternalList(List.of(
                    createTwoInternal("id1", "a", "b"),
                    createTwoInternal("id2", "e", "f"),
                    createTwoInternal("id4", "g", "h")
            )).build());

    var resp1 = getObjOneFlux(oneFlux, twoMono, "id1").collectList().block();
    System.err.println(resp1); // prints first element of oneFlux

    var resp2 = getObjOneFlux(oneFlux, twoMono, "id2").collectList().block();
    System.err.println(resp2); // prints empty array
}

as I can see, your filtering works. Perhaps you can think a bit about your model because TwoInternal looks a lot like class One so maybe you can reuse some things there.

Emanuel Trandafir
  • 1,526
  • 1
  • 5
  • 13