Skip to content

Commit

Permalink
added lua scripts support in node.
Browse files Browse the repository at this point in the history
  • Loading branch information
Adan committed Jan 10, 2024
1 parent 3034c88 commit ea6cb39
Show file tree
Hide file tree
Showing 4 changed files with 110 additions and 2 deletions.
6 changes: 4 additions & 2 deletions node/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,23 @@ export {
BaseClientConfiguration,
ProtocolVersion,
ReturnType,
ScriptOptions
} from "./src/BaseClient";
export {
ExpireOptions,
InfoOptions,
SetOptions,
parseInfoResponse,
parseInfoResponse
} from "./src/Commands";
export {
ClosingError,
ExecAbortError,
RedisError,
RequestError,
TimeoutError,
TimeoutError
} from "./src/Errors";
export { Logger } from "./src/Logger";
export { RedisClient } from "./src/RedisClient";
export { RedisClusterClient } from "./src/RedisClusterClient";
export { ScriptObject as Script } from "./src/Script";
export { ClusterTransaction, Transaction } from "./src/Transaction";
57 changes: 57 additions & 0 deletions node/src/BaseClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ import {
} from "./Errors";
import { Logger } from "./Logger";
import { connection_request, redis_request, response } from "./ProtobufMessage";
import { ScriptObject } from "./Script";

/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
type PromiseFunction = (value?: any) => void;
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 @@ -313,6 +325,41 @@ export class BaseClient {
});
}

/**
* @internal
*/
protected createScriptWritePromise<T>(
hash: string,
keys?: string[],
args?: string[],
): Promise<T> {
if (this.isClosed) {
throw new ClosingError(
"Unable to execute requests; the client is closed. Please create a new client."
);
}

return new Promise((resolve, reject) => {
const callbackIndex = this.getCallbackIndex();
this.promiseCallbackFunctions[callbackIndex] = [resolve, reject];
const message = redis_request.RedisRequest.create({
callbackIdx: callbackIndex,
scriptInvocation: redis_request.ScriptInvocation.create({
hash: hash,
keys: keys,
args: args
})
});

this.writeOrBufferRequest(
message,
(message: redis_request.RedisRequest, writer: Writer) => {
redis_request.RedisRequest.encodeDelimited(message, writer);
}
);
});
}

private writeOrBufferRedisRequest(
callbackIdx: number,
command: redis_request.Command | redis_request.Command[],
Expand Down Expand Up @@ -888,6 +935,16 @@ export class BaseClient {
return this.createWritePromise(createTTL(key));
}

/** Invoke the given script with its keys and arguments.
*
* @param script - The script to invoke.
* @param option - The script option that includes the script keys and arguments.
* @returns a value that depends on the script that was executed.
*/
public invokeScript(script: ScriptObject, option?: ScriptOptions): Promise<ReturnType> {
return this.createScriptWritePromise(script.getHash(), option?.keys, option?.args);
}

private readonly MAP_READ_FROM_STRATEGY: Record<
ReadFrom,
connection_request.ReadFrom
Expand Down
13 changes: 13 additions & 0 deletions node/src/Script.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { Script } from "glide-rs";

export class ScriptObject {
private script: Script;

constructor(script: string) {
this.script = new Script(script);
}

public getHash(): string{
return this.script.getHash();
}
}
36 changes: 36 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,41 @@ 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: ["bar"],
})
).toEqual("OK");

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

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

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

0 comments on commit ea6cb39

Please sign in to comment.