diff --git a/glide-core/src/protobuf/redis_request.proto b/glide-core/src/protobuf/redis_request.proto index 30f8e3c046..4731e6bd8d 100644 --- a/glide-core/src/protobuf/redis_request.proto +++ b/glide-core/src/protobuf/redis_request.proto @@ -107,6 +107,7 @@ enum RequestType { HLen = 69; Echo = 70; ZPopMin = 71; + Strlen = 72; } message Command { @@ -133,7 +134,7 @@ message Transaction { message RedisRequest { uint32 callback_idx = 1; - + oneof command { Command single_command = 2; Transaction transaction = 3; diff --git a/glide-core/src/socket_listener.rs b/glide-core/src/socket_listener.rs index 0090a1e20e..23329b7962 100644 --- a/glide-core/src/socket_listener.rs +++ b/glide-core/src/socket_listener.rs @@ -350,6 +350,7 @@ fn get_command(request: &Command) -> Option { RequestType::HLen => Some(cmd("HLEN")), RequestType::Echo => Some(cmd("ECHO")), RequestType::ZPopMin => Some(cmd("ZPOPMIN")), + RequestType::Strlen => Some(cmd("STRLEN")), } } diff --git a/node/src/BaseClient.ts b/node/src/BaseClient.ts index 742e646363..7e6542dc6e 100644 --- a/node/src/BaseClient.ts +++ b/node/src/BaseClient.ts @@ -50,6 +50,7 @@ import { createSMembers, createSRem, createSet, + createStrlen, createTTL, createType, createUnlink, @@ -57,7 +58,7 @@ import { createZcard, createZcount, createZrem, - createZscore, + createZscore } from "./Commands"; import { ClosingError, @@ -1066,9 +1067,23 @@ export class BaseClient { return this.createWritePromise(createZcount(key, minScore, maxScore)); } + /** Returns the length of the string value stored at key. An error is returned when key holds a non-string value. + * See https://redis.io/commands/strlen/ for more details. + * + * @param key - The key to check its length. + * @returns - The length of the string value stored at key + * If `key` does not exist, it is treated as an empty string, and the command returns 0. + * An error is returned when key holds a non-string value. + */ + public strlen( + key: string, + ): Promise { + return this.createWritePromise(createStrlen(key)); + } + /** Returns the string representation of the type of the value stored at `key`. * See https://redis.io/commands/type/ for more details. - * + * * @param key - The key to check its data type. * @returns If the key exists, the type of the stored value is returned. Otherwise, a "none" string is returned. */ diff --git a/node/src/Commands.ts b/node/src/Commands.ts index 9f6996312d..2e7bf511b2 100644 --- a/node/src/Commands.ts +++ b/node/src/Commands.ts @@ -813,9 +813,9 @@ export function createZcount( : maxScore.bound.toString(); args.push(value); } - + return createCommand(RequestType.Zcount, args); -} +} /** * @internal @@ -823,3 +823,10 @@ export function createZcount( export function createType(key: string): redis_request.Command { return createCommand(RequestType.Type, [key]); } + +/** + * @internal + */ +export function createStrlen(key: string): redis_request.Command { + return createCommand(RequestType.Strlen, [key]); +} diff --git a/node/src/Transaction.ts b/node/src/Transaction.ts index 691dc7b8e6..59bfa21f89 100644 --- a/node/src/Transaction.ts +++ b/node/src/Transaction.ts @@ -53,6 +53,7 @@ import { createSRem, createSelect, createSet, + createStrlen, createTTL, createType, createUnlink, @@ -60,7 +61,7 @@ import { createZcard, createZcount, createZrem, - createZscore, + createZscore } from "./Commands"; import { redis_request } from "./ProtobufMessage"; @@ -121,7 +122,7 @@ export class BaseTransaction> { /** Ping the Redis server. * See https://redis.io/commands/ping/ for details. * - * @param message - An optional message to include in the PING command. + * @param message - An optional message to include in the PING command. * If not provided, the server will respond with "PONG". * If provided, the server will respond with a copy of the message. * @@ -428,7 +429,7 @@ export class BaseTransaction> { * See https://redis.io/commands/lpop/ for details. * * @param key - The key of the list. - * + * * Command Response - The value of the first element. * If `key` does not exist null will be returned. */ @@ -441,7 +442,7 @@ export class BaseTransaction> { * * @param key - The key of the list. * @param count - The count of the elements to pop from the list. - * + * * Command Response - A list of the popped elements will be returned depending on the list's length. * If `key` does not exist null will be returned. */ @@ -534,7 +535,7 @@ export class BaseTransaction> { * See https://redis.io/commands/rpop/ for details. * * @param key - The key of the list. - * + * * Command Response - The value of the last element. * If `key` does not exist null will be returned. */ @@ -547,7 +548,7 @@ export class BaseTransaction> { * * @param key - The key of the list. * @param count - The count of the elements to pop from the list. - * + * * Command Response - A list of popped elements will be returned depending on the list's length. * If `key` does not exist null will be returned. */ @@ -831,15 +832,30 @@ export class BaseTransaction> { /** Returns the string representation of the type of the value stored at `key`. * See https://redis.io/commands/type/ for more details. - * + * * @param key - The key to check its data type. - * + * * Command Response - If the key exists, the type of the stored value is returned. Otherwise, a "none" string is returned. */ public type(key: string): T { return this.addAndReturn(createType(key)); } + /** Returns the length of the string value stored at key. + * An error is returned when key holds a non-string value. + * See https://redis.io/commands/strlen/ for more details. + * + * @param key - The key to check its length. + * @returns - The length of the string value stored at key + * If `key` does not exist, it is treated as an empty string, and the command returns 0. + * An error is returned when key holds a non-string value. + */ + public strlen( + key: string, + ): T { + return this.addAndReturn(createStrlen(key)); + } + /** Executes a single command, without checking inputs. Every part of the command, including subcommands, * should be added as a separate value in args. * diff --git a/node/tests/SharedTests.ts b/node/tests/SharedTests.ts index 6e6b047afc..b16456eaf1 100644 --- a/node/tests/SharedTests.ts +++ b/node/tests/SharedTests.ts @@ -1460,7 +1460,7 @@ export function runBaseTests(config: { "positiveInfinity" ) ).toEqual(0); - + expect(await client.set(key2, "foo")).toEqual("OK"); await expect( client.zcount(key2, "negativeInfinity", "positiveInfinity") @@ -1510,6 +1510,37 @@ export function runBaseTests(config: { }, config.timeout ); + + it.each([ProtocolVersion.RESP2, ProtocolVersion.RESP3])( + `strlen test_%p`, + async (protocol) => { + await runTest(async (client: BaseClient) => { + const key1 = uuidv4(); + const key1Value = uuidv4(); + const key1ValueLength = key1Value.length; + expect(await client.set(key1, key1Value)).toEqual("OK"); + expect(await client.strlen(key1)).toEqual(key1ValueLength); + + expect(await client.strlen("nonExistKey")).toEqual(0); + + const listName = "myList"; + const listKey1Value = uuidv4(); + const listKey2Value = uuidv4(); + + expect(await client.lpush(listName, [listKey1Value])).toEqual(1); + expect(await client.lpush(listName, [listKey2Value])).toEqual(2); + + try { + expect(await client.strlen(listName)).toThrow(); + } catch (e) { + expect((e as Error).message).toMatch( + "WRONGTYPE: Operation against a key holding the wrong kind of value" + ); + } + }, protocol); + }, + config.timeout + ); } export function runCommonTests(config: { diff --git a/node/tests/TestUtilities.ts b/node/tests/TestUtilities.ts index a4ff1df332..25df8a38f6 100644 --- a/node/tests/TestUtilities.ts +++ b/node/tests/TestUtilities.ts @@ -61,11 +61,14 @@ export function transactionTest( const key6 = "{key}" + uuidv4(); const key7 = "{key}" + uuidv4(); const key8 = "{key}" + uuidv4(); + const key9 = "{key}" + uuidv4(); const field = uuidv4(); const value = uuidv4(); const args: ReturnType[] = []; baseTransaction.set(key1, "bar"); args.push("OK"); + baseTransaction.set(key9, "bar"); + args.push("OK"); baseTransaction.type(key1); args.push("string"); baseTransaction.set(key2, "baz", { @@ -137,6 +140,8 @@ export function transactionTest( args.push(3.0); baseTransaction.zcount(key8, { bound: 2 }, "positiveInfinity"); args.push(1); + baseTransaction.strlen(key9); + args.push(3); return args; }