Skip to content

Commit

Permalink
Add CLIENT ID and CLIENT GETNAME commands. (valkey-io#1014)
Browse files Browse the repository at this point in the history
* Add `CLIENT ID` and `CLIENT GETNAME` commands. (#98)

Signed-off-by: Yury-Fridlyand <[email protected]>
Co-authored-by: Andrew Carbonetto <[email protected]>
Co-authored-by: SanHalacogluImproving <[email protected]>
  • Loading branch information
3 people authored Mar 5, 2024
1 parent fb93d1a commit 0ee5ab0
Show file tree
Hide file tree
Showing 13 changed files with 407 additions and 7 deletions.
3 changes: 2 additions & 1 deletion java/client/src/main/java/glide/api/BaseClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ public abstract class BaseClient
HashCommands,
ListBaseCommands,
SetCommands {

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

Expand Down Expand Up @@ -214,7 +215,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
*/
@SuppressWarnings("unchecked") // raw Map cast to Map<String, V>
protected <V> Map<String, V> handleMapResponse(Response response) throws RedisException {
Expand Down
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;
import static redis_request.RedisRequestOuterClass.RequestType.Ping;
Expand Down Expand Up @@ -75,4 +77,15 @@ public CompletableFuture<String> select(long index) {
return commandManager.submitNewCommand(
Select, new String[] {Long.toString(index)}, 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);
}
}
37 changes: 37 additions & 0 deletions 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 @@ -144,4 +146,39 @@ public CompletableFuture<ClusterValue<String>> info(
? ClusterValue.of(handleStringResponse(response))
: ClusterValue.of(handleMapResponse(response)));
}

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

@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<String> clientGetName() {
return commandManager.submitNewCommand(
ClientGetName, new String[0], this::handleStringOrNullResponse);
}

@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
@@ -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 Down Expand Up @@ -48,4 +49,74 @@ public interface ConnectionManagementClusterCommands {
* @return <code>String</code> with a copy of the argument <code>message</code>.
*/
CompletableFuture<String> ping(String message, Route route);

/**
* Gets the current connection id.<br>
* The command will be routed to a random node.
*
* @see <a href="https://redis.io/commands/client-id/">redis.io</a> for details.
* @return The id of the client.
* @example
* <pre>{@code
* long id = client.clientId().get();
* assert id > 0
* }</pre>
*/
CompletableFuture<Long> clientId();

/**
* Gets 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.
* @example
* <pre>{@code
* long id = client.clientId(new SlotIdRoute(...)).get().getSingleValue();
* assert id > 0;
*
* Map<String, Long> idPerNode = client.clientId(ALL_NODES).get().getMultiValue();
* assert idPerNode.get("<node 1 address>") > 0;
* </pre>
*/
CompletableFuture<ClusterValue<Long>> clientId(Route route);

/**
* Gets the name of the current connection.<br>
* The command will be routed a random node.
*
* @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 <code>null</code> if
* no name is assigned.
* @example
* <pre>{@code
* String clientName = client.clientGetName().get();
* assert clientName != null;
* }</pre>
*/
CompletableFuture<String> clientGetName();

/**
* Gets 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.
* @example
* <pre>{@code
* String clientName = client.clientGetName(new SlotIdRoute(...)).get().getSingleValue();
* assert clientName != null;
*
* Map<String, String> clientNamePerNode = client.clientGetName(ALL_NODES).get().getMultiValue();
* assert clientNamePerNode.get("<node 1 address>") != null
* }</pre>
*/
CompletableFuture<ClusterValue<String>> clientGetName(Route route);
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
import java.util.concurrent.CompletableFuture;

/**
* Connection Management Commands interface.
* Connection Management Commands interface for standalone 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 ConnectionManagementCommands {

Expand All @@ -26,4 +26,31 @@ public interface ConnectionManagementCommands {
* @return <code>String</code> with a copy of the argument <code>message</code>.
*/
CompletableFuture<String> ping(String message);

/**
* Gets the current connection id.
*
* @see <a href="https://redis.io/commands/client-id/">redis.io</a> for details.
* @return The id of the client.
* @example
* <pre>{@code
* Long id = client.clientId().get();
* assert id > 0;
* }</pre>
*/
CompletableFuture<Long> clientId();

/**
* Gets 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 <code>null</code> if
* no name is assigned.
* @example
* <pre>{@code
* String clientName = client.clientGetName().get();
* assert clientName != null;
* }</pre>
*/
CompletableFuture<String> clientGetName();
}
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 @@ -966,6 +968,29 @@ public T ttl(@NonNull 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
* <code>null</code> 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
40 changes: 40 additions & 0 deletions java/client/src/test/java/glide/api/RedisClientTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,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.CustomCommand;
import static redis_request.RedisRequestOuterClass.RequestType.Decr;
import static redis_request.RedisRequestOuterClass.RequestType.DecrBy;
Expand Down Expand Up @@ -1286,4 +1288,42 @@ public void scard_returns_success() {
assertEquals(testResponse, response);
assertEquals(value, payload);
}

@SneakyThrows
@Test
public void clientId_returns_success() {
// setup
CompletableFuture<Long> testResponse = mock(CompletableFuture.class);
when(testResponse.get()).thenReturn(42L);

// match on protobuf request
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());
}

@SneakyThrows
@Test
public void clientGetName_returns_success() {
// setup
CompletableFuture<String> testResponse = mock(CompletableFuture.class);
when(testResponse.get()).thenReturn("TEST");

// match on protobuf request
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());
}
}
Loading

0 comments on commit 0ee5ab0

Please sign in to comment.