4

I'm enriching data by calling multiple web services for each item returned as the result of a previous webservice call (Ie - fanning out).

I'm using Kotlin, Spring Boot 2 and the new reactive WebClient.

In this snippet I'm showing just the code fanning out to the webservices, however in reality this is at the end of a larger pipeline.

Previously I'd been using Mono.zip(t1, t2, t3) interface which supports passing up to 8 values and returns a tuple with the correct types.

Definition:

    public static <T1, T2, T3, T4, T5, T6, T7, T8> Mono<Tuple8<T1, T2, T3, T4, T5, T6, T7, T8>> zip(Mono<? extends T1> p1,
            Mono<? extends T2> p2,
            Mono<? extends T3> p3,
            Mono<? extends T4> p4,
            Mono<? extends T5> p5,
            Mono<? extends T6> p6,
            Mono<? extends T7> p7,
            Mono<? extends T8> p8) {
        return onAssembly(new MonoZip(false, a -> Tuples.fromArray((Object[])a), p1, p2, p3, p4, p5, p6, p7, p8));
    }

I now have more than 8 services to call so am looking at the Mono.zip(combinatorFn, ... monos) method of combining the results of the calls.

Definition:

    public static <R> Mono<R> zip(Function<? super Object[], ? extends R> combinator, Mono<?>... monos) {
        if (monos.length == 0) {
            return empty();
        }
        if (monos.length == 1) {
            return monos[0].map(d -> combinator.apply(new Object[]{d}));
        }
        return onAssembly(new MonoZip<>(false, combinator, monos));
    }

My problem using KOTLIN I'm having difficulty defining the combinator function at the call site. Here is what I have so far:

val tst: (Array<Any>) -> Mono<AggregateReport> = { it -> Mono.just(AggregateReport("Test")) }


val res = Mono.zip(
        tst,
        Client.callThatReturnsAMono(),
        Client.callThatReturnsAMono(),
        Client.callThatReturnsAMono(),
        Client.callThatReturnsAMono(),
        Client.callThatReturnsAMono(),
        Client.callThatReturnsAMono(),
        Client.callThatReturnsAMono(),
        Client.callThatReturnsAMono(),
        Client.callThatReturnsAMono()
)

Obviously I'd need to enhance the combinator to actually work with the input array and produce the aggregate. However the code doesn't currently compile.

Compile error:

Error:(339, 41) Kotlin: Type mismatch: inferred type is (Array<Any>) -> Mono<ConsoleApplication.AggregateReport> but Function<in Array<(out) Any!>!, out (???..???)> was expected

Any suggestions?

Thanks!

  • What type are this 8+ responses? Are this request returns the same data type? What is your expected final output type? – mslowiak Sep 04 '19 at 16:27
  • My answer didn't worked for you? Can you check it, please? :) – adrianbukros Sep 05 '19 at 20:05
  • I had 8 Types that all extend `Report`. The result i wanted was an `AggregateReport`. The combinator performs type assertions on the different reports to produce the aggregate, – Ben Andersen-Waine Sep 06 '19 at 14:47

4 Answers4

2

After some additional research I found that there are Kotlin Extensions for Reactor that provide a zip function with a nicer method signature for combining N monos.

IE - fun <R> zip(vararg monos: Mono<*>, combinator: (Array<*>) -> R): Mono<R>

In this way I was able to supply a combinator that expected an Array and returned a Mono. I implemented a generic combinator that could inspect the types of the incoming responses and type assert to the correct subclass of the various properties of my aggregate.

https://github.com/reactor/reactor-kotlin-extensions

0

In pure kotlin you can't do this.

The signature of method zip that you present in question description expects Function type which is java type.

This (kotlin one):

    val kotlin: (List<String>) -> String = { array: List<String> -> "kotlin" }

is not equivalent to (java):

    Function<List<String>, String> java = array -> "java"
mslowiak
  • 1,688
  • 12
  • 25
0

What you can do is the following:

import reactor.core.publisher.Mono
import java.util.function.Function

...

fun main() {
    val tst = Function<Array<Any>, Mono<AggregateReport>> { Mono.just(AggregateReport("Test")) }

    Mono.zip(
        tst,
        Client.callThatReturnsAMono(),
        Client.callThatReturnsAMono(),
        Client.callThatReturnsAMono(),
        Client.callThatReturnsAMono(),
        Client.callThatReturnsAMono(),
        Client.callThatReturnsAMono(),
        Client.callThatReturnsAMono(),
        Client.callThatReturnsAMono(),
        Client.callThatReturnsAMono(),
        Client.callThatReturnsAMono()
    )
}

You have to actually use the java.util.function.Function type but Kotlin is helping to make a look a bit nicer.

adrianbukros
  • 326
  • 2
  • 7
0

Without a combinatory function, you can use zipWith, in this way:

Mono
    .zip(t1, t2, t3, t4, t5, t6, t7, t8).zipWith(t9).thenReturn(result);

Hope it could be useful, for me it was.

Alex
  • 11
  • 2