Skip to content

Commit

Permalink
Rest Client Reactive: SmallRye Stork integration
Browse files Browse the repository at this point in the history
  • Loading branch information
michalszynkiewicz committed Oct 4, 2021
1 parent fdb9f5e commit ec20dd3
Show file tree
Hide file tree
Showing 32 changed files with 879 additions and 55 deletions.
16 changes: 16 additions & 0 deletions bom/application/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
<smallrye-reactive-types-converter.version>2.6.0</smallrye-reactive-types-converter.version>
<smallrye-mutiny-vertx-binding.version>2.13.0</smallrye-mutiny-vertx-binding.version>
<smallrye-reactive-messaging.version>3.9.1</smallrye-reactive-messaging.version>
<smallrye-stork.version>1.0.0.Alpha6</smallrye-stork.version>
<jakarta.activation.version>1.2.1</jakarta.activation.version>
<jakarta.annotation-api.version>1.3.5</jakarta.annotation-api.version>
<jakarta.el-impl.version>3.0.4</jakarta.el-impl.version>
Expand Down Expand Up @@ -3533,6 +3534,21 @@
<artifactId>smallrye-jwt-build</artifactId>
<version>${smallrye-jwt.version}</version>
</dependency>
<dependency>
<groupId>io.smallrye.stork</groupId>
<artifactId>smallrye-stork-load-balancer-response-time</artifactId>
<version>${smallrye-stork.version}</version>
</dependency>
<dependency>
<groupId>io.smallrye.stork</groupId>
<artifactId>smallrye-stork-service-discovery-static-list</artifactId>
<version>${smallrye-stork.version}</version>
</dependency>
<dependency>
<groupId>io.smallrye.stork</groupId>
<artifactId>smallrye-stork-microprofile-config</artifactId>
<version>${smallrye-stork.version}</version>
</dependency>
<dependency>
<groupId>jakarta.activation</groupId>
<artifactId>jakarta.activation-api</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,17 @@
<artifactId>jakarta.servlet-api</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependency>
<groupId>io.smallrye.stork</groupId>
<artifactId>smallrye-stork-load-balancer-response-time</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.smallrye.stork</groupId>
<artifactId>smallrye-stork-service-discovery-static-list</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<build>
<plugins>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@
import static io.quarkus.rest.client.reactive.deployment.DotNames.REGISTER_CLIENT_HEADERS;
import static io.quarkus.rest.client.reactive.deployment.DotNames.REGISTER_PROVIDER;
import static io.quarkus.rest.client.reactive.deployment.DotNames.REGISTER_PROVIDERS;
import static java.util.Arrays.asList;
import static org.jboss.resteasy.reactive.common.processor.EndpointIndexer.CDI_WRAPPER_SUFFIX;
import static org.jboss.resteasy.reactive.common.processor.scanning.ResteasyReactiveScanner.BUILTIN_HTTP_ANNOTATIONS_TO_METHOD;

import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
Expand Down Expand Up @@ -54,14 +54,18 @@
import io.quarkus.deployment.Feature;
import io.quarkus.deployment.annotations.BuildProducer;
import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.annotations.Consume;
import io.quarkus.deployment.annotations.ExecutionTime;
import io.quarkus.deployment.annotations.Record;
import io.quarkus.deployment.builditem.CombinedIndexBuildItem;
import io.quarkus.deployment.builditem.ConfigurationTypeBuildItem;
import io.quarkus.deployment.builditem.ExtensionSslNativeSupportBuildItem;
import io.quarkus.deployment.builditem.FeatureBuildItem;
import io.quarkus.deployment.builditem.RuntimeConfigSetupCompleteBuildItem;
import io.quarkus.deployment.builditem.ShutdownContextBuildItem;
import io.quarkus.deployment.builditem.nativeimage.NativeImageResourceBuildItem;
import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem;
import io.quarkus.deployment.builditem.nativeimage.ServiceProviderBuildItem;
import io.quarkus.deployment.util.AsmUtil;
import io.quarkus.gizmo.ClassCreator;
import io.quarkus.gizmo.MethodCreator;
Expand All @@ -77,9 +81,13 @@
import io.quarkus.rest.client.reactive.runtime.RestClientReactiveCDIWrapperBase;
import io.quarkus.rest.client.reactive.runtime.RestClientReactiveConfig;
import io.quarkus.rest.client.reactive.runtime.RestClientRecorder;
import io.quarkus.rest.client.reactive.runtime.SmallRyeStorkRecorder;
import io.quarkus.restclient.config.RestClientConfigUtils;
import io.quarkus.restclient.config.RestClientsConfig;
import io.quarkus.resteasy.reactive.spi.ContainerRequestFilterBuildItem;
import io.smallrye.stork.microprofile.MicroProfileConfigProvider;
import io.smallrye.stork.spi.LoadBalancerProvider;
import io.smallrye.stork.spi.ServiceDiscoveryProvider;

class RestClientReactiveProcessor {

Expand All @@ -95,6 +103,16 @@ void announceFeature(BuildProducer<FeatureBuildItem> features) {
features.produce(new FeatureBuildItem(Feature.REST_CLIENT_REACTIVE));
}

@BuildStep
void registerServiceProviders(BuildProducer<ServiceProviderBuildItem> services) {
services.produce(new ServiceProviderBuildItem(io.smallrye.stork.config.ConfigProvider.class.getName(),
MicroProfileConfigProvider.class.getName()));

for (Class<?> providerClass : asList(LoadBalancerProvider.class, ServiceDiscoveryProvider.class)) {
services.produce(ServiceProviderBuildItem.allProvidersFromClassPath(providerClass.getName()));
}
}

@BuildStep
void registerQueryParamStyleForConfig(BuildProducer<ConfigurationTypeBuildItem> configurationTypes) {
configurationTypes.produce(new ConfigurationTypeBuildItem(QueryParamStyle.class));
Expand Down Expand Up @@ -135,14 +153,20 @@ void registerRestClientListenerForTracing(

@BuildStep
@Record(ExecutionTime.STATIC_INIT)
void setupAdditionalBeans(
BuildProducer<AdditionalBeanBuildItem> additionalBeans,
void setupAdditionalBeans(BuildProducer<AdditionalBeanBuildItem> additionalBeans,
RestClientRecorder restClientRecorder) {
restClientRecorder.setRestClientBuilderResolver();
additionalBeans.produce(new AdditionalBeanBuildItem(RestClient.class));
additionalBeans.produce(AdditionalBeanBuildItem.unremovableOf(HeaderContainer.class));
}

@BuildStep
@Record(ExecutionTime.RUNTIME_INIT)
@Consume(RuntimeConfigSetupCompleteBuildItem.class)
void initializeStork(SmallRyeStorkRecorder storkRecorder, ShutdownContextBuildItem shutdown) {
storkRecorder.initialize(shutdown);
}

@BuildStep
UnremovableBeanBuildItem makeConfigUnremovable() {
return UnremovableBeanBuildItem.beanTypes(RestClientsConfig.class);
Expand Down Expand Up @@ -220,7 +244,7 @@ void registerProvidersFromAnnotations(CombinedIndexBuildItem indexBuildItem,
for (AnnotationInstance annotation : index.getAnnotations(REGISTER_PROVIDERS)) {
String targetClass = annotation.target().asClass().name().toString();
annotationsByClassName.computeIfAbsent(targetClass, key -> new ArrayList<>())
.addAll(Arrays.asList(annotation.value().asNestedArray()));
.addAll(asList(annotation.value().asNestedArray()));
}

try (ClassCreator classCreator = ClassCreator.builder()
Expand Down Expand Up @@ -310,7 +334,7 @@ AdditionalBeanBuildItem registerProviderBeans(CombinedIndexBuildItem combinedInd
IndexView index = combinedIndex.getIndex();
List<AnnotationInstance> allInstances = new ArrayList<>(index.getAnnotations(REGISTER_PROVIDER));
for (AnnotationInstance annotation : index.getAnnotations(REGISTER_PROVIDERS)) {
allInstances.addAll(Arrays.asList(annotation.value().asNestedArray()));
allInstances.addAll(asList(annotation.value().asNestedArray()));
}
allInstances.addAll(index.getAnnotations(REGISTER_CLIENT_HEADERS));
AdditionalBeanBuildItem.Builder builder = AdditionalBeanBuildItem.builder().setUnremovable();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package io.quarkus.rest.client.reactive.stork;

import javax.ws.rs.GET;
import javax.ws.rs.Path;

import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;

@Path("/")
@RegisterRestClient(configKey = "hello2")
public interface HelloClient {
@GET
String hello();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package io.quarkus.rest.client.reactive.stork;

import javax.ws.rs.GET;
import javax.ws.rs.Path;

@Path("/hello")
public class HelloResource {

public static final String HELLO_WORLD = "Hello, World!";

@GET
public String hello() {
return HELLO_WORLD;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package io.quarkus.rest.client.reactive.stork;

import java.net.URI;

import javax.ws.rs.GET;
import javax.ws.rs.Path;

import org.eclipse.microprofile.rest.client.RestClientBuilder;
import org.eclipse.microprofile.rest.client.inject.RestClient;

@Path("/helper")
public class PassThroughResource {

@RestClient
HelloClient client;

@GET
public String invokeClient() {
HelloClient client = RestClientBuilder.newBuilder()
.baseUri(URI.create("stork://hello-service/hello"))
.build(HelloClient.class);
return client.hello();
}

@Path("/cdi")
@GET
public String invokeCdiClient() {
return client.hello();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package io.quarkus.rest.client.reactive.stork;

import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options;
import static io.quarkus.rest.client.reactive.stork.HelloResource.HELLO_WORLD;
import static io.restassured.RestAssured.when;
import static org.hamcrest.Matchers.equalTo;

import java.io.File;

import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.spec.JavaArchive;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import com.github.tomakehurst.wiremock.WireMockServer;
import com.github.tomakehurst.wiremock.client.WireMock;

import io.quarkus.test.QuarkusDevModeTest;

public class StorkDevModeTest {

public static final String WIREMOCK_RESPONSE = "response from the wiremock server";

private static WireMockServer wireMockServer;

@BeforeAll
public static void setUp() {
wireMockServer = new WireMockServer(options().port(8766));
wireMockServer.stubFor(WireMock.get("/hello")
.willReturn(aResponse().withFixedDelay(1000)
.withBody(WIREMOCK_RESPONSE).withStatus(200)));
wireMockServer.start();
}

@AfterAll
public static void shutDown() {
wireMockServer.stop();
}

@RegisterExtension
static QuarkusDevModeTest TEST = new QuarkusDevModeTest()
.setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)
.addClasses(PassThroughResource.class, HelloResource.class, HelloClient.class)
.addAsResource(
new File("src/test/resources/stork-dev-application.properties"),
"application.properties"));

@Test
void shouldModifyStorkSettings() {
// @formatter:off
when()
.get("/helper")
.then()
.statusCode(200)
.body(equalTo(HELLO_WORLD));
// @formatter:on

TEST.modifyResourceFile("application.properties",
v -> v.replaceAll("stork.hello-service.service-discovery.1=.*",
"stork.hello-service.service-discovery.1=localhost:8766"));
// @formatter:off
when()
.get("/helper")
.then()
.statusCode(200)
.body(equalTo(WIREMOCK_RESPONSE));
// @formatter:on
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package io.quarkus.rest.client.reactive.stork;

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

import java.net.URI;

import org.eclipse.microprofile.rest.client.RestClientBuilder;
import org.eclipse.microprofile.rest.client.inject.RestClient;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.spec.JavaArchive;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.Timeout;
import org.junit.jupiter.api.extension.RegisterExtension;

import io.quarkus.rest.client.reactive.HelloClient2;
import io.quarkus.rest.client.reactive.HelloResource;
import io.quarkus.test.QuarkusUnitTest;

public class StorkIntegrationTest {
@RegisterExtension
static final QuarkusUnitTest TEST = new QuarkusUnitTest()
.setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)
.addClasses(HelloClient2.class, HelloResource.class))
.withConfigurationResource("stork-application.properties");

@RestClient
HelloClient2 client;

@Test
void shouldDetermineUrlViaStork() {
String greeting = RestClientBuilder.newBuilder().baseUri(URI.create("stork://hello-service/hello"))
.build(HelloClient2.class)
.echo("black and white bird");
assertThat(greeting).isEqualTo("hello, black and white bird");
}

@Test
void shouldDetermineUrlViaStorkCDI() {
String greeting = client.echo("big bird");
assertThat(greeting).isEqualTo("hello, big bird");
}

@Test
@Timeout(20)
void shouldFailOnUnknownService() {
HelloClient2 client2 = RestClientBuilder.newBuilder()
.baseUri(URI.create("stork://nonexistent-service"))
.build(HelloClient2.class);
assertThatThrownBy(() -> client2.echo("foo")).isInstanceOf(IllegalArgumentException.class);
}

@Test
@Timeout(20)
void shouldFailForServiceWithoutEndpoints() {
HelloClient2 client2 = RestClientBuilder.newBuilder()
.baseUri(URI.create("stork://service-without-endpoints"))
.build(HelloClient2.class);
assertThatThrownBy(() -> client2.echo("foo")).isInstanceOf(IllegalArgumentException.class);
}
}
Loading

0 comments on commit ec20dd3

Please sign in to comment.