From 22563e498a180732f46354e742c57c6ce1f0a4c4 Mon Sep 17 00:00:00 2001 From: Jan Martiska Date: Mon, 23 Oct 2023 09:48:51 +0200 Subject: [PATCH] Make sure OIDC/GraphQL client integration runs after client config is initialized, get rid of config merger bean --- ...OidcGraphQLClientIntegrationProcessor.java | 4 +- ...aphQLClientConfigInitializedBuildItem.java | 9 ++ .../SmallRyeGraphQLClientProcessor.java | 36 +---- .../GraphQLClientConfigurationMergerBean.java | 131 ------------------ .../SmallRyeGraphQLClientRecorder.java | 108 +++++++++++++-- 5 files changed, 113 insertions(+), 175 deletions(-) create mode 100644 extensions/smallrye-graphql-client/deployment/src/main/java/io/quarkus/smallrye/graphql/client/deployment/GraphQLClientConfigInitializedBuildItem.java delete mode 100644 extensions/smallrye-graphql-client/runtime/src/main/java/io/quarkus/smallrye/graphql/client/runtime/GraphQLClientConfigurationMergerBean.java diff --git a/extensions/oidc-client-graphql/deployment/src/main/java/io/quarkus/oidc/client/graphql/OidcGraphQLClientIntegrationProcessor.java b/extensions/oidc-client-graphql/deployment/src/main/java/io/quarkus/oidc/client/graphql/OidcGraphQLClientIntegrationProcessor.java index 5cc32a72e59c1..8defc7d6ba3d2 100644 --- a/extensions/oidc-client-graphql/deployment/src/main/java/io/quarkus/oidc/client/graphql/OidcGraphQLClientIntegrationProcessor.java +++ b/extensions/oidc-client-graphql/deployment/src/main/java/io/quarkus/oidc/client/graphql/OidcGraphQLClientIntegrationProcessor.java @@ -19,6 +19,7 @@ import io.quarkus.deployment.builditem.FeatureBuildItem; import io.quarkus.oidc.client.graphql.runtime.OidcClientGraphQLConfig; import io.quarkus.oidc.client.graphql.runtime.OidcGraphQLClientIntegrationRecorder; +import io.quarkus.smallrye.graphql.client.deployment.GraphQLClientConfigInitializedBuildItem; public class OidcGraphQLClientIntegrationProcessor { @@ -37,7 +38,8 @@ void feature(BuildProducer featureProducer) { void initialize(BeanContainerBuildItem containerBuildItem, OidcGraphQLClientIntegrationRecorder recorder, OidcClientGraphQLConfig config, - BeanArchiveIndexBuildItem index) { + BeanArchiveIndexBuildItem index, + GraphQLClientConfigInitializedBuildItem configInitialized) { Map configKeysToOidcClients = new HashMap<>(); for (AnnotationInstance annotation : index.getIndex().getAnnotations(GRAPHQL_CLIENT_API)) { ClassInfo clazz = annotation.target().asClass(); diff --git a/extensions/smallrye-graphql-client/deployment/src/main/java/io/quarkus/smallrye/graphql/client/deployment/GraphQLClientConfigInitializedBuildItem.java b/extensions/smallrye-graphql-client/deployment/src/main/java/io/quarkus/smallrye/graphql/client/deployment/GraphQLClientConfigInitializedBuildItem.java new file mode 100644 index 0000000000000..a0dd8434a0b12 --- /dev/null +++ b/extensions/smallrye-graphql-client/deployment/src/main/java/io/quarkus/smallrye/graphql/client/deployment/GraphQLClientConfigInitializedBuildItem.java @@ -0,0 +1,9 @@ +package io.quarkus.smallrye.graphql.client.deployment; + +import io.quarkus.builder.item.SimpleBuildItem; + +/** + * This marks that the SmallRye GraphQL Client configurations have been processed and initialized. + */ +public final class GraphQLClientConfigInitializedBuildItem extends SimpleBuildItem { +} 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 7fec562a3f912..f67f6f98b4a63 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 @@ -9,7 +9,6 @@ import java.util.Map; import jakarta.enterprise.context.ApplicationScoped; -import jakarta.inject.Singleton; import org.jboss.jandex.AnnotationInstance; import org.jboss.jandex.AnnotationValue; @@ -33,8 +32,6 @@ import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem; import io.quarkus.deployment.builditem.nativeimage.ReflectiveHierarchyBuildItem; import io.quarkus.deployment.builditem.nativeimage.ServiceProviderBuildItem; -import io.quarkus.runtime.RuntimeValue; -import io.quarkus.smallrye.graphql.client.runtime.GraphQLClientConfigurationMergerBean; import io.quarkus.smallrye.graphql.client.runtime.GraphQLClientSupport; import io.quarkus.smallrye.graphql.client.runtime.GraphQLClientsConfig; import io.quarkus.smallrye.graphql.client.runtime.SmallRyeGraphQLClientRecorder; @@ -157,7 +154,7 @@ void setTypesafeApiClasses(BeanArchiveIndexBuildItem index, */ @BuildStep @Record(RUNTIME_INIT) - void initializeClientSupport(BuildProducer syntheticBeans, + GraphQLClientConfigInitializedBuildItem mergeClientConfigurations(BuildProducer syntheticBeans, SmallRyeGraphQLClientRecorder recorder, GraphQLClientsConfig quarkusConfig, BeanArchiveIndexBuildItem index) { @@ -182,33 +179,12 @@ void initializeClientSupport(BuildProducer syntheticBean knownConfigKeys.add(configKey); } - RuntimeValue support = recorder.clientSupport(shortNamesToQualifiedNames, - knownConfigKeys); - - DotName supportClassName = DotName.createSimple(GraphQLClientSupport.class.getName()); - SyntheticBeanBuildItem bean = SyntheticBeanBuildItem - .configure(supportClassName) - .addType(supportClassName) - .scope(Singleton.class) - .runtimeValue(support) - .setRuntimeInit() - .unremovable() - .done(); - syntheticBeans.produce(bean); - } - - @BuildStep - AdditionalBeanBuildItem configurationMergerBean() { - return AdditionalBeanBuildItem.unremovableOf(GraphQLClientConfigurationMergerBean.class); - } + GraphQLClientSupport support = new GraphQLClientSupport(); + support.setShortNamesToQualifiedNamesMapping(shortNamesToQualifiedNames); + support.setKnownConfigKeys(knownConfigKeys); - // FIXME: this seems unnecessary, but is needed to make sure that the GraphQLClientConfigurationMergerBean - // gets initialized, can this be done differently? - @BuildStep - @Record(RUNTIME_INIT) - void initializeConfigMergerBean(BeanContainerBuildItem containerBuildItem, - SmallRyeGraphQLClientRecorder recorder) { - recorder.initializeConfigurationMergerBean(); + recorder.mergeClientConfigurations(support, quarkusConfig); + return new GraphQLClientConfigInitializedBuildItem(); } @BuildStep 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 deleted file mode 100644 index ac23a5b57b453..0000000000000 --- a/extensions/smallrye-graphql-client/runtime/src/main/java/io/quarkus/smallrye/graphql/client/runtime/GraphQLClientConfigurationMergerBean.java +++ /dev/null @@ -1,131 +0,0 @@ -package io.quarkus.smallrye.graphql.client.runtime; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Map; -import java.util.Optional; - -import jakarta.annotation.PostConstruct; -import jakarta.inject.Inject; -import jakarta.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; - -/** - * On startup, this beans takes Quarkus-specific configuration of GraphQL clients (quarkus.* properties) - * and merges this configuration with the configuration parsed by SmallRye GraphQL itself (CLIENT/mp-graphql/* properties) - * - * The resulting merged configuration resides in `io.smallrye.graphql.client.GraphQLClientsConfiguration` - * - * Quarkus configuration overrides SmallRye configuration where applicable. - */ -@Singleton -public class GraphQLClientConfigurationMergerBean { - - private final Logger logger = Logger.getLogger(GraphQLClientConfigurationMergerBean.class); - - GraphQLClientsConfiguration upstreamConfigs; - - @Inject - GraphQLClientsConfig quarkusConfiguration; - - @Inject - GraphQLClientSupport support; - - @PostConstruct - void enhanceGraphQLConfiguration() { - upstreamConfigs = GraphQLClientsConfiguration.getInstance(); - for (Map.Entry client : quarkusConfiguration.clients.entrySet()) { - // the raw config key provided in the config, this might be a short class name, - // so translate that into the fully qualified name if applicable - String rawConfigKey = client.getKey(); - Map shortNamesToQualifiedNamesMapping = support.getShortNamesToQualifiedNamesMapping(); - String configKey = shortNamesToQualifiedNamesMapping != null && - shortNamesToQualifiedNamesMapping.containsKey(rawConfigKey) - ? shortNamesToQualifiedNamesMapping.get(rawConfigKey) - : rawConfigKey; - - GraphQLClientConfig quarkusConfig = client.getValue(); - // if SmallRye configuration does not contain this client, simply use it - GraphQLClientConfiguration upstreamConfig = upstreamConfigs.getClient(configKey); - if (upstreamConfig == null) { - GraphQLClientConfiguration transformed = toSmallRyeNativeConfiguration(quarkusConfig); - upstreamConfigs.addClient(configKey, transformed); - } else { - // if SmallRye configuration already contains this client, enhance it with the Quarkus configuration - upstreamConfig.merge(toSmallRyeNativeConfiguration(quarkusConfig)); - } - } - - // 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 - // by SmallRye GraphQL - private GraphQLClientConfiguration toSmallRyeNativeConfiguration(GraphQLClientConfig quarkusConfig) { - GraphQLClientConfiguration transformed = new GraphQLClientConfiguration(); - transformed.setHeaders(quarkusConfig.headers); - transformed.setInitPayload(Optional.ofNullable(quarkusConfig.initPayload) - .map(m -> new HashMap(m)).orElse(null)); - quarkusConfig.url.ifPresent(transformed::setUrl); - transformed.setWebsocketSubprotocols(quarkusConfig.subprotocols.orElse(new ArrayList<>())); - quarkusConfig.keyStore.ifPresent(transformed::setKeyStore); - quarkusConfig.keyStoreType.ifPresent(transformed::setKeyStoreType); - quarkusConfig.keyStorePassword.ifPresent(transformed::setKeyStorePassword); - quarkusConfig.trustStore.ifPresent(transformed::setTrustStore); - quarkusConfig.trustStoreType.ifPresent(transformed::setTrustStoreType); - quarkusConfig.trustStorePassword.ifPresent(transformed::setTrustStorePassword); - quarkusConfig.proxyHost.ifPresent(transformed::setProxyHost); - quarkusConfig.proxyPort.ifPresent(transformed::setProxyPort); - quarkusConfig.proxyUsername.ifPresent(transformed::setProxyUsername); - quarkusConfig.proxyPassword.ifPresent(transformed::setProxyPassword); - quarkusConfig.maxRedirects.ifPresent(transformed::setMaxRedirects); - quarkusConfig.executeSingleResultOperationsOverWebsocket - .ifPresent(transformed::setExecuteSingleOperationsOverWebsocket); - quarkusConfig.websocketInitializationTimeout.ifPresent(transformed::setWebsocketInitializationTimeout); - quarkusConfig.allowUnexpectedResponseFields.ifPresent(transformed::setAllowUnexpectedResponseFields); - transformed.setDynamicHeaders(new HashMap<>()); - 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/SmallRyeGraphQLClientRecorder.java b/extensions/smallrye-graphql-client/runtime/src/main/java/io/quarkus/smallrye/graphql/client/runtime/SmallRyeGraphQLClientRecorder.java index d06dfbda954c1..15557e6a41195 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 @@ -1,13 +1,20 @@ package io.quarkus.smallrye.graphql.client.runtime; +import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.function.Supplier; import java.util.stream.Collectors; -import io.quarkus.arc.Arc; -import io.quarkus.runtime.RuntimeValue; +import org.eclipse.microprofile.config.Config; +import org.eclipse.microprofile.config.ConfigProvider; +import org.jboss.logging.Logger; + +import io.quarkus.runtime.LaunchMode; import io.quarkus.runtime.annotations.Recorder; +import io.smallrye.graphql.client.impl.GraphQLClientConfiguration; import io.smallrye.graphql.client.impl.GraphQLClientsConfiguration; import io.smallrye.graphql.client.typesafe.api.TypesafeGraphQLClientBuilder; import io.smallrye.graphql.client.vertx.VertxManager; @@ -16,6 +23,8 @@ @Recorder public class SmallRyeGraphQLClientRecorder { + private final Logger logger = Logger.getLogger(SmallRyeGraphQLClientRecorder.class); + public Supplier typesafeClientSupplier(Class targetClassName) { return () -> { TypesafeGraphQLClientBuilder builder = TypesafeGraphQLClientBuilder.newBuilder(); @@ -36,22 +45,95 @@ public void setTypesafeApiClasses(List apiClassNames) { configBean.addTypesafeClientApis(classes); } - public RuntimeValue clientSupport(Map shortNamesToQualifiedNames, - List knownConfigKeys) { - GraphQLClientSupport support = new GraphQLClientSupport(); - support.setShortNamesToQualifiedNamesMapping(shortNamesToQualifiedNames); - support.setKnownConfigKeys(knownConfigKeys); - return new RuntimeValue<>(support); - } + public void mergeClientConfigurations(GraphQLClientSupport support, GraphQLClientsConfig quarkusConfiguration) { + GraphQLClientsConfiguration upstreamConfigs = GraphQLClientsConfiguration.getInstance(); + for (Map.Entry client : quarkusConfiguration.clients.entrySet()) { + // the raw config key provided in the config, this might be a short class name, + // so translate that into the fully qualified name if applicable + String rawConfigKey = client.getKey(); + Map shortNamesToQualifiedNamesMapping = support.getShortNamesToQualifiedNamesMapping(); + String configKey = shortNamesToQualifiedNamesMapping != null && + shortNamesToQualifiedNamesMapping.containsKey(rawConfigKey) + ? shortNamesToQualifiedNamesMapping.get(rawConfigKey) + : rawConfigKey; - public void initializeConfigurationMergerBean() { - GraphQLClientConfigurationMergerBean merger = Arc.container() - .instance(GraphQLClientConfigurationMergerBean.class).get(); - merger.nothing(); + GraphQLClientConfig quarkusConfig = client.getValue(); + // if SmallRye configuration does not contain this client, simply use it + GraphQLClientConfiguration upstreamConfig = upstreamConfigs.getClient(configKey); + if (upstreamConfig == null) { + GraphQLClientConfiguration transformed = toSmallRyeNativeConfiguration(quarkusConfig); + upstreamConfigs.addClient(configKey, transformed); + } else { + // if SmallRye configuration already contains this client, enhance it with the Quarkus configuration + upstreamConfig.merge(toSmallRyeNativeConfiguration(quarkusConfig)); + } + } + + // 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); + } + } + } } public void setGlobalVertxInstance(Supplier vertx) { VertxManager.setFromGlobal(vertx.get()); } + private String maybeWithQuotes(String key) { + if (key.contains(".")) { + return "\"" + key + "\""; + } else { + return key; + } + } + + // translates a Quarkus `GraphQLClientConfig` configuration object to `GraphQLClientConfiguration` which is understood + // by SmallRye GraphQL + private GraphQLClientConfiguration toSmallRyeNativeConfiguration(GraphQLClientConfig quarkusConfig) { + GraphQLClientConfiguration transformed = new GraphQLClientConfiguration(); + transformed.setHeaders(quarkusConfig.headers); + transformed.setInitPayload(Optional.ofNullable(quarkusConfig.initPayload) + .map(m -> new HashMap(m)).orElse(null)); + quarkusConfig.url.ifPresent(transformed::setUrl); + transformed.setWebsocketSubprotocols(quarkusConfig.subprotocols.orElse(new ArrayList<>())); + quarkusConfig.keyStore.ifPresent(transformed::setKeyStore); + quarkusConfig.keyStoreType.ifPresent(transformed::setKeyStoreType); + quarkusConfig.keyStorePassword.ifPresent(transformed::setKeyStorePassword); + quarkusConfig.trustStore.ifPresent(transformed::setTrustStore); + quarkusConfig.trustStoreType.ifPresent(transformed::setTrustStoreType); + quarkusConfig.trustStorePassword.ifPresent(transformed::setTrustStorePassword); + quarkusConfig.proxyHost.ifPresent(transformed::setProxyHost); + quarkusConfig.proxyPort.ifPresent(transformed::setProxyPort); + quarkusConfig.proxyUsername.ifPresent(transformed::setProxyUsername); + quarkusConfig.proxyPassword.ifPresent(transformed::setProxyPassword); + quarkusConfig.maxRedirects.ifPresent(transformed::setMaxRedirects); + quarkusConfig.executeSingleResultOperationsOverWebsocket + .ifPresent(transformed::setExecuteSingleOperationsOverWebsocket); + quarkusConfig.websocketInitializationTimeout.ifPresent(transformed::setWebsocketInitializationTimeout); + quarkusConfig.allowUnexpectedResponseFields.ifPresent(transformed::setAllowUnexpectedResponseFields); + transformed.setDynamicHeaders(new HashMap<>()); + 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"; + } + }