From a1a40d9521f973e4f9e7daffdbb5a5dbc0d02d94 Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Thu, 18 Jul 2024 16:15:25 -0700 Subject: [PATCH 1/2] Node: add SETBIT command Signed-off-by: aaron-congo --- CHANGELOG.md | 1 + node/src/BaseClient.ts | 24 ++++++++++++++++++++++++ node/src/Commands.ts | 15 +++++++++++++++ node/src/Transaction.ts | 19 +++++++++++++++++++ node/tests/SharedTests.ts | 30 ++++++++++++++++++++++++++++++ node/tests/TestUtilities.ts | 5 +++++ 6 files changed, 94 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 911d93abc5..e10360d45d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ #### Changes * Node: Added GETDEL command ([#1968](https://github.com/valkey-io/valkey-glide/pull/1968)) +* Node: Added SETBIT command ([#1978](https://github.com/valkey-io/valkey-glide/pull/1978)) * Node: Added LPUSHX and RPUSHX command([#1959](https://github.com/valkey-io/valkey-glide/pull/1959)) * Node: Added LSET command ([#1952](https://github.com/valkey-io/valkey-glide/pull/1952)) * Node: Added SDIFFSTORE command ([#1931](https://github.com/valkey-io/valkey-glide/pull/1931)) diff --git a/node/src/BaseClient.ts b/node/src/BaseClient.ts index 99ae4af26a..95dcbcd0d2 100644 --- a/node/src/BaseClient.ts +++ b/node/src/BaseClient.ts @@ -81,6 +81,7 @@ import { createSCard, createSDiff, createSDiffStore, + createSetBit, createSInter, createSInterCard, createSInterStore, @@ -962,6 +963,29 @@ export class BaseClient { return this.createWritePromise(createDecrBy(key, amount)); } + /** + * Sets or clears the bit at `offset` in the string value stored at `key`. The `offset` is a zero-based index, with + * `0` being the first element of the list, `1` being the next element, and so on. The `offset` must be less than + * `2^32` and greater than or equal to `0`. If a key is non-existent then the bit at `offset` is set to `value` and + * the preceding bits are set to `0`. + * + * See https://valkey.io/commands/setbit/ for more details. + * + * @param key - The key of the string. + * @param offset - The index of the bit to be set. + * @param value - The bit value to set at `offset`. The value must be `0` or `1`. + * @returns The bit value that was previously stored at `offset`. + * + * @example + * ```typescript + * const result = await client.setbit("key", 1, 1); + * console.log(result); // Output: 0 - The second bit value was 0 before setting to 1. + * ``` + */ + public setbit(key: string, offset: number, value: number): Promise { + return this.createWritePromise(createSetBit(key, offset, value)); + } + /** Retrieve the value associated with `field` in the hash stored at `key`. * See https://valkey.io/commands/hget/ for details. * diff --git a/node/src/Commands.ts b/node/src/Commands.ts index 9fbf4ccf57..a487fe9a6f 100644 --- a/node/src/Commands.ts +++ b/node/src/Commands.ts @@ -442,6 +442,21 @@ export function createDecrBy( return createCommand(RequestType.DecrBy, [key, amount.toString()]); } +/** + * @internal + */ +export function createSetBit( + key: string, + offset: number, + value: number, +): command_request.Command { + return createCommand(RequestType.SetBit, [ + key.toString(), + offset.toString(), + value.toString(), + ]); +} + /** * @internal */ diff --git a/node/src/Transaction.ts b/node/src/Transaction.ts index 767b96cf55..65d53fd71e 100644 --- a/node/src/Transaction.ts +++ b/node/src/Transaction.ts @@ -89,6 +89,7 @@ import { createSCard, createSDiff, createSDiffStore, + createSetBit, createSInter, createSInterCard, createSInterStore, @@ -374,6 +375,24 @@ export class BaseTransaction> { return this.addAndReturn(createDecrBy(key, amount)); } + /** + * Sets or clears the bit at `offset` in the string value stored at `key`. The `offset` is a zero-based index, with + * `0` being the first element of the list, `1` being the next element, and so on. The `offset` must be less than + * `2^32` and greater than or equal to `0`. If a key is non-existent then the bit at `offset` is set to `value` and + * the preceding bits are set to `0`. + * + * See https://valkey.io/commands/setbit/ for more details. + * + * @param key - The key of the string. + * @param offset - The index of the bit to be set. + * @param value - The bit value to set at `offset`. The value must be `0` or `1`. + * + * Command Response - The bit value that was previously stored at `offset`. + */ + public setbit(key: string, offset: number, value: number): T { + return this.addAndReturn(createSetBit(key, offset, value)); + } + /** Reads the configuration parameters of a running Redis server. * See https://valkey.io/commands/config-get/ for details. * diff --git a/node/tests/SharedTests.ts b/node/tests/SharedTests.ts index c38534a88f..1ef8b47b8e 100644 --- a/node/tests/SharedTests.ts +++ b/node/tests/SharedTests.ts @@ -488,6 +488,36 @@ export function runBaseTests(config: { config.timeout, ); + it.each([ProtocolVersion.RESP2, ProtocolVersion.RESP3])( + `setbit test_%p`, + async (protocol) => { + await runTest(async (client: BaseClient) => { + const key = `{key}-${uuidv4()}`; + const stringKey = `{key}-${uuidv4()}`; + + expect(await client.setbit(key, 1, 1)).toEqual(0); + expect(await client.setbit(key, 1, 0)).toEqual(1); + + // invalid argument - offset can't be negative + await expect(client.setbit(key, -1, 1)).rejects.toThrow( + RequestError, + ); + + // invalid argument - "value" arg must be 0 or 1 + await expect(client.setbit(key, 0, 2)).rejects.toThrow( + RequestError, + ); + + // key exists, but it is not a string + expect(await client.sadd(stringKey, ["foo"])).toEqual(1); + await expect(client.setbit(stringKey, 0, 0)).rejects.toThrow( + RequestError, + ); + }, protocol); + }, + config.timeout, + ); + it.each([ProtocolVersion.RESP2, ProtocolVersion.RESP3])( `config get and config set with timeout parameter_%p`, async (protocol) => { diff --git a/node/tests/TestUtilities.ts b/node/tests/TestUtilities.ts index 43d1df5fe3..faffca4ccb 100644 --- a/node/tests/TestUtilities.ts +++ b/node/tests/TestUtilities.ts @@ -312,6 +312,7 @@ export async function transactionTest( const key14 = "{key}" + uuidv4(); // sorted set const key15 = "{key}" + uuidv4(); // list const key16 = "{key}" + uuidv4(); // list + const key17 = "{key}" + uuidv4(); // bitmap const field = uuidv4(); const value = uuidv4(); const args: ReturnType[] = []; @@ -566,6 +567,10 @@ export async function transactionTest( args.push([key6, field + "3"]); baseTransaction.blpop([key6], 0.1); args.push([key6, field + "1"]); + + baseTransaction.setbit(key17, 1, 1); + args.push(0); + baseTransaction.pfadd(key11, ["a", "b", "c"]); args.push(1); baseTransaction.pfcount([key11]); From 810bf56c0ce8d6abc40a7441f34c966a251db017 Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Fri, 19 Jul 2024 10:47:30 -0700 Subject: [PATCH 2/2] PR suggestions Signed-off-by: aaron-congo --- node/src/Commands.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node/src/Commands.ts b/node/src/Commands.ts index a487fe9a6f..538c51de1a 100644 --- a/node/src/Commands.ts +++ b/node/src/Commands.ts @@ -451,7 +451,7 @@ export function createSetBit( value: number, ): command_request.Command { return createCommand(RequestType.SetBit, [ - key.toString(), + key, offset.toString(), value.toString(), ]);