Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add the Uni.chain and Uni.then shortcuts #204

Merged
merged 2 commits into from Jul 9, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
25 changes: 25 additions & 0 deletions documentation/src/main/docs/faq.adoc
Expand Up @@ -117,6 +117,31 @@ include::../../../src/test/java/snippets/EventsTest.java[tags=invoke-uni]

`invokeUni` is a shortcut for `onItem().invokeUni(...)`.


.List of the shortcuts offered by the Uni class
|===
|Shortcut | Equivalent

|`uni.map(x -> y)`
|`uni.onItem().transform(x -> y)`

|`uni.flatMap(x -> uni2)`
|`uni.onItem().transformToUni(x -> uni2)`

|`uni.chain(x -> uni2)`
|`uni.onItem().transformToUni(x -> uni2)`

|`uni.then(() -> uni2)`
|`uni.onItem().transformToUni(ignored -> uni2)`

|`uni.invoke(x -> System.out.println(x))`
|`uni.onItem().invoke(x -> System.out.println(x))`

|`uni.invokeUni(x -> uni2)`
|`uni.onItem().invokeUni(x -> uni2)`
|===


=== How do I merge, concatenate or combine Multis?

You can create instances of `Multi` by concatenating, merging, or combining ``Multi``s and ``Publisher``s:
Expand Down
6 changes: 6 additions & 0 deletions documentation/src/test/java/snippets/FlatMapTest.java
Expand Up @@ -62,6 +62,11 @@ public void mutiny() {
.onItem().transformToUni(i -> Uni.createFrom().item(i + 1))
.await().indefinitely();

// Shortcut for .onItem().transformToUni
int result3 = uni
.chain(i -> Uni.createFrom().item(i + 1))
.await().indefinitely();

List<Integer> list = multi
.onItem().transform(i -> i + 1)
.collectItems().asList()
Expand Down Expand Up @@ -95,6 +100,7 @@ public void mutiny() {
// end::mutiny[]
assertThat(result).isEqualTo(2);
assertThat(result2).isEqualTo(2);
assertThat(result3).isEqualTo(2);
assertThat(list).containsExactly(2, 3);
assertThat(list2).containsExactly(1, 1, 2, 2);
assertThat(list3).containsExactly(1, 1, 2, 2);
Expand Down
68 changes: 68 additions & 0 deletions implementation/src/main/java/io/smallrye/mutiny/Uni.java
Expand Up @@ -8,6 +8,7 @@
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;

import io.smallrye.mutiny.groups.*;
import io.smallrye.mutiny.subscription.UniEmitter;
Expand Down Expand Up @@ -446,6 +447,7 @@ default Uni<T> invokeUni(Function<? super T, ? extends Uni<?>> action) {
* {@link Uni} returned by this method.
* <p>
* This operation is generally named {@code flatMap}.
* This method is a shortcut on {@link UniOnItem#transformToUni(Function)} onItem().transformToUni(mapper)}.
*
* @param mapper the function called with the item of the this {@link Uni} and producing the {@link Uni},
* must not be {@code null}, must not return {@code null}.
Expand All @@ -457,6 +459,72 @@ default <O> Uni<O> flatMap(Function<? super T, Uni<? extends O>> mapper) {
return onItem().transformToUni(nonNull(mapper, "mapper"));
}

/**
* One the observed {@code Uni} emits an item, execute the given {@code mapper}. This mapper produces another
* {@code Uni}. The downstream receives the events emitted by this produced {@code Uni}.
*
* This operation allows <em>chaining</em> asynchronous operations: when the upstream completes with an item, run
* the mapper and emits the item (or failure) sent by the produced {@code Uni}:
*
* <pre>
* Uni&lt;Session&gt; uni = getSomeSession();
* return uni.chain(session -&gt; session.persist(fruit))
* .chain(session -&gt; session.flush())
* .map(x -&gt; Response.ok(fruit).status(201).build());
* </pre>
*
* The mapper is called with the item event of the current {@link Uni} and produces an {@link Uni}, possibly
* using another type of item ({@code R}). The events fired by produced {@link Uni} are forwarded to the
* {@link Uni} returned by this method.
* <p>
* This operation is generally named {@code flatMap}.
* This method is a shortcut for {@link UniOnItem#transformToUni(Function) onItem().transformToUni(mapper)}.
*
* @param mapper the function called with the item of the this {@link Uni} and producing the {@link Uni},
* must not be {@code null}, must not return {@code null}.
* @param <O> the type of item
* @return a new {@link Uni} that would fire events from the uni produced by the mapper function, possibly
* in an asynchronous manner.
* @see #then(Supplier)
*/
default <O> Uni<O> chain(Function<? super T, Uni<? extends O>> mapper) {
return onItem().transformToUni(nonNull(mapper, "mapper"));
}

/**
* One the observed {@code Uni} emits an item, execute the given {@code supplier}. Unlike {@link #chain(Function)},
* the received item is not required to run the {@code supplier}, and so omitted. The supplier produces another
* {@code Uni}. The downstream receives the events emitted by this produced {@code Uni}.
*
* This operation allows <em>chaining</em> asynchronous operation when you don't need the previous result: when the
* upstream completes with an item, run the supplier and emits the item (or failure) sent by the produced
* {@code Uni}:
*
* <pre>
* {@code
* String id = ...;
* Session session = getSomeSession();
* session.find(Fruit.class, id)
* .chain(fruit -> session.remove(fruit)
* .then(() -> session.flush());
* }
* </pre>
*
* This method is a shortcut for {@link UniOnItem#transformToUni(Function)
* onItem().transformToUni(ignored -> supplier.get())}.
*
* @param supplier the function called when the item of the this {@link Uni} is emitted and producing the {@link Uni},
* must not be {@code null}, must not return {@code null}.
* @param <O> the type of item
* @return a new {@link Uni} that would fire events from the uni produced by the mapper function, possibly
* in an asynchronous manner.
* @see #chain(Function)
*/
default <O> Uni<O> then(Supplier<Uni<? extends O>> supplier) {
Supplier<Uni<? extends O>> actual = nonNull(supplier, "supplier");
return onItem().transformToUni(ignored -> actual.get());
}

/**
* Converts an {@link Uni} to other types such as {@link CompletionStage}
*
Expand Down
Expand Up @@ -7,6 +7,7 @@
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
import java.util.function.Supplier;

import org.testng.annotations.Test;

Expand All @@ -31,12 +32,36 @@ public void testProduceUniWithImmediateValueDeprecated() {
}

@Test
public void testTransformToUniShortcut() {
public void testTransformToUniShortcutFlatmap() {
UniAssertSubscriber<Integer> test = UniAssertSubscriber.create();
Uni.createFrom().item(1).flatMap(v -> Uni.createFrom().item(2)).subscribe().withSubscriber(test);
test.assertCompletedSuccessfully().assertItem(2).assertNoFailure();
}

@Test
public void testTransformToUniShortcutChain() {
UniAssertSubscriber<Integer> test = UniAssertSubscriber.create();
Uni.createFrom().item(1).chain(v -> Uni.createFrom().item(2)).subscribe().withSubscriber(test);
test.assertCompletedSuccessfully().assertItem(2).assertNoFailure();
}

@Test
public void testTransformToUniShortcutThen() {
UniAssertSubscriber<Integer> test = UniAssertSubscriber.create();
Uni.createFrom().item(1).then(() -> Uni.createFrom().item(2)).subscribe().withSubscriber(test);
test.assertCompletedSuccessfully().assertItem(2).assertNoFailure();
}

@Test(expectedExceptions = IllegalArgumentException.class)
public void testTransformToUniShortcutThenWithNullSupplier() {
Uni.createFrom().item(1).then((Supplier<Uni<?>>) null);
}

@Test(expectedExceptions = IllegalArgumentException.class)
public void testTransformToUniShortcutChainWithNullMApper() {
Uni.createFrom().item(1).chain(null);
}

@Test
public void testWithImmediateCancellation() {
UniAssertSubscriber<Integer> test = new UniAssertSubscriber<>(true);
Expand Down
@@ -1,7 +1,6 @@
package io.smallrye.mutiny.unchecked;

import static io.smallrye.mutiny.unchecked.Unchecked.function;
import static io.smallrye.mutiny.unchecked.Unchecked.unchecked;
import static io.smallrye.mutiny.unchecked.Unchecked.*;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;

Expand Down Expand Up @@ -51,6 +50,10 @@ interface UniReader {
Uni<Integer> read(int i) throws IOException;
}

interface UniSupplier {
Uni<Integer> get() throws IOException;
}

@Test
public void testWithInterfaceMap() {
Reader reader = i -> i;
Expand All @@ -69,6 +72,24 @@ public void testWithFlatMap() {
assertThat(res).isEqualTo(1);
}

@Test
public void testWithChain() {
UniReader reader = i -> Uni.createFrom().item(i);
int res = Uni.createFrom().item(1)
.chain(function(reader::read))
.await().indefinitely();
assertThat(res).isEqualTo(1);
}

@Test
public void testWithThen() {
UniSupplier reader = () -> Uni.createFrom().item(23);
int res = Uni.createFrom().item(1)
.then(supplier(reader::get))
.await().indefinitely();
assertThat(res).isEqualTo(23);
}

private int validate(int i) throws IOException {
if (i != 0) {
return i;
Expand Down