Skip to content

Commit

Permalink
Add CLIENT ID and CLIENT GETNAME commands.
Browse files Browse the repository at this point in the history
Signed-off-by: Yury-Fridlyand <[email protected]>
  • Loading branch information
Yury-Fridlyand committed Feb 20, 2024
1 parent 2b9f9f7 commit c31877e
Show file tree
Hide file tree
Showing 11 changed files with 319 additions and 12 deletions.
19 changes: 16 additions & 3 deletions java/client/src/main/java/glide/api/BaseClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
import static glide.ffi.resolvers.SocketListenerResolver.getSocket;
import static glide.utils.ArrayTransformUtils.castArray;
import static glide.utils.ArrayTransformUtils.convertMapToArgArray;
import static redis_request.RedisRequestOuterClass.RequestType.ClientGetName;
import static redis_request.RedisRequestOuterClass.RequestType.ClientId;
import static redis_request.RedisRequestOuterClass.RequestType.Decr;
import static redis_request.RedisRequestOuterClass.RequestType.DecrBy;
import static redis_request.RedisRequestOuterClass.RequestType.GetString;
Expand All @@ -22,7 +24,7 @@
import static redis_request.RedisRequestOuterClass.RequestType.SRem;
import static redis_request.RedisRequestOuterClass.RequestType.SetString;

import glide.api.commands.ConnectionManagementCommands;
import glide.api.commands.ConnectionManagementBaseCommands;
import glide.api.commands.HashCommands;
import glide.api.commands.SetCommands;
import glide.api.commands.StringCommands;
Expand Down Expand Up @@ -53,10 +55,11 @@
@AllArgsConstructor
public abstract class BaseClient
implements AutoCloseable,
ConnectionManagementCommands,
ConnectionManagementBaseCommands,
StringCommands,
HashCommands,
SetCommands {

/** Redis simple string response with "OK" */
public static final String OK = ConstantResponse.OK.toString();

Expand Down Expand Up @@ -188,7 +191,7 @@ protected Object[] handleArrayOrNullResponse(Response response) throws RedisExce
/**
* @param response A Protobuf response
* @return A map of <code>String</code> to <code>V</code>
* @param <V> Value type could be even map too
* @param <V> Value type, could be even map too
*/
@SuppressWarnings("unchecked") // raw Map cast to Map<String, V>
protected <V> Map<String, V> handleMapResponse(Response response) throws RedisException {
Expand Down Expand Up @@ -309,4 +312,14 @@ public CompletableFuture<Set<String>> smembers(String key) {
public CompletableFuture<Long> scard(String key) {
return commandManager.submitNewCommand(SCard, new String[] {key}, this::handleLongResponse);
}

public CompletableFuture<Long> clientId() {
return commandManager.submitNewCommand(ClientId, new String[0], this::handleLongResponse);
}

@Override
public CompletableFuture<String> clientGetName() {
return commandManager.submitNewCommand(
ClientGetName, new String[0], this::handleStringOrNullResponse);
}
}
13 changes: 13 additions & 0 deletions java/client/src/main/java/glide/api/RedisClient.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
/** Copyright GLIDE-for-Redis Project Contributors - SPDX Identifier: Apache-2.0 */
package glide.api;

import static redis_request.RedisRequestOuterClass.RequestType.ClientGetName;
import static redis_request.RedisRequestOuterClass.RequestType.ClientId;
import static redis_request.RedisRequestOuterClass.RequestType.CustomCommand;
import static redis_request.RedisRequestOuterClass.RequestType.Info;

Expand Down Expand Up @@ -54,4 +56,15 @@ public CompletableFuture<String> info() {
public CompletableFuture<String> info(@NonNull InfoOptions options) {
return commandManager.submitNewCommand(Info, options.toArgs(), this::handleStringResponse);
}

@Override
public CompletableFuture<Long> clientId() {
return commandManager.submitNewCommand(ClientId, new String[0], this::handleLongResponse);
}

@Override
public CompletableFuture<String> clientGetName() {
return commandManager.submitNewCommand(
ClientGetName, new String[0], this::handleStringOrNullResponse);
}
}
41 changes: 40 additions & 1 deletion java/client/src/main/java/glide/api/RedisClusterClient.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
/** Copyright GLIDE-for-Redis Project Contributors - SPDX Identifier: Apache-2.0 */
package glide.api;

import static redis_request.RedisRequestOuterClass.RequestType.ClientGetName;
import static redis_request.RedisRequestOuterClass.RequestType.ClientId;
import static redis_request.RedisRequestOuterClass.RequestType.CustomCommand;
import static redis_request.RedisRequestOuterClass.RequestType.Info;
import static redis_request.RedisRequestOuterClass.RequestType.Ping;
Expand Down Expand Up @@ -53,7 +55,8 @@ public CompletableFuture<ClusterValue<Object>> customCommand(@NonNull String[] a
}

@Override
public CompletableFuture<ClusterValue<Object>> customCommand(String[] args, Route route) {
public CompletableFuture<ClusterValue<Object>> customCommand(
@NonNull String[] args, @NonNull Route route) {
return commandManager.submitNewCommand(
CustomCommand, args, route, response -> handleCustomCommandResponse(route, response));
}
Expand Down Expand Up @@ -132,4 +135,40 @@ public CompletableFuture<ClusterValue<String>> info(
? ClusterValue.of(handleStringResponse(response))
: ClusterValue.of(handleMapResponse(response)));
}

/** {@inheritDoc} The command will be routed a random node. */
@Override
public CompletableFuture<Long> clientId() {
return super.clientId();
}

/** {@inheritDoc} The command will be routed a random node. */
@Override
public CompletableFuture<String> clientGetName() {
return super.clientGetName();
}

@Override
public CompletableFuture<ClusterValue<Long>> clientId(@NonNull Route route) {
return commandManager.submitNewCommand(
ClientId,
new String[0],
route,
response ->
route.isSingleNodeRoute()
? ClusterValue.of(handleLongResponse(response))
: ClusterValue.of(handleMapResponse(response)));
}

@Override
public CompletableFuture<ClusterValue<String>> clientGetName(@NonNull Route route) {
return commandManager.submitNewCommand(
ClientGetName,
new String[0],
route,
response ->
route.isSingleNodeRoute()
? ClusterValue.of(handleStringOrNullResponse(response))
: ClusterValue.of(handleMapResponse(response)));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@
import java.util.concurrent.CompletableFuture;

/**
* Connection Management Commands interface.
* Connection Management Commands interface for both standalone and cluster clients.
*
* @see: <a href="https://redis.io/commands/?group=connection">Connection Management Commands</a>
* @see <a href="https://redis.io/commands/?group=connection">Connection Management Commands</a>
*/
public interface ConnectionManagementCommands {
public interface ConnectionManagementBaseCommands {

/**
* Ping the Redis server.
Expand All @@ -27,4 +27,21 @@ public interface ConnectionManagementCommands {
* str</code>.
*/
CompletableFuture<String> ping(String str);

/**
* Get the current connection id.
*
* @see <a href="https://redis.io/commands/client-id/">redis.io</a> for details.
* @return The id of the client.
*/
CompletableFuture<Long> clientId();

/**
* Get the name of the current connection.
*
* @see <a href="https://redis.io/commands/client-getname/">redis.io</a> for details.
* @return The name of the client connection as a string if a name is set, or null if no name is
* assigned.
*/
CompletableFuture<String> clientGetName();
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
/** Copyright GLIDE-for-Redis Project Contributors - SPDX Identifier: Apache-2.0 */
package glide.api.commands;

import glide.api.models.ClusterValue;
import glide.api.models.configuration.RequestRoutingConfiguration.Route;
import java.util.concurrent.CompletableFuture;

/**
* Connection Management Commands interface.
* Connection Management Commands interface for cluster client.
*
* @see: <a href="https://redis.io/commands/?group=connection">Connection Management Commands</a>
* @see <a href="https://redis.io/commands/?group=connection">Connection Management Commands</a>
*/
public interface ConnectionManagementClusterCommands {

Expand All @@ -17,7 +18,7 @@ public interface ConnectionManagementClusterCommands {
* @see <a href="https://redis.io/commands/ping/">redis.io</a> for details.
* @param route Routing configuration for the command. Client will route the command to the nodes
* defined.
* @return Response from Redis containing a <code>String</code> with "PONG".
* @return Response from Redis containing a <code>String</code> with <code>PONG</code>.
*/
CompletableFuture<String> ping(Route route);

Expand All @@ -32,4 +33,29 @@ public interface ConnectionManagementClusterCommands {
* str</code>.
*/
CompletableFuture<String> ping(String str, Route route);

/**
* Get the current connection id.
*
* @see <a href="https://redis.io/commands/client-id/">redis.io</a> for details.
* @param route Routing configuration for the command. Client will route the command to the nodes
* defined.
* @return A {@link ClusterValue} which holds a single value if single node route is used or a
* dictionary where each address is the key and its corresponding node response is the value.
* The value is the id of the client on that node.
*/
CompletableFuture<ClusterValue<Long>> clientId(Route route);

/**
* Get the name of the current connection.
*
* @see <a href="https://redis.io/commands/client-getname/">redis.io</a> for details.
* @param route Routing configuration for the command. Client will route the command to the nodes
* defined.
* @return A {@link ClusterValue} which holds a single value if single node route is used or a
* dictionary where each address is the key and its corresponding node response is the value.
* The value is the name of the client connection as a string if a name is set, or null if no
* name is assigned.
*/
CompletableFuture<ClusterValue<String>> clientGetName(Route route);
}
25 changes: 25 additions & 0 deletions java/client/src/main/java/glide/api/models/BaseTransaction.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
package glide.api.models;

import static glide.utils.ArrayTransformUtils.convertMapToArgArray;
import static redis_request.RedisRequestOuterClass.RequestType.ClientGetName;
import static redis_request.RedisRequestOuterClass.RequestType.ClientId;
import static redis_request.RedisRequestOuterClass.RequestType.CustomCommand;
import static redis_request.RedisRequestOuterClass.RequestType.Decr;
import static redis_request.RedisRequestOuterClass.RequestType.DecrBy;
Expand Down Expand Up @@ -413,6 +415,29 @@ public T scard(String key) {
return getThis();
}

/**
* Get the current connection id.
*
* @see <a href="https://redis.io/commands/client-id/">redis.io</a> for details.
* @return Command response - The id of the client.
*/
public T clientId() {
protobufTransaction.addCommands(buildCommand(ClientId));
return getThis();
}

/**
* Get the name of the current connection.
*
* @see <a href="https://redis.io/commands/client-getname/">redis.io</a> for details.
* @return Command response - The name of the client connection as a string if a name is set, or
* null if no name is assigned.
*/
public T clientGetName() {
protobufTransaction.addCommands(buildCommand(ClientGetName));
return getThis();
}

/** Build protobuf {@link Command} object for given command and arguments. */
protected Command buildCommand(RequestType requestType) {
return buildCommand(requestType, buildArgs());
Expand Down
83 changes: 82 additions & 1 deletion java/client/src/test/java/glide/api/RedisClusterClientTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static redis_request.RedisRequestOuterClass.RequestType.ClientGetName;
import static redis_request.RedisRequestOuterClass.RequestType.ClientId;
import static redis_request.RedisRequestOuterClass.RequestType.Info;
import static redis_request.RedisRequestOuterClass.RequestType.Ping;

Expand Down Expand Up @@ -118,7 +120,6 @@ public TestClient(CommandManager commandManager, Object objectToReturn) {
object = objectToReturn;
}

@SuppressWarnings("unchecked")
@Override
protected <T> T handleRedisResponse(Class<T> classType, boolean isNullable, Response response) {
return (T) object;
Expand Down Expand Up @@ -314,4 +315,84 @@ public void info_with_options_and_multi_node_route_returns_multi_value() {
assertAll(
() -> assertTrue(value.hasMultiData()), () -> assertEquals(data, value.getMultiValue()));
}

@SneakyThrows
@Test
public void clientId_returns_success() {
// setup
CompletableFuture<Long> testResponse = mock(CompletableFuture.class);
when(testResponse.get()).thenReturn(42L);
when(commandManager.<Long>submitNewCommand(eq(ClientId), eq(new String[0]), any()))
.thenReturn(testResponse);

// exercise
CompletableFuture<Long> response = service.clientId();

// verify
assertEquals(testResponse, response);
assertEquals(42L, response.get());
}

@Test
@SneakyThrows
public void clientId_with_multi_node_route_returns_success() {
var commandManager = new TestCommandManager(null);

var data = Map.of("n1", 42L);
var client = new TestClient(commandManager, data);

var value = client.clientId(ALL_NODES).get();
assertEquals(data, value.getMultiValue());
}

@Test
@SneakyThrows
public void clientId_with_single_node_route_returns_success() {
var commandManager = new TestCommandManager(null);

var client = new TestClient(commandManager, 42L);

var value = client.clientId(RANDOM).get();
assertEquals(42, value.getSingleValue());
}

@SneakyThrows
@Test
public void clientGetName_returns_success() {
// setup
CompletableFuture<String> testResponse = mock(CompletableFuture.class);
when(testResponse.get()).thenReturn("TEST");
when(commandManager.<String>submitNewCommand(eq(ClientGetName), eq(new String[0]), any()))
.thenReturn(testResponse);

// exercise
CompletableFuture<String> response = service.clientGetName();

// verify
assertEquals(testResponse, response);
assertEquals("TEST", response.get());
}

@Test
@SneakyThrows
public void clientGetName_with_single_node_route_returns_success() {
var commandManager = new TestCommandManager(null);

var client = new TestClient(commandManager, "TEST");

var value = client.clientGetName(RANDOM).get();
assertEquals("TEST", value.getSingleValue());
}

@Test
@SneakyThrows
public void clientGetName_with_multi_node_route_returns_success() {
var commandManager = new TestCommandManager(null);

var data = Map.of("n1", "TEST");
var client = new TestClient(commandManager, data);

var value = client.clientGetName(ALL_NODES).get();
assertEquals(data, value.getMultiValue());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@

import static glide.api.models.commands.SetOptions.RETURN_OLD_VALUE;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static redis_request.RedisRequestOuterClass.RequestType.ClientGetName;
import static redis_request.RedisRequestOuterClass.RequestType.ClientId;
import static redis_request.RedisRequestOuterClass.RequestType.Decr;
import static redis_request.RedisRequestOuterClass.RequestType.DecrBy;
import static redis_request.RedisRequestOuterClass.RequestType.GetString;
Expand Down Expand Up @@ -116,6 +118,10 @@ public void transaction_builds_protobuf_request() {
transaction.scard("key");
results.add(Pair.of(SCard, ArgsArray.newBuilder().addArgs("key").build()));

transaction.clientId().clientGetName();
results.add(Pair.of(ClientId, ArgsArray.newBuilder().build()));
results.add(Pair.of(ClientGetName, ArgsArray.newBuilder().build()));

var protobufTransaction = transaction.getProtobufTransaction().build();

for (int idx = 0; idx < protobufTransaction.getCommandsCount(); idx++) {
Expand Down
Loading

0 comments on commit c31877e

Please sign in to comment.