Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Read client configuration directly from Config rather than via @ConfigRoot #21530

Merged
merged 1 commit into from
Nov 22, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@

public final class Constants {

public static final String MP_REST_SCOPE_FORMAT = "%s/mp-rest/scope";
public static final String QUARKUS_REST_SCOPE_FORMAT = "quarkus.rest-client.%s.scope";
public static final String QUARKUS_CONFIG_PREFIX = "quarkus.rest-client.";
public static final String MP_REST = "/mp-rest/";
public static final String MP_REST_SCOPE_FORMAT = "%s" + MP_REST + "scope";
public static final String QUARKUS_REST_SCOPE_FORMAT = QUARKUS_CONFIG_PREFIX + "%s.scope";

private Constants() {
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package io.quarkus.restclient.config;

import org.eclipse.microprofile.config.spi.Converter;
import org.eclipse.microprofile.rest.client.ext.QueryParamStyle;

public class QueryParamStyleConverter implements Converter<QueryParamStyle> {
@Override
public QueryParamStyle convert(String value) throws IllegalArgumentException, NullPointerException {
return QueryParamStyle.valueOf(value);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import java.util.Optional;

import org.eclipse.microprofile.config.Config;
import org.eclipse.microprofile.config.ConfigProvider;
import org.eclipse.microprofile.rest.client.ext.QueryParamStyle;

import io.quarkus.runtime.annotations.ConfigGroup;
Expand Down Expand Up @@ -159,4 +161,83 @@ public class RestClientConfig {
@ConfigItem
public Optional<Integer> maxRedirects;

public static RestClientConfig load(String configKey) {
final RestClientConfig instance = new RestClientConfig();

instance.url = getConfigValue(configKey, "url", String.class);
instance.uri = getConfigValue(configKey, "uri", String.class);
instance.scope = getConfigValue(configKey, "scope", String.class);
instance.providers = getConfigValue(configKey, "providers", String.class);
instance.connectTimeout = getConfigValue(configKey, "connect-timeout", Long.class);
instance.readTimeout = getConfigValue(configKey, "read-timeout", Long.class);
instance.followRedirects = getConfigValue(configKey, "follow-redirects", Boolean.class);
instance.proxyAddress = getConfigValue(configKey, "proxy-address", String.class);
instance.queryParamStyle = getConfigValue(configKey, "query-param-style", QueryParamStyle.class);
instance.trustStore = getConfigValue(configKey, "trust-store", String.class);
instance.trustStorePassword = getConfigValue(configKey, "trust-store-password", String.class);
instance.trustStoreType = getConfigValue(configKey, "trust-store-type", String.class);
instance.keyStore = getConfigValue(configKey, "key-store", String.class);
instance.keyStorePassword = getConfigValue(configKey, "key-store-password", String.class);
instance.keyStoreType = getConfigValue(configKey, "key-store-type", String.class);
instance.hostnameVerifier = getConfigValue(configKey, "hostname-verifier", String.class);
instance.connectionTTL = getConfigValue(configKey, "connection-ttl", Integer.class);
instance.connectionPoolSize = getConfigValue(configKey, "connection-pool-size", Integer.class);
instance.maxRedirects = getConfigValue(configKey, "max-redirects", Integer.class);

return instance;
}

public static RestClientConfig load(Class<?> interfaceClass) {
final RestClientConfig instance = new RestClientConfig();

instance.url = getConfigValue(interfaceClass, "url", String.class);
instance.uri = getConfigValue(interfaceClass, "uri", String.class);
instance.scope = getConfigValue(interfaceClass, "scope", String.class);
instance.providers = getConfigValue(interfaceClass, "providers", String.class);
instance.connectTimeout = getConfigValue(interfaceClass, "connect-timeout", Long.class);
instance.readTimeout = getConfigValue(interfaceClass, "read-timeout", Long.class);
instance.followRedirects = getConfigValue(interfaceClass, "follow-redirects", Boolean.class);
instance.proxyAddress = getConfigValue(interfaceClass, "proxy-address", String.class);
instance.queryParamStyle = getConfigValue(interfaceClass, "query-param-style", QueryParamStyle.class);
instance.trustStore = getConfigValue(interfaceClass, "trust-store", String.class);
instance.trustStorePassword = getConfigValue(interfaceClass, "trust-store-password", String.class);
instance.trustStoreType = getConfigValue(interfaceClass, "trust-store-type", String.class);
instance.keyStore = getConfigValue(interfaceClass, "key-store", String.class);
instance.keyStorePassword = getConfigValue(interfaceClass, "key-store-password", String.class);
instance.keyStoreType = getConfigValue(interfaceClass, "key-store-type", String.class);
instance.hostnameVerifier = getConfigValue(interfaceClass, "hostname-verifier", String.class);
instance.connectionTTL = getConfigValue(interfaceClass, "connection-ttl", Integer.class);
instance.connectionPoolSize = getConfigValue(interfaceClass, "connection-pool-size", Integer.class);
instance.maxRedirects = getConfigValue(interfaceClass, "max-redirects", Integer.class);

return instance;
}

private static <T> Optional<T> getConfigValue(String configKey, String fieldName, Class<T> type) {
final Config config = ConfigProvider.getConfig();
Optional<T> optional = config.getOptionalValue(composePropertyKey(configKey, fieldName), type);
if (optional.isEmpty()) { // try to find property with quoted configKey
optional = config.getOptionalValue(composePropertyKey('"' + configKey + '"', fieldName), type);
}
return optional;
}

private static <T> Optional<T> getConfigValue(Class<?> clientInterface, String fieldName, Class<T> type) {
final Config config = ConfigProvider.getConfig();
// first try interface full name
Optional<T> optional = config.getOptionalValue(composePropertyKey('"' + clientInterface.getName() + '"', fieldName),
type);
if (optional.isEmpty()) { // then interface simple name
optional = config.getOptionalValue(composePropertyKey(clientInterface.getSimpleName(), fieldName), type);
}
if (optional.isEmpty()) { // lastly quoted interface simple name
optional = config.getOptionalValue(composePropertyKey('"' + clientInterface.getSimpleName() + '"', fieldName),
type);
}
return optional;
}

private static String composePropertyKey(String key, String fieldName) {
return Constants.QUARKUS_CONFIG_PREFIX + key + "." + fieldName;
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
package io.quarkus.restclient.config;

import java.util.HashMap;
import java.util.Map;
import java.util.Optional;

import org.wildfly.common.annotation.NotNull;

import io.quarkus.runtime.annotations.ConfigItem;
import io.quarkus.runtime.annotations.ConfigPhase;
import io.quarkus.runtime.annotations.ConfigRoot;
Expand All @@ -17,8 +20,7 @@ public class RestClientsConfig {
* a class bearing that annotation, in which case it is possible to use the short name, as well as fully qualified
* name.
*/
@ConfigItem(name = ConfigItem.PARENT)
public Map<String, RestClientConfig> configs;
private final Map<String, RestClientConfig> configs = new HashMap<>();

/**
* By default, REST Client Reactive uses text/plain content type for String values
Expand Down Expand Up @@ -47,4 +49,23 @@ public class RestClientsConfig {

public RestClientLoggingConfig logging;

public RestClientConfig getClientConfig(String configKey) {
if (configKey == null) {
return RestClientConfig.EMPTY;
}
return configs.computeIfAbsent(configKey, RestClientConfig::load);
}

public RestClientConfig getClientConfig(@NotNull Class<?> clientInterface) {
return configs.computeIfAbsent(clientInterface.getName(), name -> RestClientConfig.load(clientInterface));
}

public void putClientConfig(String configKey, RestClientConfig clientConfig) {
configs.put(configKey, clientConfig);
}

public void putClientConfig(Class<?> clientInterface, RestClientConfig clientConfig) {
configs.put(clientInterface.getName(), clientConfig);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
io.quarkus.restclient.config.QueryParamStyleConverter
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package io.quarkus.restclient.config;

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

import java.io.IOException;
import java.net.URL;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Optional;

import org.eclipse.microprofile.config.Config;
import org.eclipse.microprofile.config.ConfigProvider;
import org.eclipse.microprofile.config.spi.ConfigBuilder;
import org.eclipse.microprofile.config.spi.ConfigProviderResolver;
import org.eclipse.microprofile.rest.client.ext.QueryParamStyle;
import org.junit.jupiter.api.Test;

import io.smallrye.config.ConfigLogging;
import io.smallrye.config.PropertiesConfigSource;

public class RestClientConfigTest {

@Test
public void testLoadRestClientConfig() throws IOException {
setupMPConfig();

Config config = ConfigProvider.getConfig();
Optional<String> optionalValue = config.getOptionalValue("quarkus.rest-client.test-client.url", String.class);
assertThat(optionalValue).isPresent();

RestClientConfig configForKey = RestClientConfig.load("test-client");
verifyConfig(configForKey);
RestClientConfig configForClassName = RestClientConfig.load(RestClientConfigTest.class);
verifyConfig(configForClassName);
}

private void verifyConfig(RestClientConfig config) {
assertThat(config.url).isPresent();
assertThat(config.url.get()).isEqualTo("http://localhost:8080");
assertThat(config.uri).isPresent();
assertThat(config.uri.get()).isEqualTo("http://localhost:8081");
assertThat(config.scope).isPresent();
assertThat(config.scope.get()).isEqualTo("Singleton");
assertThat(config.providers).isPresent();
assertThat(config.providers.get()).isEqualTo("io.quarkus.restclient.configuration.MyResponseFilter");
assertThat(config.connectTimeout).isPresent();
assertThat(config.connectTimeout.get()).isEqualTo(5000);
assertThat(config.readTimeout).isPresent();
assertThat(config.readTimeout.get()).isEqualTo(6000);
assertThat(config.followRedirects).isPresent();
assertThat(config.followRedirects.get()).isEqualTo(true);
assertThat(config.proxyAddress).isPresent();
assertThat(config.proxyAddress.get()).isEqualTo("localhost:8080");
assertThat(config.queryParamStyle).isPresent();
assertThat(config.queryParamStyle.get()).isEqualTo(QueryParamStyle.COMMA_SEPARATED);
assertThat(config.hostnameVerifier).isPresent();
assertThat(config.hostnameVerifier.get()).isEqualTo("io.quarkus.restclient.configuration.MyHostnameVerifier");
assertThat(config.connectionTTL).isPresent();
assertThat(config.connectionTTL.get()).isEqualTo(30000);
assertThat(config.connectionPoolSize).isPresent();
assertThat(config.connectionPoolSize.get()).isEqualTo(10);
}

private static void setupMPConfig() throws IOException {
ConfigProviderResolver resolver = ConfigProviderResolver.instance();
ConfigBuilder configBuilder = resolver.getBuilder();
URL propertyFile = RestClientConfigTest.class.getClassLoader().getResource("application.properties");
assertThat(propertyFile).isNotNull();
configBuilder.withSources(new PropertiesConfigSource(propertyFile));
resolver.registerConfig(configBuilder.build(), getContextClassLoader());
}

static ClassLoader getContextClassLoader() {
if (System.getSecurityManager() == null) {
return Thread.currentThread().getContextClassLoader();
} else {
return AccessController.doPrivileged((PrivilegedAction<ClassLoader>) () -> {
ClassLoader tccl = null;
try {
tccl = Thread.currentThread().getContextClassLoader();
} catch (SecurityException ex) {
ConfigLogging.log.failedToRetrieveClassloader(ex);
}
return tccl;
});
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
quarkus.rest-client.test-client.url=http://localhost:8080
quarkus.rest-client.test-client.uri=http://localhost:8081
quarkus.rest-client.test-client.scope=Singleton
quarkus.rest-client.test-client.providers=io.quarkus.restclient.configuration.MyResponseFilter
quarkus.rest-client.test-client.connect-timeout=5000
quarkus.rest-client.test-client.read-timeout=6000
quarkus.rest-client.test-client.follow-redirects=true
quarkus.rest-client.test-client.proxy-address=localhost:8080
quarkus.rest-client.test-client.query-param-style=COMMA_SEPARATED
quarkus.rest-client.test-client.hostname-verifier=io.quarkus.restclient.configuration.MyHostnameVerifier
quarkus.rest-client.test-client.connection-ttl=30000
quarkus.rest-client.test-client.connection-pool-size=10

quarkus.rest-client."io.quarkus.restclient.config.RestClientConfigTest".url=http://localhost:8080
quarkus.rest-client."RestClientConfigTest".uri=http://localhost:8081
quarkus.rest-client.RestClientConfigTest.scope=Singleton
quarkus.rest-client."io.quarkus.restclient.config.RestClientConfigTest".providers=io.quarkus.restclient.configuration.MyResponseFilter
quarkus.rest-client."io.quarkus.restclient.config.RestClientConfigTest".connect-timeout=5000
quarkus.rest-client."io.quarkus.restclient.config.RestClientConfigTest".read-timeout=6000
quarkus.rest-client."io.quarkus.restclient.config.RestClientConfigTest".follow-redirects=true
quarkus.rest-client."io.quarkus.restclient.config.RestClientConfigTest".proxy-address=localhost:8080
quarkus.rest-client."io.quarkus.restclient.config.RestClientConfigTest".query-param-style=COMMA_SEPARATED
quarkus.rest-client."io.quarkus.restclient.config.RestClientConfigTest".hostname-verifier=io.quarkus.restclient.configuration.MyHostnameVerifier
quarkus.rest-client."io.quarkus.restclient.config.RestClientConfigTest".connection-ttl=30000
quarkus.rest-client."io.quarkus.restclient.config.RestClientConfigTest".connection-pool-size=10
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,6 @@ public class QuarkusConfigurationTest {
@Inject
RestClientsConfig configRoot;

@Test
public void shouldConnect() {
// TODO move to separate test
//Assertions.assertEquals("Hello", client.echo("Hello"));
}

@Test
void shouldHaveSingletonScope() {
BeanManager beanManager = Arc.container().beanManager();
Expand All @@ -51,13 +45,11 @@ void shouldHaveSingletonScope() {

@Test
void configurationsShouldBeLoaded() {
assertThat(configRoot.configs.size()).isEqualTo(5);

verifyClientConfig(configRoot.configs.get("echo-client"), true);
verifyClientConfig(configRoot.configs.get("io.quarkus.restclient.configuration.EchoClient"), true);
verifyClientConfig(configRoot.configs.get("EchoClient"), true);
verifyClientConfig(configRoot.configs.get("mp-client"), false); // non-standard properties cannot be set via MP style config
verifyClientConfig(configRoot.configs.get("a.b.c.Client"), false);
verifyClientConfig(configRoot.getClientConfig("echo-client"), true);
verifyClientConfig(configRoot.getClientConfig("io.quarkus.restclient.configuration.EchoClient"), true);
verifyClientConfig(configRoot.getClientConfig("EchoClient"), true);
verifyClientConfig(configRoot.getClientConfig("mp-client"), false); // non-standard properties cannot be set via MP style config
verifyClientConfig(configRoot.getClientConfig("a.b.c.Client"), false);
}

void verifyClientConfig(RestClientConfig clientConfig, boolean verifyNonStandardProperties) {
Expand Down
Loading