From 010af604d2a4ef8818f3e33847714ec18ff23861 Mon Sep 17 00:00:00 2001 From: Jan Martiska Date: Wed, 27 Jul 2022 13:25:01 +0200 Subject: [PATCH] Attempt to autowire GraphQL clients in test mode --- .../asciidoc/smallrye-graphql-client.adoc | 5 +++ .../SmallRyeGraphQLClientProcessor.java | 16 +++++++- .../GraphQLClientConfigurationMergerBean.java | 40 +++++++++++++++++++ .../client/runtime/GraphQLClientSupport.java | 16 ++++++++ .../SmallRyeGraphQLClientRecorder.java | 4 +- 5 files changed, 78 insertions(+), 3 deletions(-) diff --git a/docs/src/main/asciidoc/smallrye-graphql-client.adoc b/docs/src/main/asciidoc/smallrye-graphql-client.adoc index 918f80a9b95dc3..fc17cf9559775a 100644 --- a/docs/src/main/asciidoc/smallrye-graphql-client.adoc +++ b/docs/src/main/asciidoc/smallrye-graphql-client.adoc @@ -229,6 +229,11 @@ or move this over to the configuration file, `application.properties`: quarkus.smallrye-graphql-client.star-wars-typesafe.url=https://swapi-graphql.netlify.app/.netlify/functions/index ---- +NOTE: During *tests only*, the URL is an optional property, and if it's not specified, Quarkus will assume +that the target of the client is the application that is being tested (typically, `http://localhost:8081/graphql`). +This is useful if your application contains a GraphQL server-side API as well as a GraphQL client that is used for +testing the API. + `star-wars-typesafe` is the name of the configured client instance, and corresponds to the `configKey` in the `@GraphQLClientApi` annotation. If you don't want to specify a custom name, you can leave out the `configKey`, and then refer to it by using the fully qualified name of the interface. diff --git a/extensions/smallrye-graphql-client/deployment/src/main/java/io/quarkus/smallrye/graphql/client/deployment/SmallRyeGraphQLClientProcessor.java b/extensions/smallrye-graphql-client/deployment/src/main/java/io/quarkus/smallrye/graphql/client/deployment/SmallRyeGraphQLClientProcessor.java index 63400a4b51cf84..bd8f8daa7b7868 100644 --- a/extensions/smallrye-graphql-client/deployment/src/main/java/io/quarkus/smallrye/graphql/client/deployment/SmallRyeGraphQLClientProcessor.java +++ b/extensions/smallrye-graphql-client/deployment/src/main/java/io/quarkus/smallrye/graphql/client/deployment/SmallRyeGraphQLClientProcessor.java @@ -140,17 +140,29 @@ void setTypesafeApiClasses(BeanArchiveIndexBuildItem index, */ @BuildStep @Record(RUNTIME_INIT) - void shortNamesToQualifiedNames(BuildProducer syntheticBeans, + void initializeClientSupport(BuildProducer syntheticBeans, SmallRyeGraphQLClientRecorder recorder, GraphQLClientsConfig quarkusConfig, BeanArchiveIndexBuildItem index) { + // to store config keys of all clients found in the application code + List knownConfigKeys = new ArrayList<>(); + Map shortNamesToQualifiedNames = new HashMap<>(); for (AnnotationInstance annotation : index.getIndex().getAnnotations(GRAPHQL_CLIENT_API)) { ClassInfo clazz = annotation.target().asClass(); shortNamesToQualifiedNames.put(clazz.name().withoutPackagePrefix(), clazz.name().toString()); + knownConfigKeys.add(clazz.name().toString()); + } + + for (AnnotationInstance annotation : index.getIndex().getAnnotations(GRAPHQL_CLIENT)) { + String configKey = annotation.value().asString(); + if (configKey == null) { + configKey = "default"; + } + knownConfigKeys.add(configKey); } - RuntimeValue support = recorder.clientSupport(shortNamesToQualifiedNames); + RuntimeValue support = recorder.clientSupport(shortNamesToQualifiedNames, knownConfigKeys); DotName supportClassName = DotName.createSimple(GraphQLClientSupport.class.getName()); SyntheticBeanBuildItem bean = SyntheticBeanBuildItem diff --git a/extensions/smallrye-graphql-client/runtime/src/main/java/io/quarkus/smallrye/graphql/client/runtime/GraphQLClientConfigurationMergerBean.java b/extensions/smallrye-graphql-client/runtime/src/main/java/io/quarkus/smallrye/graphql/client/runtime/GraphQLClientConfigurationMergerBean.java index 79c6f9282c0456..ebe1d544ef6861 100644 --- a/extensions/smallrye-graphql-client/runtime/src/main/java/io/quarkus/smallrye/graphql/client/runtime/GraphQLClientConfigurationMergerBean.java +++ b/extensions/smallrye-graphql-client/runtime/src/main/java/io/quarkus/smallrye/graphql/client/runtime/GraphQLClientConfigurationMergerBean.java @@ -7,6 +7,11 @@ import javax.inject.Inject; import javax.inject.Singleton; +import org.eclipse.microprofile.config.Config; +import org.eclipse.microprofile.config.ConfigProvider; +import org.jboss.logging.Logger; + +import io.quarkus.runtime.LaunchMode; import io.smallrye.graphql.client.impl.GraphQLClientConfiguration; import io.smallrye.graphql.client.impl.GraphQLClientsConfiguration; @@ -21,6 +26,8 @@ @Singleton public class GraphQLClientConfigurationMergerBean { + private final Logger logger = Logger.getLogger(GraphQLClientConfigurationMergerBean.class); + GraphQLClientsConfiguration upstreamConfigs; @Inject @@ -54,6 +61,32 @@ void enhanceGraphQLConfiguration() { } } + // allow automatically wiring client to the local server instance in test mode + if (LaunchMode.current() == LaunchMode.TEST) { + String testUrl = null; + for (String configKey : support.getKnownConfigKeys()) { + GraphQLClientConfiguration config = upstreamConfigs.getClient(configKey); + if (config.getUrl() == null) { + if (testUrl == null) { + testUrl = getTestingServerUrl(); + } + logger.info("Automatically wiring the URL of GraphQL client named " + configKey + " to " + testUrl + + ". If this is incorrect, " + + "please set it manually using the quarkus.smallrye-graphql-client." + maybeWithQuotes(configKey) + + ".url property. Also note that" + + " this autowiring is only supported during tests."); + config.setUrl(testUrl); + } + } + } + } + + private String maybeWithQuotes(String key) { + if (key.contains(".")) { + return "\"" + key + "\""; + } else { + return key; + } } // translates a Quarkus `GraphQLClientConfig` configuration object to `GraphQLClientConfiguration` which is understood @@ -80,6 +113,13 @@ private GraphQLClientConfiguration toSmallRyeNativeConfiguration(GraphQLClientCo return transformed; } + private String getTestingServerUrl() { + Config config = ConfigProvider.getConfig(); + // the client extension doesn't have dependencies on neither the server extension nor quarkus-vertx-http, so guessing + // is somewhat limited + return "http://localhost:" + config.getOptionalValue("quarkus.http.test-port", int.class).orElse(8081) + "/graphql"; + } + public void nothing() { } } diff --git a/extensions/smallrye-graphql-client/runtime/src/main/java/io/quarkus/smallrye/graphql/client/runtime/GraphQLClientSupport.java b/extensions/smallrye-graphql-client/runtime/src/main/java/io/quarkus/smallrye/graphql/client/runtime/GraphQLClientSupport.java index 2055789e4973ff..e2438d9ee3d320 100644 --- a/extensions/smallrye-graphql-client/runtime/src/main/java/io/quarkus/smallrye/graphql/client/runtime/GraphQLClientSupport.java +++ b/extensions/smallrye-graphql-client/runtime/src/main/java/io/quarkus/smallrye/graphql/client/runtime/GraphQLClientSupport.java @@ -1,5 +1,6 @@ package io.quarkus.smallrye.graphql.client.runtime; +import java.util.List; import java.util.Map; /** @@ -14,6 +15,13 @@ public class GraphQLClientSupport { */ private Map shortNamesToQualifiedNamesMapping; + /** + * All config keys of clients found in the application. The reason for having this is that in TEST mode, + * if a config key is used but doesn't have any associated configuration, we can automatically generate a configuration + * containing a guess of the target URL. + */ + private List knownConfigKeys; + public Map getShortNamesToQualifiedNamesMapping() { return shortNamesToQualifiedNamesMapping; } @@ -21,4 +29,12 @@ public Map getShortNamesToQualifiedNamesMapping() { public void setShortNamesToQualifiedNamesMapping(Map shortNamesToQualifiedNamesMapping) { this.shortNamesToQualifiedNamesMapping = shortNamesToQualifiedNamesMapping; } + + public void setKnownConfigKeys(List knownConfigKeys) { + this.knownConfigKeys = knownConfigKeys; + } + + public List getKnownConfigKeys() { + return knownConfigKeys; + } } diff --git a/extensions/smallrye-graphql-client/runtime/src/main/java/io/quarkus/smallrye/graphql/client/runtime/SmallRyeGraphQLClientRecorder.java b/extensions/smallrye-graphql-client/runtime/src/main/java/io/quarkus/smallrye/graphql/client/runtime/SmallRyeGraphQLClientRecorder.java index 2204c616d1dac8..210a103da7a5d5 100644 --- a/extensions/smallrye-graphql-client/runtime/src/main/java/io/quarkus/smallrye/graphql/client/runtime/SmallRyeGraphQLClientRecorder.java +++ b/extensions/smallrye-graphql-client/runtime/src/main/java/io/quarkus/smallrye/graphql/client/runtime/SmallRyeGraphQLClientRecorder.java @@ -34,9 +34,11 @@ public void setTypesafeApiClasses(List apiClassNames) { configBean.addTypesafeClientApis(classes); } - public RuntimeValue clientSupport(Map shortNamesToQualifiedNames) { + public RuntimeValue clientSupport(Map shortNamesToQualifiedNames, + List knownConfigKeys) { GraphQLClientSupport support = new GraphQLClientSupport(); support.setShortNamesToQualifiedNamesMapping(shortNamesToQualifiedNames); + support.setKnownConfigKeys(knownConfigKeys); return new RuntimeValue<>(support); }