From e90d560921ef499b715df37721752cc8d8d8eb99 Mon Sep 17 00:00:00 2001 From: Matheus Cruz Date: Tue, 24 Sep 2024 19:37:23 -0300 Subject: [PATCH] Add client query parameter to redis connection string --- .../client/VertxRedisClientFactory.java | 38 ++++++++++++++++- .../client/config/RedisClientConfig.java | 7 ++++ .../quarkus/redis/it/CustomCodecResource.java | 2 +- .../quarkus/redis/it/RedisClientResource.java | 42 +++++++++++++++++++ .../src/main/resources/application.properties | 9 +++- .../io/quarkus/redis/it/QuarkusRedisTest.java | 10 +++++ 6 files changed, 104 insertions(+), 4 deletions(-) create mode 100644 integration-tests/redis-client/src/main/java/io/quarkus/redis/it/RedisClientResource.java diff --git a/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/runtime/client/VertxRedisClientFactory.java b/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/runtime/client/VertxRedisClientFactory.java index 524c83e458ac63..99fd237e243c79 100644 --- a/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/runtime/client/VertxRedisClientFactory.java +++ b/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/runtime/client/VertxRedisClientFactory.java @@ -8,10 +8,12 @@ import static io.quarkus.vertx.core.runtime.SSLConfigHelper.configurePfxTrustOptions; import java.net.URI; +import java.net.URISyntaxException; import java.util.ArrayList; import java.util.List; import java.util.Optional; import java.util.Set; +import java.util.logging.Logger; import org.jboss.logging.Logger; @@ -28,6 +30,7 @@ import io.quarkus.tls.TlsConfigurationRegistry; import io.quarkus.tls.runtime.config.TlsConfigUtils; import io.smallrye.common.annotation.Identifier; +import io.smallrye.config.common.utils.StringUtil; import io.vertx.core.Vertx; import io.vertx.core.net.NetClientOptions; import io.vertx.core.net.ProxyOptions; @@ -40,6 +43,7 @@ */ public class VertxRedisClientFactory { + private static final Logger LOGGER = Logger.getLogger(VertxRedisClientFactory.class.getName()); public static final String DEFAULT_CLIENT = ""; private static final Logger LOGGER = Logger.getLogger(VertxRedisClientFactory.class); @@ -51,18 +55,22 @@ private VertxRedisClientFactory() { public static Redis create(String name, Vertx vertx, RedisClientConfig config, TlsConfigurationRegistry tlsRegistry) { RedisOptions options = new RedisOptions(); + String clientName = config.clientName().orElse(name); + List hosts = new ArrayList<>(); if (config.hosts().isPresent()) { hosts.addAll(config.hosts().get()); for (URI uri : config.hosts().get()) { - options.addConnectionString(uri.toString().trim()); + String connectionString = generateConnectionString(clientName, uri); + options.addConnectionString(connectionString); } } else if (config.hostsProviderName().isPresent()) { RedisHostsProvider hostsProvider = findProvider(config.hostsProviderName().get()); Set computedHosts = hostsProvider.getHosts(); hosts.addAll(computedHosts); for (URI uri : computedHosts) { - options.addConnectionString(uri.toString()); + String connectionString = generateConnectionString(clientName, uri); + options.addConnectionString(connectionString); } } else { throw new ConfigurationException("Redis host not configured - you must either configure 'quarkus.redis.hosts` or" + @@ -107,6 +115,32 @@ public static Redis create(String name, Vertx vertx, RedisClientConfig config, T return Redis.createClient(vertx, options); } + public static String generateConnectionString(String clientName, URI uri) { + var sanitizedClientName = StringUtil.replaceNonAlphanumericByUnderscores(clientName) + .replaceAll("_", "-") + .replaceAll("^-+", "") + .replaceAll("-+$", ""); + URI withClientName = addClientParameter(sanitizedClientName, uri); + return withClientName.toString().trim(); + } + + private static URI addClientParameter(String clientName, URI uri) { + try { + String query = uri.getQuery() == null ? "client=%s".formatted(clientName) + : uri.getQuery().formatted("&client=%s", clientName); + return new URI( + uri.getScheme(), + uri.getAuthority(), + uri.getPath(), + query, + uri.getFragment()); + } catch (URISyntaxException e) { + LOGGER.warning("Was not possible to apply the client parameter ('%s') to the redis connection string".formatted( + clientName)); + return uri; + } + } + private static void customize(String name, RedisOptions options) { if (Arc.container() != null) { List> customizers = Arc.container().listAll(RedisOptionsCustomizer.class); diff --git a/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/runtime/client/config/RedisClientConfig.java b/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/runtime/client/config/RedisClientConfig.java index 962c368eb27d00..78a4bab669d1b3 100644 --- a/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/runtime/client/config/RedisClientConfig.java +++ b/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/runtime/client/config/RedisClientConfig.java @@ -169,6 +169,12 @@ public interface RedisClientConfig { TlsConfig tls(); /** + * The client name used to identify the connection. + *

+ * If not set it will try to extract the value from the redis client name. + */ + Optional clientName(); + * The name of the TLS configuration to use. *

* If a name is configured, it uses the configuration from {@code quarkus.tls..*} @@ -203,6 +209,7 @@ default String toDebugString() { ", hashSlotCacheTtl=" + hashSlotCacheTtl() + ", tcp=" + tcp() + ", tls=" + tls() + + ", clientName=" + clientName() + '}'; } diff --git a/integration-tests/redis-client/src/main/java/io/quarkus/redis/it/CustomCodecResource.java b/integration-tests/redis-client/src/main/java/io/quarkus/redis/it/CustomCodecResource.java index 119817d0d0775e..9a9e13c4ab401c 100644 --- a/integration-tests/redis-client/src/main/java/io/quarkus/redis/it/CustomCodecResource.java +++ b/integration-tests/redis-client/src/main/java/io/quarkus/redis/it/CustomCodecResource.java @@ -16,7 +16,7 @@ public class CustomCodecResource { private final ValueCommands values; public CustomCodecResource(RedisDataSource ds) { - values = ds.value(Person.class); + this.values = ds.value(Person.class); } // synchronous diff --git a/integration-tests/redis-client/src/main/java/io/quarkus/redis/it/RedisClientResource.java b/integration-tests/redis-client/src/main/java/io/quarkus/redis/it/RedisClientResource.java new file mode 100644 index 00000000000000..3ba73d6b5b7060 --- /dev/null +++ b/integration-tests/redis-client/src/main/java/io/quarkus/redis/it/RedisClientResource.java @@ -0,0 +1,42 @@ +package io.quarkus.redis.it; + +import static jakarta.ws.rs.core.MediaType.TEXT_PLAIN; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; + +import io.quarkus.redis.client.RedisClientName; +import io.quarkus.redis.datasource.RedisDataSource; +import io.vertx.mutiny.redis.client.Command; + +@Path("/quarkus-redis/clients") +@ApplicationScoped +public class RedisClientResource { + + private final RedisDataSource ds; + private final RedisDataSource client11; + + @Inject + public RedisClientResource(@RedisClientName("quarkiverse") RedisDataSource ds, + @RedisClientName("client11") RedisDataSource client11) { + this.ds = ds; + this.client11 = client11; + } + + @GET + @Produces(TEXT_PLAIN) + public String clients() { + // ignore response + return ds.execute(Command.CLIENT, "GETNAME").toString(); + } + + @GET + @Path("/11") + @Produces(TEXT_PLAIN) + public String client11() { + return client11.execute(Command.CLIENT, "GETNAME").toString(); + } +} diff --git a/integration-tests/redis-client/src/main/resources/application.properties b/integration-tests/redis-client/src/main/resources/application.properties index 6bb245e908d5ed..bb8893013d5091 100644 --- a/integration-tests/redis-client/src/main/resources/application.properties +++ b/integration-tests/redis-client/src/main/resources/application.properties @@ -8,4 +8,11 @@ quarkus.redis.instance-client.hosts=redis://localhost:6379/5 # use DB 3 quarkus.redis.provided-hosts.hosts-provider-name=test-hosts-provider -quarkus.redis.load-script=starwars.redis \ No newline at end of file +quarkus.redis.load-script=starwars.redis + +# client-name +quarkus.redis.quarkiverse.client-name=quarkus +quarkus.redis.quarkiverse.hosts=redis://localhost:6379/10 + +quarkus.redis.client11.client-name=%#@#% !@#$%¨&*(special +quarkus.redis.client11.hosts=redis://localhost:6379/11 diff --git a/integration-tests/redis-client/src/test/java/io/quarkus/redis/it/QuarkusRedisTest.java b/integration-tests/redis-client/src/test/java/io/quarkus/redis/it/QuarkusRedisTest.java index c2dd33be49cefc..b7d771f41ae8ee 100644 --- a/integration-tests/redis-client/src/test/java/io/quarkus/redis/it/QuarkusRedisTest.java +++ b/integration-tests/redis-client/src/test/java/io/quarkus/redis/it/QuarkusRedisTest.java @@ -114,4 +114,14 @@ public void testCustomCodec() { Assertions.assertEquals(person.firstName, "bob"); Assertions.assertEquals(person.lastName, "MORANE"); } + + @Test + public void testClientNames() { + RestAssured.given() + .when() + .get("/quarkus-redis/clients/11") + .then() + .statusCode(Matchers.is(200)) + .body(Matchers.is("special")); + } }