Skip to content

Commit

Permalink
Java: Add FUNCTION LIST command. (valkey-io#1452)
Browse files Browse the repository at this point in the history
* Add `FUNCTION LIST` command. (#277)

Signed-off-by: Yury-Fridlyand <[email protected]>
Co-authored-by: Aaron <[email protected]>
  • Loading branch information
2 people authored and yipin-chen committed Jun 7, 2024
1 parent 91aebd1 commit f775561
Show file tree
Hide file tree
Showing 16 changed files with 781 additions and 38 deletions.
1 change: 1 addition & 0 deletions glide-core/src/protobuf/redis_request.proto
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,7 @@ enum RequestType {
BitOp = 148;
HStrlen = 149;
FunctionLoad = 150;
FunctionList = 151;
LMPop = 155;
ExpireTime = 156;
PExpireTime = 157;
Expand Down
3 changes: 3 additions & 0 deletions glide-core/src/request_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@ pub enum RequestType {
BitOp = 148,
HStrlen = 149,
FunctionLoad = 150,
FunctionList = 151,
LMPop = 155,
ExpireTime = 156,
PExpireTime = 157,
Expand Down Expand Up @@ -338,6 +339,7 @@ impl From<::protobuf::EnumOrUnknown<ProtobufRequestType>> for RequestType {
ProtobufRequestType::GetBit => RequestType::GetBit,
ProtobufRequestType::ZInter => RequestType::ZInter,
ProtobufRequestType::FunctionLoad => RequestType::FunctionLoad,
ProtobufRequestType::FunctionList => RequestType::FunctionList,
ProtobufRequestType::BitPos => RequestType::BitPos,
ProtobufRequestType::BitOp => RequestType::BitOp,
ProtobufRequestType::HStrlen => RequestType::HStrlen,
Expand Down Expand Up @@ -513,6 +515,7 @@ impl RequestType {
RequestType::GetBit => Some(cmd("GETBIT")),
RequestType::ZInter => Some(cmd("ZINTER")),
RequestType::FunctionLoad => Some(get_two_word_command("FUNCTION", "LOAD")),
RequestType::FunctionList => Some(get_two_word_command("FUNCTION", "LIST")),
RequestType::BitPos => Some(cmd("BITPOS")),
RequestType::BitOp => Some(cmd("BITOP")),
RequestType::HStrlen => Some(cmd("HSTRLEN")),
Expand Down
12 changes: 12 additions & 0 deletions java/client/src/main/java/glide/api/BaseClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,18 @@ protected Set<String> handleSetResponse(Response response) throws RedisException
return handleRedisResponse(Set.class, false, response);
}

/** Process a <code>FUNCTION LIST</code> standalone response. */
@SuppressWarnings("unchecked")
protected Map<String, Object>[] handleFunctionListResponse(Object[] response) {
Map<String, Object>[] data = castArray(response, Map.class);
for (Map<String, Object> libraryInfo : data) {
Object[] functions = (Object[]) libraryInfo.get("functions");
var functionInfo = castArray(functions, Map.class);
libraryInfo.put("functions", functionInfo);
}
return data;
}

@Override
public CompletableFuture<Long> del(@NonNull String[] keys) {
return commandManager.submitNewCommand(Del, keys, this::handleLongResponse);
Expand Down
22 changes: 22 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 glide.api.models.commands.function.FunctionListOptions.LIBRARY_NAME_REDIS_API;
import static glide.api.models.commands.function.FunctionListOptions.WITH_CODE_REDIS_API;
import static glide.api.models.commands.function.FunctionLoadOptions.REPLACE;
import static glide.utils.ArrayTransformUtils.castArray;
import static glide.utils.ArrayTransformUtils.concatenateArrays;
Expand All @@ -14,6 +16,7 @@
import static redis_request.RedisRequestOuterClass.RequestType.CustomCommand;
import static redis_request.RedisRequestOuterClass.RequestType.Echo;
import static redis_request.RedisRequestOuterClass.RequestType.FlushAll;
import static redis_request.RedisRequestOuterClass.RequestType.FunctionList;
import static redis_request.RedisRequestOuterClass.RequestType.FunctionLoad;
import static redis_request.RedisRequestOuterClass.RequestType.Info;
import static redis_request.RedisRequestOuterClass.RequestType.LastSave;
Expand Down Expand Up @@ -203,4 +206,23 @@ public CompletableFuture<Boolean> move(@NonNull String key, long dbIndex) {
return commandManager.submitNewCommand(
Move, new String[] {key, Long.toString(dbIndex)}, this::handleBooleanResponse);
}

@Override
public CompletableFuture<Map<String, Object>[]> functionList(boolean withCode) {
return commandManager.submitNewCommand(
FunctionList,
withCode ? new String[] {WITH_CODE_REDIS_API} : new String[0],
response -> handleFunctionListResponse(handleArrayResponse(response)));
}

@Override
public CompletableFuture<Map<String, Object>[]> functionList(
@NonNull String libNamePattern, boolean withCode) {
return commandManager.submitNewCommand(
FunctionList,
withCode
? new String[] {LIBRARY_NAME_REDIS_API, libNamePattern, WITH_CODE_REDIS_API}
: new String[] {LIBRARY_NAME_REDIS_API, libNamePattern},
response -> handleFunctionListResponse(handleArrayResponse(response)));
}
}
62 changes: 62 additions & 0 deletions java/client/src/main/java/glide/api/RedisClusterClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
package glide.api;

import static glide.api.commands.ServerManagementCommands.VERSION_REDIS_API;
import static glide.api.models.commands.function.FunctionListOptions.LIBRARY_NAME_REDIS_API;
import static glide.api.models.commands.function.FunctionListOptions.WITH_CODE_REDIS_API;
import static glide.api.models.commands.function.FunctionLoadOptions.REPLACE;
import static glide.utils.ArrayTransformUtils.castArray;
import static glide.utils.ArrayTransformUtils.castMapOfArrays;
Expand All @@ -16,6 +18,7 @@
import static redis_request.RedisRequestOuterClass.RequestType.CustomCommand;
import static redis_request.RedisRequestOuterClass.RequestType.Echo;
import static redis_request.RedisRequestOuterClass.RequestType.FlushAll;
import static redis_request.RedisRequestOuterClass.RequestType.FunctionList;
import static redis_request.RedisRequestOuterClass.RequestType.FunctionLoad;
import static redis_request.RedisRequestOuterClass.RequestType.Info;
import static redis_request.RedisRequestOuterClass.RequestType.LastSave;
Expand All @@ -37,6 +40,7 @@
import glide.managers.CommandManager;
import glide.managers.ConnectionManager;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
Expand Down Expand Up @@ -432,4 +436,62 @@ public CompletableFuture<String> functionLoad(
return commandManager.submitNewCommand(
FunctionLoad, arguments, route, this::handleStringResponse);
}

/** Process a <code>FUNCTION LIST</code> cluster response. */
protected ClusterValue<Map<String, Object>[]> handleFunctionListResponse(
Response response, Route route) {
if (route instanceof SingleNodeRoute) {
Map<String, Object>[] data = handleFunctionListResponse(handleArrayResponse(response));
return ClusterValue.ofSingleValue(data);
} else {
// each `Object` is a `Map<String, Object>[]` actually
Map<String, Object> info = handleMapResponse(response);
Map<String, Map<String, Object>[]> data = new HashMap<>();
for (var nodeInfo : info.entrySet()) {
data.put(nodeInfo.getKey(), handleFunctionListResponse((Object[]) nodeInfo.getValue()));
}
return ClusterValue.ofMultiValue(data);
}
}

@Override
public CompletableFuture<Map<String, Object>[]> functionList(boolean withCode) {
return commandManager.submitNewCommand(
FunctionList,
withCode ? new String[] {WITH_CODE_REDIS_API} : new String[0],
response -> handleFunctionListResponse(handleArrayResponse(response)));
}

@Override
public CompletableFuture<Map<String, Object>[]> functionList(
@NonNull String libNamePattern, boolean withCode) {
return commandManager.submitNewCommand(
FunctionList,
withCode
? new String[] {LIBRARY_NAME_REDIS_API, libNamePattern, WITH_CODE_REDIS_API}
: new String[] {LIBRARY_NAME_REDIS_API, libNamePattern},
response -> handleFunctionListResponse(handleArrayResponse(response)));
}

@Override
public CompletableFuture<ClusterValue<Map<String, Object>[]>> functionList(
boolean withCode, @NonNull Route route) {
return commandManager.submitNewCommand(
FunctionList,
withCode ? new String[] {WITH_CODE_REDIS_API} : new String[0],
route,
response -> handleFunctionListResponse(response, route));
}

@Override
public CompletableFuture<ClusterValue<Map<String, Object>[]>> functionList(
@NonNull String libNamePattern, boolean withCode, @NonNull Route route) {
return commandManager.submitNewCommand(
FunctionList,
withCode
? new String[] {LIBRARY_NAME_REDIS_API, libNamePattern, WITH_CODE_REDIS_API}
: new String[] {LIBRARY_NAME_REDIS_API, libNamePattern},
route,
response -> handleFunctionListResponse(response, route));
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
/** 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.Map;
import java.util.concurrent.CompletableFuture;

/**
Expand Down Expand Up @@ -51,4 +53,118 @@ public interface ScriptingAndFunctionsClusterCommands {
* }</pre>
*/
CompletableFuture<String> functionLoad(String libraryCode, boolean replace, Route route);

/**
* Returns information about the functions and libraries.<br>
* The command will be routed to a random node.
*
* @since Redis 7.0 and above.
* @see <a href="https://redis.io/docs/latest/commands/function-list/">redis.io</a> for details.
* @param withCode Specifies whether to request the library code from the server or not.
* @return Info about all libraries and their functions.
* @example
* <pre>{@code
* Map<String, Object>[] response = client.functionList(true).get();
* for (Map<String, Object> libraryInfo : response) {
* System.out.printf("Server has library '%s' which runs on %s engine%n",
* libraryInfo.get("library_name"), libraryInfo.get("engine"));
* Map<String, Object>[] functions = (Map<String, Object>[]) libraryInfo.get("functions");
* for (Map<String, Object> function : functions) {
* Set<String> flags = (Set<String>) function.get("flags");
* System.out.printf("Library has function '%s' with flags '%s' described as %s%n",
* function.get("name"), String.join(", ", flags), function.get("description"));
* }
* System.out.printf("Library code:%n%s%n", libraryInfo.get("library_code"));
* }
* }</pre>
*/
CompletableFuture<Map<String, Object>[]> functionList(boolean withCode);

/**
* Returns information about the functions and libraries.<br>
* The command will be routed to a random node.
*
* @since Redis 7.0 and above.
* @see <a href="https://redis.io/docs/latest/commands/function-list/">redis.io</a> for details.
* @param libNamePattern A wildcard pattern for matching library names.
* @param withCode Specifies whether to request the library code from the server or not.
* @return Info about queried libraries and their functions.
* @example
* <pre>{@code
* Map<String, Object>[] response = client.functionList("myLib?_backup", true).get();
* for (Map<String, Object> libraryInfo : response) {
* System.out.printf("Server has library '%s' which runs on %s engine%n",
* libraryInfo.get("library_name"), libraryInfo.get("engine"));
* Map<String, Object>[] functions = (Map<String, Object>[]) libraryInfo.get("functions");
* for (Map<String, Object> function : functions) {
* Set<String> flags = (Set<String>) function.get("flags");
* System.out.printf("Library has function '%s' with flags '%s' described as %s%n",
* function.get("name"), String.join(", ", flags), function.get("description"));
* }
* System.out.printf("Library code:%n%s%n", libraryInfo.get("library_code"));
* }
* }</pre>
*/
CompletableFuture<Map<String, Object>[]> functionList(String libNamePattern, boolean withCode);

/**
* Returns information about the functions and libraries.
*
* @since Redis 7.0 and above.
* @see <a href="https://redis.io/docs/latest/commands/function-list/">redis.io</a> for details.
* @param withCode Specifies whether to request the library code from the server or not.
* @param route Specifies the routing configuration for the command. The client will route the
* command to the nodes defined by <code>route</code>.
* @return Info about all libraries and their functions.
* @example
* <pre>{@code
* ClusterValue<Map<String, Object>[]> response = client.functionList(true, ALL_NODES).get();
* for (String node : response.getMultiValue().keySet()) {
* for (Map<String, Object> libraryInfo : response.getMultiValue().get(node)) {
* System.out.printf("Node '%s' has library '%s' which runs on %s engine%n",
* node, libraryInfo.get("library_name"), libraryInfo.get("engine"));
* Map<String, Object>[] functions = (Map<String, Object>[]) libraryInfo.get("functions");
* for (Map<String, Object> function : functions) {
* Set<String> flags = (Set<String>) function.get("flags");
* System.out.printf("Library has function '%s' with flags '%s' described as %s%n",
* function.get("name"), String.join(", ", flags), function.get("description"));
* }
* System.out.printf("Library code:%n%s%n", libraryInfo.get("library_code"));
* }
* }
* }</pre>
*/
CompletableFuture<ClusterValue<Map<String, Object>[]>> functionList(
boolean withCode, Route route);

/**
* Returns information about the functions and libraries.
*
* @since Redis 7.0 and above.
* @see <a href="https://redis.io/docs/latest/commands/function-list/">redis.io</a> for details.
* @param libNamePattern A wildcard pattern for matching library names.
* @param withCode Specifies whether to request the library code from the server or not.
* @param route Specifies the routing configuration for the command. The client will route the
* command to the nodes defined by <code>route</code>.
* @return Info about queried libraries and their functions.
* @example
* <pre>{@code
* ClusterValue<Map<String, Object>[]> response = client.functionList("myLib?_backup", true, ALL_NODES).get();
* for (String node : response.getMultiValue().keySet()) {
* for (Map<String, Object> libraryInfo : response.getMultiValue().get(node)) {
* System.out.printf("Node '%s' has library '%s' which runs on %s engine%n",
* node, libraryInfo.get("library_name"), libraryInfo.get("engine"));
* Map<String, Object>[] functions = (Map<String, Object>[]) libraryInfo.get("functions");
* for (Map<String, Object> function : functions) {
* Set<String> flags = (Set<String>) function.get("flags");
* System.out.printf("Library has function '%s' with flags '%s' described as %s%n",
* function.get("name"), String.join(", ", flags), function.get("description"));
* }
* System.out.printf("Library code:%n%s%n", libraryInfo.get("library_code"));
* }
* }
* }</pre>
*/
CompletableFuture<ClusterValue<Map<String, Object>[]>> functionList(
String libNamePattern, boolean withCode, Route route);
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/** Copyright GLIDE-for-Redis Project Contributors - SPDX Identifier: Apache-2.0 */
package glide.api.commands;

import java.util.Map;
import java.util.concurrent.CompletableFuture;

/**
Expand Down Expand Up @@ -29,4 +30,55 @@ public interface ScriptingAndFunctionsCommands {
* }</pre>
*/
CompletableFuture<String> functionLoad(String libraryCode, boolean replace);

/**
* Returns information about the functions and libraries.
*
* @since Redis 7.0 and above.
* @see <a href="https://redis.io/docs/latest/commands/function-list/">redis.io</a> for details.
* @param withCode Specifies whether to request the library code from the server or not.
* @return Info about all libraries and their functions.
* @example
* <pre>{@code
* Map<String, Object>[] response = client.functionList(true).get();
* for (Map<String, Object> libraryInfo : response) {
* System.out.printf("Server has library '%s' which runs on %s engine%n",
* libraryInfo.get("library_name"), libraryInfo.get("engine"));
* Map<String, Object>[] functions = (Map<String, Object>[]) libraryInfo.get("functions");
* for (Map<String, Object> function : functions) {
* Set<String> flags = (Set<String>) function.get("flags");
* System.out.printf("Library has function '%s' with flags '%s' described as %s%n",
* function.get("name"), String. join(", ", flags), function.get("description"));
* }
* System.out.printf("Library code:%n%s%n", libraryInfo.get("library_code"));
* }
* }</pre>
*/
CompletableFuture<Map<String, Object>[]> functionList(boolean withCode);

/**
* Returns information about the functions and libraries.
*
* @since Redis 7.0 and above.
* @see <a href="https://redis.io/docs/latest/commands/function-list/">redis.io</a> for details.
* @param libNamePattern A wildcard pattern for matching library names.
* @param withCode Specifies whether to request the library code from the server or not.
* @return Info about queried libraries and their functions.
* @example
* <pre>{@code
* Map<String, Object>[] response = client.functionList("myLib?_backup", true).get();
* for (Map<String, Object> libraryInfo : response) {
* System.out.printf("Server has library '%s' which runs on %s engine%n",
* libraryInfo.get("library_name"), libraryInfo.get("engine"));
* Map<String, Object>[] functions = (Map<String, Object>[]) libraryInfo.get("functions");
* for (Map<String, Object> function : functions) {
* Set<String> flags = (Set<String>) function.get("flags");
* System.out.printf("Library has function '%s' with flags '%s' described as %s%n",
* function.get("name"), String. join(", ", flags), function.get("description"));
* }
* System.out.printf("Library code:%n%s%n", libraryInfo.get("library_code"));
* }
* }</pre>
*/
CompletableFuture<Map<String, Object>[]> functionList(String libNamePattern, boolean withCode);
}
Loading

0 comments on commit f775561

Please sign in to comment.