Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Java: Add FUNCTION LIST command. #1452

Merged
merged 15 commits into from
Jun 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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()));
aaron-congo marked this conversation as resolved.
Show resolved Hide resolved
}
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
Loading