Skip to content

Commit

Permalink
Merge pull request quarkusio#36623 from jmartisk/graphql-client-oidc-…
Browse files Browse the repository at this point in the history
…optimize

Fix OIDC/GraphQL client initialization
  • Loading branch information
sberyozkin authored Oct 23, 2023
2 parents 7d4f4bd + 22563e4 commit 4fb9864
Show file tree
Hide file tree
Showing 5 changed files with 113 additions and 175 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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 {

Expand All @@ -37,7 +38,8 @@ void feature(BuildProducer<FeatureBuildItem> featureProducer) {
void initialize(BeanContainerBuildItem containerBuildItem,
OidcGraphQLClientIntegrationRecorder recorder,
OidcClientGraphQLConfig config,
BeanArchiveIndexBuildItem index) {
BeanArchiveIndexBuildItem index,
GraphQLClientConfigInitializedBuildItem configInitialized) {
Map<String, String> configKeysToOidcClients = new HashMap<>();
for (AnnotationInstance annotation : index.getIndex().getAnnotations(GRAPHQL_CLIENT_API)) {
ClassInfo clazz = annotation.target().asClass();
Expand Down
Original file line number Diff line number Diff line change
@@ -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 {
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -157,7 +154,7 @@ void setTypesafeApiClasses(BeanArchiveIndexBuildItem index,
*/
@BuildStep
@Record(RUNTIME_INIT)
void initializeClientSupport(BuildProducer<SyntheticBeanBuildItem> syntheticBeans,
GraphQLClientConfigInitializedBuildItem mergeClientConfigurations(BuildProducer<SyntheticBeanBuildItem> syntheticBeans,
SmallRyeGraphQLClientRecorder recorder,
GraphQLClientsConfig quarkusConfig,
BeanArchiveIndexBuildItem index) {
Expand All @@ -182,33 +179,12 @@ void initializeClientSupport(BuildProducer<SyntheticBeanBuildItem> syntheticBean
knownConfigKeys.add(configKey);
}

RuntimeValue<GraphQLClientSupport> 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
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -16,6 +23,8 @@
@Recorder
public class SmallRyeGraphQLClientRecorder {

private final Logger logger = Logger.getLogger(SmallRyeGraphQLClientRecorder.class);

public <T> Supplier<T> typesafeClientSupplier(Class<T> targetClassName) {
return () -> {
TypesafeGraphQLClientBuilder builder = TypesafeGraphQLClientBuilder.newBuilder();
Expand All @@ -36,22 +45,95 @@ public void setTypesafeApiClasses(List<String> apiClassNames) {
configBean.addTypesafeClientApis(classes);
}

public RuntimeValue<GraphQLClientSupport> clientSupport(Map<String, String> shortNamesToQualifiedNames,
List<String> 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<String, GraphQLClientConfig> 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<String, String> 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> 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<String, Object>(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";
}

}

0 comments on commit 4fb9864

Please sign in to comment.