Skip to content

Commit

Permalink
Merge pull request #21807 from Sgitario/reactive_ClientHeadersFactory
Browse files Browse the repository at this point in the history
Rest Client Reactive: Add reactive flavor for ClientHeadersFactory
  • Loading branch information
geoand authored Nov 30, 2021
2 parents d4709b7 + 87be3ed commit d8199b3
Show file tree
Hide file tree
Showing 4 changed files with 155 additions and 2 deletions.
33 changes: 32 additions & 1 deletion docs/src/main/asciidoc/rest-client-reactive.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -515,7 +515,7 @@ More details about this can be found in https://smallrye.io/smallrye-mutiny/#_un

There are a few ways in which you can specify custom headers for your REST calls:

- by registering a `ClientHeadersFactory` with the `@RegisterClientHeaders` annotation
- by registering a `ClientHeadersFactory` or a `ReactiveClientHeadersFactory` with the `@RegisterClientHeaders` annotation
- by specifying the value of the header with `@ClientHeaderParam`
- by specifying the value of the header by `@HeaderParam`

Expand Down Expand Up @@ -591,6 +591,37 @@ To specify a value for `${header.value}`, simply put the following in your `appl
header.value=value of the header
----

Also, there is a reactive flavor of `ClientHeadersFactory` that allows doing blocking operations. For example:

[source, java]
----
package org.acme.rest.client;
import org.eclipse.microprofile.rest.client.ext.ClientHeadersFactory;
import javax.enterprise.context.ApplicationScoped;
import javax.ws.rs.core.MultivaluedHashMap;
import javax.ws.rs.core.MultivaluedMap;
import java.util.UUID;
@ApplicationScoped
public class GetTokenReactiveClientHeadersFactory extends ReactiveClientHeadersFactory {
@Inject
Service service;
@Override
public Uni<MultivaluedMap<String, String>> getHeaders(MultivaluedMap<String, String> incomingHeaders) {
return Uni.createFrom().item(() -> {
MultivaluedHashMap<String, String> newHeaders = new MultivaluedHashMap<>();
// perform blocking call
newHeaders.add(HEADER_NAME, service.getToken());
return newHeaders;
});
}
}
----

=== Default header factory

The `@RegisterClientHeaders` annotation can also be used without any custom factory specified. In that case the `DefaultClientHeadersFactoryImpl` factory will be used.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package io.quarkus.rest.client.reactive.headers;

import static org.assertj.core.api.Assertions.assertThat;

import java.net.URI;

import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import javax.ws.rs.GET;
import javax.ws.rs.HeaderParam;
import javax.ws.rs.Path;
import javax.ws.rs.core.MultivaluedHashMap;
import javax.ws.rs.core.MultivaluedMap;

import org.eclipse.microprofile.rest.client.RestClientBuilder;
import org.eclipse.microprofile.rest.client.annotation.RegisterClientHeaders;
import org.jboss.shrinkwrap.api.asset.StringAsset;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import io.quarkus.rest.client.reactive.ReactiveClientHeadersFactory;
import io.quarkus.test.QuarkusUnitTest;
import io.quarkus.test.common.http.TestHTTPResource;
import io.smallrye.common.annotation.Blocking;
import io.smallrye.mutiny.Uni;

public class ReactiveClientHeadersFromProviderTest {
private static final String HEADER_NAME = "my-header";
private static final String HEADER_VALUE = "oifajrofijaeoir5gjaoasfaxcvcz";

@TestHTTPResource
URI baseUri;

@RegisterExtension
static final QuarkusUnitTest config = new QuarkusUnitTest()
.withApplicationRoot((jar) -> jar.addClasses(ReactiveClientHeadersFromProviderTest.Client.class)
.addAsResource(
new StringAsset("my.property-value=" + HEADER_VALUE),
"application.properties"));

@Test
void shouldSetHeaderFromProperties() {
ReactiveClientHeadersFromProviderTest.Client client = RestClientBuilder.newBuilder().baseUri(baseUri)
.build(ReactiveClientHeadersFromProviderTest.Client.class);

assertThat(client.getWithHeader()).isEqualTo(HEADER_VALUE);
}

@Path("/")
@ApplicationScoped
public static class Resource {
@GET
public String returnHeaderValue(@HeaderParam(HEADER_NAME) String header) {
return header;
}
}

@ApplicationScoped
public static class Service {
@Blocking
public String getValue() {
return HEADER_VALUE;
}
}

@RegisterClientHeaders(CustomReactiveClientHeadersFactory.class)
public interface Client {
@GET
String getWithHeader();
}

public static class CustomReactiveClientHeadersFactory extends ReactiveClientHeadersFactory {

@Inject
Service service;

@Override
public Uni<MultivaluedMap<String, String>> getHeaders(MultivaluedMap<String, String> incomingHeaders) {
return Uni.createFrom().item(() -> {
MultivaluedHashMap<String, String> newHeaders = new MultivaluedHashMap<>();
newHeaders.add(HEADER_NAME, service.getValue());
return newHeaders;
});
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package io.quarkus.rest.client.reactive;

import javax.ws.rs.core.MultivaluedMap;

import org.eclipse.microprofile.rest.client.ext.ClientHeadersFactory;

import io.smallrye.mutiny.Uni;

/**
* Reactive ClientHeadersFactory flavor for Quarkus rest-client reactive extension.
*/
public abstract class ReactiveClientHeadersFactory implements ClientHeadersFactory {
public abstract Uni<MultivaluedMap<String, String>> getHeaders(MultivaluedMap<String, String> incomingHeaders);

@Override
public final MultivaluedMap<String, String> update(MultivaluedMap<String, String> incomingHeaders,
MultivaluedMap<String, String> clientOutgoingHeaders) {
throw new RuntimeException(
"Can't call `update` method in a Reactive context. Use `getHeaders` or implement ClientHeadersFactory.");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,11 @@

import org.eclipse.microprofile.rest.client.ext.ClientHeadersFactory;
import org.eclipse.microprofile.rest.client.ext.DefaultClientHeadersFactoryImpl;
import org.jboss.resteasy.reactive.client.spi.ResteasyReactiveClientRequestContext;

import io.quarkus.arc.Arc;
import io.quarkus.rest.client.reactive.HeaderFiller;
import io.quarkus.rest.client.reactive.ReactiveClientHeadersFactory;

@Priority(Integer.MIN_VALUE)
public class MicroProfileRestClientRequestFilter implements ClientRequestFilter {
Expand Down Expand Up @@ -63,7 +65,20 @@ public void filter(ClientRequestContext requestContext) {
}

if (clientHeadersFactory != null) {
incomingHeaders = clientHeadersFactory.update(incomingHeaders, headers);
if (clientHeadersFactory instanceof ReactiveClientHeadersFactory) {
// reactive
ResteasyReactiveClientRequestContext reactiveRequestContext = (ResteasyReactiveClientRequestContext) requestContext;
ReactiveClientHeadersFactory reactiveClientHeadersFactory = (ReactiveClientHeadersFactory) clientHeadersFactory;
reactiveRequestContext.suspend();
MultivaluedMap<String, String> outgoingHeaders = incomingHeaders;
reactiveClientHeadersFactory.getHeaders(incomingHeaders).subscribe().with(newHeaders -> {
outgoingHeaders.putAll(newHeaders);
reactiveRequestContext.resume();
}, reactiveRequestContext::resume);
} else {
// blocking
incomingHeaders = clientHeadersFactory.update(incomingHeaders, headers);
}
}

for (Map.Entry<String, List<String>> headerEntry : incomingHeaders.entrySet()) {
Expand Down

0 comments on commit d8199b3

Please sign in to comment.