Skip to content

Commit

Permalink
added lua scripts support in node. (#775)
Browse files Browse the repository at this point in the history
  • Loading branch information
Adan Wattad authored Jan 11, 2024
1 parent d885e86 commit 9d6010d
Show file tree
Hide file tree
Showing 3 changed files with 100 additions and 3 deletions.
2 changes: 2 additions & 0 deletions node/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
export { Script } from "glide-rs";
export {
BaseClientConfiguration,
ProtocolVersion,
ReturnType,
ScriptOptions,
} from "./src/BaseClient";
export {
ExpireOptions,
Expand Down
59 changes: 56 additions & 3 deletions node/src/BaseClient.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {
DEFAULT_TIMEOUT_IN_MILLISECONDS,
Script,
StartSocketConnection,
valueFromSplitPointer,
} from "glide-rs";
Expand Down Expand Up @@ -154,6 +155,17 @@ export type BaseClientConfiguration = {
clientName?: string;
};

export type ScriptOptions = {
/**
* The keys that are used in the script.
*/
keys?: string[];
/**
* The arguments for the script.
*/
args?: string[];
};

function getRequestErrorClass(
type: response.RequestErrorType | null | undefined
): typeof RequestError {
Expand Down Expand Up @@ -297,7 +309,10 @@ export class BaseClient {
* @internal
*/
protected createWritePromise<T>(
command: redis_request.Command | redis_request.Command[],
command:
| redis_request.Command
| redis_request.Command[]
| redis_request.ScriptInvocation,
route?: redis_request.Routes
): Promise<T> {
if (this.isClosed) {
Expand All @@ -315,7 +330,10 @@ export class BaseClient {

private writeOrBufferRedisRequest(
callbackIdx: number,
command: redis_request.Command | redis_request.Command[],
command:
| redis_request.Command
| redis_request.Command[]
| redis_request.ScriptInvocation,
route?: redis_request.Routes
) {
const message = Array.isArray(command)
Expand All @@ -325,9 +343,14 @@ export class BaseClient {
commands: command,
}),
})
: redis_request.RedisRequest.create({
: command instanceof redis_request.Command
? redis_request.RedisRequest.create({
callbackIdx,
singleCommand: command,
})
: redis_request.RedisRequest.create({
callbackIdx,
scriptInvocation: command,
});
message.route = route;

Expand Down Expand Up @@ -888,6 +911,36 @@ export class BaseClient {
return this.createWritePromise(createTTL(key));
}

/** Invokes a Lua script with its keys and arguments.
* This method simplifies the process of invoking scripts on a Redis server by using an object that represents a Lua script.
* The script loading, argument preparation, and execution will all be handled internally. If the script has not already been loaded,
* it will be loaded automatically using the Redis `SCRIPT LOAD` command. After that, it will be invoked using the Redis `EVALSHA` command
*
* @param script - The Lua script to execute.
* @param options - The script option that contains keys and arguments for the script.
* @returns a value that depends on the script that was executed.
*
* @example
* const luaScript = "return \{ KEYS[1], ARGV[1] \}";
* const scriptOptions = \{
* keys: ["foo"],
* args: ["bar"],
* \};
* await invokeScript(luaScript, scriptOptions);
* ["foo", "bar"]
*/
public invokeScript(
script: Script,
option?: ScriptOptions
): Promise<ReturnType> {
const scriptInvocation = redis_request.ScriptInvocation.create({
hash: script.getHash(),
keys: option?.keys,
args: option?.args,
});
return this.createWritePromise(scriptInvocation);
}

private readonly MAP_READ_FROM_STRATEGY: Record<
ReadFrom,
connection_request.ReadFrom
Expand Down
42 changes: 42 additions & 0 deletions node/tests/SharedTests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
ProtocolVersion,
RedisClient,
RedisClusterClient,
Script,
parseInfoResponse,
} from "../";
import { Client, GetAndSetRandomValue, getFirstResult } from "./TestUtilities";
Expand Down Expand Up @@ -1203,6 +1204,47 @@ export function runBaseTests<Context>(config: {
},
config.timeout
);

it(
"script test",
async () => {
await runTest(async (client: BaseClient) => {
const key1 = uuidv4();
const key2 = uuidv4();

let script = new Script("return 'Hello'");
expect(await client.invokeScript(script)).toEqual("Hello");

script = new Script(
"return redis.call('SET', KEYS[1], ARGV[1])"
);
expect(
await client.invokeScript(script, {
keys: [key1],
args: ["value1"],
})
).toEqual("OK");

/// Reuse the same script with different parameters.
expect(
await client.invokeScript(script, {
keys: [key2],
args: ["value2"],
})
).toEqual("OK");

script = new Script("return redis.call('GET', KEYS[1])");
expect(
await client.invokeScript(script, { keys: [key1] })
).toEqual("value1");

expect(
await client.invokeScript(script, { keys: [key2] })
).toEqual("value2");
});
},
config.timeout
);
}

export function runCommonTests<Context>(config: {
Expand Down

0 comments on commit 9d6010d

Please sign in to comment.