From 5b4342d8203979a0da9301ce465811c95205148b Mon Sep 17 00:00:00 2001 From: Georgios Andrianakis Date: Tue, 21 Nov 2023 12:18:26 +0200 Subject: [PATCH] Document SSE usage in REST Client (cherry picked from commit 6f41d71bf861bb64c3404c85c1ce7856b4c6f62d) --- .../main/asciidoc/rest-client-reactive.adoc | 101 ++++++++++++++++++ 1 file changed, 101 insertions(+) diff --git a/docs/src/main/asciidoc/rest-client-reactive.adoc b/docs/src/main/asciidoc/rest-client-reactive.adoc index 4d1af8480e4d3..b3c063bccdda2 100644 --- a/docs/src/main/asciidoc/rest-client-reactive.adoc +++ b/docs/src/main/asciidoc/rest-client-reactive.adoc @@ -883,6 +883,107 @@ If you use a `CompletionStage`, you would need to call the service's method to r This difference comes from the laziness aspect of Mutiny and its subscription protocol. More details about this can be found in https://smallrye.io/smallrye-mutiny/latest/reference/uni-and-multi/[the Mutiny documentation]. +=== Server-Sent Event (SSE) support + +Consuming SSE events is possible simply by declaring the result type as a `io.smallrye.mutiny.Multi`. + +The simplest example is: + +[source, java] +---- +package org.acme.rest.client; + +import io.smallrye.mutiny.Multi; +import org.eclipse.microprofile.rest.client.inject.RegisterRestClient; + +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; + +@Path("/sse") +@RegisterRestClient(configKey = "some-api") +public interface SseClient { + @GET + @Produces(MediaType.SERVER_SENT_EVENTS) + Multi get(); +} +---- + +[NOTE] +==== +All the IO involved in streaming the SSE results is done in a non-blocking manner. +==== + +Results are not limited to strings - for example when the server returns JSON payload for each event, Quarkus automatically deserializes it into the generic type used in the `Multi`. + +[TIP] +==== +Users can also access the entire SSE event by using the `org.jboss.resteasy.reactive.client.SseEvent` type. + +A simple example where the event payloads are `Long` values is the following: + +[source, java] +---- +package org.acme.rest.client; + +import io.smallrye.mutiny.Uni; +import org.eclipse.microprofile.rest.client.inject.RegisterRestClient; +import org.jboss.resteasy.reactive.client.SseEvent; + +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.QueryParam; + +@Path("/sse") +@RegisterRestClient(configKey = "some-api") +public interface SseClient { + @GET + @Produces(MediaType.SERVER_SENT_EVENTS) + Multi> get(); +} +---- +==== + +==== Filtering out events + +On occasion, the stream of SSE events may contain some events that should not be returned by the client - an example of this is having the server send heartbeat events in order to keep the underlying TCP connection open. +The REST Client supports filtering out such events by providing the `@org.jboss.resteasy.reactive.client.SseEventFilter`. + +Here is an example of filtering out heartbeat events: + +[source,java] +---- +package org.acme.rest.client; + +import io.smallrye.mutiny.Uni; +import java.util.function.Predicate; +import org.eclipse.microprofile.rest.client.inject.RegisterRestClient; +import org.jboss.resteasy.reactive.client.SseEvent; +import org.jboss.resteasy.reactive.client.SseEventFilter; + +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.QueryParam; + +@Path("/sse") +@RegisterRestClient(configKey = "some-api") +public interface SseClient { + + @GET + @Produces(MediaType.SERVER_SENT_EVENTS) + @SseEventFilter(HeartbeatFilter.class) + Multi> get(); + + + class HeartbeatFilter implements Predicate> { + + @Override + public boolean test(SseEvent event) { + return !"heartbeat".equals(event.id()); + } + } +} +---- + == Custom headers support There are a few ways in which you can specify custom headers for your REST calls: