diff --git a/java/client/src/main/java/glide/api/BaseClient.java b/java/client/src/main/java/glide/api/BaseClient.java index 2e6a1a9241..8ad5a4ba55 100644 --- a/java/client/src/main/java/glide/api/BaseClient.java +++ b/java/client/src/main/java/glide/api/BaseClient.java @@ -25,6 +25,7 @@ import glide.managers.BaseCommandResponseResolver; import glide.managers.CommandManager; import glide.managers.ConnectionManager; +import java.util.Map; import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; @@ -121,7 +122,7 @@ protected static CommandManager buildCommandManager(ChannelHandler channelHandle * @throws RedisException on a type mismatch */ @SuppressWarnings("unchecked") - private T handleRedisResponse(Class classType, boolean isNullable, Response response) + protected T handleRedisResponse(Class classType, boolean isNullable, Response response) throws RedisException { Object value = new BaseCommandResponseResolver(RedisValueResolver::valueFromPointer).apply(response); @@ -139,10 +140,6 @@ private T handleRedisResponse(Class classType, boolean isNullable, Respon + classType.getSimpleName()); } - protected Object handleObjectResponse(Response response) throws RedisException { - return handleRedisResponse(Object.class, false, response); - } - protected Object handleObjectOrNullResponse(Response response) throws RedisException { return handleRedisResponse(Object.class, true, response); } @@ -159,11 +156,22 @@ protected Long handleLongResponse(Response response) throws RedisException { return handleRedisResponse(Long.class, false, response); } - protected Object[] handleArrayResponse(Response response) { + protected Object[] handleArrayResponse(Response response) throws RedisException { return handleRedisResponse(Object[].class, true, response); } - protected Set handleSetResponse(Response response) { + /** + * @param response A Protobuf response + * @return A map of String to V + * @param Value type, could be even map too + */ + @SuppressWarnings("unchecked") // raw Map cast to Map + protected Map handleMapResponse(Response response) throws RedisException { + return handleRedisResponse(Map.class, false, response); + } + + @SuppressWarnings("unchecked") // raw Set cast to Set + protected Set handleSetResponse(Response response) throws RedisException { return handleRedisResponse(Set.class, false, response); } diff --git a/java/client/src/main/java/glide/api/RedisClusterClient.java b/java/client/src/main/java/glide/api/RedisClusterClient.java index c802979931..65f6d76e09 100644 --- a/java/client/src/main/java/glide/api/RedisClusterClient.java +++ b/java/client/src/main/java/glide/api/RedisClusterClient.java @@ -99,24 +99,36 @@ public CompletableFuture ping(@NonNull String str, @NonNull Route route) @Override public CompletableFuture> info() { return commandManager.submitNewCommand( - Info, new String[0], response -> ClusterValue.of(handleObjectResponse(response))); + Info, new String[0], response -> ClusterValue.of(handleMapResponse(response))); } public CompletableFuture> info(@NonNull Route route) { return commandManager.submitNewCommand( - Info, new String[0], route, response -> ClusterValue.of(handleObjectResponse(response))); + Info, + new String[0], + route, + response -> + route.isSingleNodeRoute() + ? ClusterValue.of(handleStringResponse(response)) + : ClusterValue.of(handleMapResponse(response))); } @Override public CompletableFuture> info(@NonNull InfoOptions options) { return commandManager.submitNewCommand( - Info, options.toArgs(), response -> ClusterValue.of(handleObjectResponse(response))); + Info, options.toArgs(), response -> ClusterValue.of(handleMapResponse(response))); } @Override public CompletableFuture> info( @NonNull InfoOptions options, @NonNull Route route) { return commandManager.submitNewCommand( - Info, options.toArgs(), route, response -> ClusterValue.of(handleObjectResponse(response))); + Info, + options.toArgs(), + route, + response -> + route.isSingleNodeRoute() + ? ClusterValue.of(handleStringResponse(response)) + : ClusterValue.of(handleMapResponse(response))); } } diff --git a/java/client/src/main/java/glide/api/commands/ServerManagementClusterCommands.java b/java/client/src/main/java/glide/api/commands/ServerManagementClusterCommands.java index eddaa21550..f511cc81fb 100644 --- a/java/client/src/main/java/glide/api/commands/ServerManagementClusterCommands.java +++ b/java/client/src/main/java/glide/api/commands/ServerManagementClusterCommands.java @@ -3,6 +3,7 @@ import glide.api.models.ClusterValue; import glide.api.models.commands.InfoOptions; +import glide.api.models.commands.InfoOptions.Section; import glide.api.models.configuration.RequestRoutingConfiguration.Route; import java.util.concurrent.CompletableFuture; @@ -14,11 +15,10 @@ public interface ServerManagementClusterCommands { /** - * Get information and statistics about the Redis server. DEFAULT option is assumed. The command - * will be routed to all primaries. + * Get information and statistics about the Redis server using the {@link Section#DEFAULT} option. + * The command will be routed to all primary nodes. * - * @see redis.io for details. {@link - * InfoOptions.Section#DEFAULT} option is assumed. + * @see redis.io for details. * @return Response from Redis cluster with a Map{@literal } with * each address as the key and its corresponding value is the information for the node. * @example @@ -29,7 +29,8 @@ public interface ServerManagementClusterCommands { CompletableFuture> info(); /** - * Get information and statistics about the Redis server. DEFAULT option is assumed + * Get information and statistics about the Redis server. If no argument is provided, so the + * {@link Section#DEFAULT} option is assumed. * * @see redis.io for details. * @param route Routing configuration for the command. Client will route the command to the nodes @@ -43,10 +44,10 @@ public interface ServerManagementClusterCommands { /** * Get information and statistics about the Redis server. The command will be routed to all - * primaries. + * primary nodes. * * @see redis.io for details. - * @param options - A list of {@link InfoOptions.Section} values specifying which sections of + * @param options A list of {@link InfoOptions.Section} values specifying which sections of * information to retrieve. When no parameter is provided, the {@link * InfoOptions.Section#DEFAULT} option is assumed. * @return Response from Redis cluster with a Map{@literal } with @@ -59,7 +60,7 @@ public interface ServerManagementClusterCommands { * Get information and statistics about the Redis server. * * @see redis.io for details. - * @param options - A list of {@link InfoOptions.Section} values specifying which sections of + * @param options A list of {@link InfoOptions.Section} values specifying which sections of * information to retrieve. When no parameter is provided, the {@link * InfoOptions.Section#DEFAULT} option is assumed. * @param route Routing configuration for the command. Client will route the command to the nodes diff --git a/java/client/src/main/java/glide/api/commands/ServerManagementCommands.java b/java/client/src/main/java/glide/api/commands/ServerManagementCommands.java index a65d0e0619..bd7ed56cdc 100644 --- a/java/client/src/main/java/glide/api/commands/ServerManagementCommands.java +++ b/java/client/src/main/java/glide/api/commands/ServerManagementCommands.java @@ -13,8 +13,7 @@ public interface ServerManagementCommands { /** - * Get information and statistics about the Redis server. No argument is provided, so the {@link - * Section#DEFAULT} option is assumed. + * Get information and statistics about the Redis server using the {@link Section#DEFAULT} option. * * @see redis.io for details. * @return Response from Redis containing a String with the information for the diff --git a/java/client/src/main/java/glide/api/models/BaseTransaction.java b/java/client/src/main/java/glide/api/models/BaseTransaction.java index 6d40cc5ce2..8721331ecc 100644 --- a/java/client/src/main/java/glide/api/models/BaseTransaction.java +++ b/java/client/src/main/java/glide/api/models/BaseTransaction.java @@ -91,8 +91,7 @@ public T ping(String msg) { } /** - * Get information and statistics about the Redis server. No argument is provided, so the {@link - * Section#DEFAULT} option is assumed. + * Get information and statistics about the Redis server using the {@link Section#DEFAULT} option. * * @see redis.io for details. * @return A response from Redis with a String. diff --git a/java/client/src/test/java/glide/api/RedisClusterClientTest.java b/java/client/src/test/java/glide/api/RedisClusterClientTest.java index 4f3efafc59..02af1b0130 100644 --- a/java/client/src/test/java/glide/api/RedisClusterClientTest.java +++ b/java/client/src/test/java/glide/api/RedisClusterClientTest.java @@ -54,9 +54,7 @@ public void custom_command_returns_single_value() { var client = new TestClient(commandManager, "TEST"); var value = client.customCommand(TEST_ARGS).get(); - assertAll( - () -> assertTrue(value.hasSingleData()), - () -> assertEquals("TEST", value.getSingleValue())); + assertEquals("TEST", value.getSingleValue()); } @Test @@ -68,8 +66,7 @@ public void custom_command_returns_multi_value() { var client = new TestClient(commandManager, data); var value = client.customCommand(TEST_ARGS).get(); - assertAll( - () -> assertTrue(value.hasMultiData()), () -> assertEquals(data, value.getMultiValue())); + assertEquals(data, value.getMultiValue()); } @Test @@ -82,8 +79,7 @@ public void custom_command_with_single_node_route_returns_single_value() { var client = new TestClient(commandManager, data); var value = client.customCommand(TEST_ARGS, RANDOM).get(); - assertAll( - () -> assertTrue(value.hasSingleData()), () -> assertEquals(data, value.getSingleValue())); + assertEquals(data, value.getSingleValue()); } @Test @@ -95,8 +91,7 @@ public void custom_command_with_multi_node_route_returns_multi_value() { var client = new TestClient(commandManager, data); var value = client.customCommand(TEST_ARGS, ALL_NODES).get(); - assertAll( - () -> assertTrue(value.hasMultiData()), () -> assertEquals(data, value.getMultiValue())); + assertEquals(data, value.getMultiValue()); } private static class TestClient extends RedisClusterClient { @@ -108,9 +103,10 @@ public TestClient(CommandManager commandManager, Object objectToReturn) { object = objectToReturn; } + @SuppressWarnings("unchecked") @Override - protected Object handleObjectOrNullResponse(Response response) { - return object; + protected T handleRedisResponse(Class classType, boolean isNullable, Response response) { + return (T) object; } } @@ -251,4 +247,56 @@ public void info_with_route_with_infoOptions_returns_string() { assertEquals("addr1 result", clusterMap.get("addr1")); assertEquals("addr2 result", clusterMap.get("addr2")); } + + @Test + @SneakyThrows + public void info_with_single_node_route_returns_single_value() { + var commandManager = new TestCommandManager(null); + + var data = "info string"; + var client = new TestClient(commandManager, data); + + var value = client.info(RANDOM).get(); + assertAll( + () -> assertTrue(value.hasSingleData()), () -> assertEquals(data, value.getSingleValue())); + } + + @Test + @SneakyThrows + public void info_with_multi_node_route_returns_multi_value() { + var commandManager = new TestCommandManager(null); + + var data = Map.of("key1", "value1", "key2", "value2"); + var client = new TestClient(commandManager, data); + + var value = client.info(ALL_NODES).get(); + assertAll( + () -> assertTrue(value.hasMultiData()), () -> assertEquals(data, value.getMultiValue())); + } + + @Test + @SneakyThrows + public void info_with_options_and_single_node_route_returns_single_value() { + var commandManager = new TestCommandManager(null); + + var data = "info string"; + var client = new TestClient(commandManager, data); + + var value = client.info(InfoOptions.builder().build(), RANDOM).get(); + assertAll( + () -> assertTrue(value.hasSingleData()), () -> assertEquals(data, value.getSingleValue())); + } + + @Test + @SneakyThrows + public void info_with_options_and_multi_node_route_returns_multi_value() { + var commandManager = new TestCommandManager(null); + + var data = Map.of("key1", "value1", "key2", "value2"); + var client = new TestClient(commandManager, data); + + var value = client.info(InfoOptions.builder().build(), ALL_NODES).get(); + assertAll( + () -> assertTrue(value.hasMultiData()), () -> assertEquals(data, value.getMultiValue())); + } } diff --git a/java/integTest/src/test/java/glide/cluster/CommandTests.java b/java/integTest/src/test/java/glide/cluster/CommandTests.java index 0e4e584877..38973aa581 100644 --- a/java/integTest/src/test/java/glide/cluster/CommandTests.java +++ b/java/integTest/src/test/java/glide/cluster/CommandTests.java @@ -158,7 +158,7 @@ public void info_without_options() { @Test @SneakyThrows - public void info_with_route() { + public void info_with_single_node_route() { ClusterValue data = clusterClient.info(RANDOM).get(); assertTrue(data.hasSingleData()); String infoData = data.getSingleValue(); @@ -167,6 +167,18 @@ public void info_with_route() { } } + @Test + @SneakyThrows + public void info_with_multi_node_route() { + ClusterValue data = clusterClient.info(ALL_NODES).get(); + assertTrue(data.hasMultiData()); + for (String info : data.getMultiValue().values()) { + for (String section : DEFAULT_INFO_SECTIONS) { + assertTrue(info.contains("# " + section), "Section " + section + " is missing"); + } + } + } + @Test @SneakyThrows public void info_with_multiple_options() { @@ -200,7 +212,7 @@ public void info_with_everything_option() { @Test @SneakyThrows - public void info_with_routing_and_options() { + public void info_with_single_node_route_and_options() { ClusterValue slotData = clusterClient.customCommand(new String[] {"cluster", "slots"}).get(); @@ -229,4 +241,23 @@ public void info_with_routing_and_options() { "Section " + section + " is missing"); } } + + @Test + @SneakyThrows + public void info_with_multi_node_route_and_options() { + InfoOptions.InfoOptionsBuilder builder = InfoOptions.builder().section(CLIENTS); + if (REDIS_VERSION.feature() >= 7) { + builder.section(COMMANDSTATS).section(REPLICATION); + } + InfoOptions options = builder.build(); + ClusterValue data = clusterClient.info(options, ALL_NODES).get(); + + for (String info : data.getMultiValue().values()) { + for (String section : options.toArgs()) { + assertTrue( + info.toLowerCase().contains("# " + section.toLowerCase()), + "Section " + section + " is missing"); + } + } + } }