From faab94fab2262e26b44159646f7c99090da789a5 Mon Sep 17 00:00:00 2001 From: leibale Date: Wed, 23 Jun 2021 18:12:12 -0400 Subject: [PATCH] add support for List commands, fix some Sorted Set commands, add some cluster commands, spawn cluster for testing, add support for command options in cluster, and more --- lib/client.ts | 6 +-- lib/cluster-slots.ts | 46 +++++++++++++-------- lib/cluster.ts | 35 +++++++++------- lib/commands/BLMOVE.spec.ts | 42 +++++++++++++++++++ lib/commands/BLMOVE.ts | 23 +++++++++++ lib/commands/BLPOP.spec.ts | 26 ++++++++++-- lib/commands/BLPOP.ts | 16 ++++++-- lib/commands/BRPOP.spec.ts | 57 ++++++++++++++++++++++++++ lib/commands/BRPOP.ts | 31 ++++++++++++++ lib/commands/BRPOPLPUSH.spec.ts | 42 +++++++++++++++++++ lib/commands/BRPOPLPUSH.ts | 9 +++++ lib/commands/CLUSTER_RESET.spec.ts | 27 +++++++++++++ lib/commands/CLUSTER_RESET.ts | 15 +++++++ lib/commands/HRANDFIELD_COUNT.ts | 5 +-- lib/commands/LINDEX.spec.ts | 26 ++++++++++++ lib/commands/LINDEX.ts | 11 +++++ lib/commands/LINSERT.spec.ts | 26 ++++++++++++ lib/commands/LINSERT.ts | 22 ++++++++++ lib/commands/LLEN.spec.ts | 26 ++++++++++++ lib/commands/LLEN.ts | 11 +++++ lib/commands/LMOVE.spec.ts | 26 ++++++++++++ lib/commands/LMOVE.ts | 22 ++++++++++ lib/commands/LPOP.spec.ts | 26 ++++++++++++ lib/commands/LPOP.ts | 9 +++++ lib/commands/LPOP_COUNT.spec.ts | 26 ++++++++++++ lib/commands/LPOP_COUNT.ts | 9 +++++ lib/commands/LPUSH.spec.ts | 9 ++++- lib/commands/LPUSH.ts | 2 +- lib/commands/LPUSHX.spec.ts | 35 ++++++++++++++++ lib/commands/LPUSHX.ts | 20 ++++++++++ lib/commands/LRANGE.spec.ts | 27 +++++++++++++ lib/commands/LRANGE.ts | 16 ++++++++ lib/commands/LREM.spec.ts | 27 +++++++++++++ lib/commands/LREM.ts | 14 +++++++ lib/commands/LSET.spec.ts | 28 +++++++++++++ lib/commands/LSET.ts | 14 +++++++ lib/commands/LTRIM.spec.ts | 26 ++++++++++++ lib/commands/LTRIM.ts | 14 +++++++ lib/commands/RPOP.spec.ts | 26 ++++++++++++ lib/commands/RPOP.ts | 9 +++++ lib/commands/RPOPLPUSH.spec.ts | 26 ++++++++++++ lib/commands/RPOPLPUSH.ts | 9 +++++ lib/commands/RPOP_COUNT.spec.ts | 26 ++++++++++++ lib/commands/RPOP_COUNT.ts | 9 +++++ lib/commands/RPUSH.spec.ts | 35 ++++++++++++++++ lib/commands/RPUSH.ts | 20 ++++++++++ lib/commands/RPUSHX.spec.ts | 35 ++++++++++++++++ lib/commands/RPUSHX.ts | 20 ++++++++++ lib/commands/ZRANDMEMBER_COUNT.ts | 5 +-- lib/commands/ZRANGE.ts | 10 ++--- lib/commands/generic-transformers.ts | 8 ++++ lib/commands/index.ts | 60 ++++++++++++++++++++++++++++ 52 files changed, 1096 insertions(+), 54 deletions(-) create mode 100644 lib/commands/BLMOVE.spec.ts create mode 100644 lib/commands/BLMOVE.ts create mode 100644 lib/commands/BRPOP.spec.ts create mode 100644 lib/commands/BRPOP.ts create mode 100644 lib/commands/BRPOPLPUSH.spec.ts create mode 100644 lib/commands/BRPOPLPUSH.ts create mode 100644 lib/commands/CLUSTER_RESET.spec.ts create mode 100644 lib/commands/CLUSTER_RESET.ts create mode 100644 lib/commands/LINDEX.spec.ts create mode 100644 lib/commands/LINDEX.ts create mode 100644 lib/commands/LINSERT.spec.ts create mode 100644 lib/commands/LINSERT.ts create mode 100644 lib/commands/LLEN.spec.ts create mode 100644 lib/commands/LLEN.ts create mode 100644 lib/commands/LMOVE.spec.ts create mode 100644 lib/commands/LMOVE.ts create mode 100644 lib/commands/LPOP.spec.ts create mode 100644 lib/commands/LPOP.ts create mode 100644 lib/commands/LPOP_COUNT.spec.ts create mode 100644 lib/commands/LPOP_COUNT.ts create mode 100644 lib/commands/LPUSHX.spec.ts create mode 100644 lib/commands/LPUSHX.ts create mode 100644 lib/commands/LRANGE.spec.ts create mode 100644 lib/commands/LRANGE.ts create mode 100644 lib/commands/LREM.spec.ts create mode 100644 lib/commands/LREM.ts create mode 100644 lib/commands/LSET.spec.ts create mode 100644 lib/commands/LSET.ts create mode 100644 lib/commands/LTRIM.spec.ts create mode 100644 lib/commands/LTRIM.ts create mode 100644 lib/commands/RPOP.spec.ts create mode 100644 lib/commands/RPOP.ts create mode 100644 lib/commands/RPOPLPUSH.spec.ts create mode 100644 lib/commands/RPOPLPUSH.ts create mode 100644 lib/commands/RPOP_COUNT.spec.ts create mode 100644 lib/commands/RPOP_COUNT.ts create mode 100644 lib/commands/RPUSH.spec.ts create mode 100644 lib/commands/RPUSH.ts create mode 100644 lib/commands/RPUSHX.spec.ts create mode 100644 lib/commands/RPUSHX.ts diff --git a/lib/client.ts b/lib/client.ts index 5cce11ee64..b0365edb31 100644 --- a/lib/client.ts +++ b/lib/client.ts @@ -193,14 +193,14 @@ export default class RedisClient(script: S, args: Array): Promise> { + async executeScript(script: S, args: Array, options?: ClientCommandOptions): Promise> { try { return await this.sendCommand([ 'EVALSHA', script.SHA, script.NUMBER_OF_KEYS.toString(), ...args - ]); + ], options); } catch (err: any) { if (!err?.message?.startsWith?.('NOSCRIPT')) { throw err; @@ -211,7 +211,7 @@ export default class RedisClient; - iterator: IterableIterator | undefined; +interface SlotClients { + master: RedisClientType; + replicas: Array>; + iterator: IterableIterator> | undefined; } -export default class RedisClusterSlots { +export default class RedisClusterSlots { readonly #options: RedisClusterOptions; - readonly #clientByKey = new Map(); - readonly #slots: Array = []; + readonly #clientByKey = new Map>(); + readonly #slots: Array> = []; constructor(options: RedisClusterOptions) { this.#options = options; @@ -32,7 +34,7 @@ export default class RedisClusterSlots { throw new Error('None of the root nodes is available'); } - async discover(startWith: RedisClient): Promise { + async discover(startWith: RedisClientType): Promise { try { await this.#discoverNodes(startWith.options?.socket); return; @@ -99,7 +101,7 @@ export default class RedisClusterSlots { } } - #initiateClientForNode(node: RedisClusterMasterNode | RedisClusterReplicaNode, readonly: boolean, clientsInUse: Set, promises: Array>): RedisClient { + #initiateClientForNode(node: RedisClusterMasterNode | RedisClusterReplicaNode, readonly: boolean, clientsInUse: Set, promises: Array>): RedisClientType { clientsInUse.add(node.url); let client = this.#clientByKey.get(node.url); @@ -118,11 +120,11 @@ export default class RedisClusterSlots { return client; } - #getSlotMaster(slot: number): RedisClient { + #getSlotMaster(slot: number): RedisClientType { return this.#slots[slot].master; } - *#slotIterator(slotNumber: number): IterableIterator { + *#slotIterator(slotNumber: number): IterableIterator> { const slot = this.#slots[slotNumber]; yield slot.master; @@ -131,7 +133,7 @@ export default class RedisClusterSlots { } } - #getSlotClient(slotNumber: number): RedisClient { + #getSlotClient(slotNumber: number): RedisClientType { const slot = this.#slots[slotNumber]; if (!slot.iterator) { slot.iterator = this.#slotIterator(slotNumber); @@ -146,9 +148,9 @@ export default class RedisClusterSlots { return value; } - #randomClientIterator?: IterableIterator; + #randomClientIterator?: IterableIterator>; - #getRandomClient(): RedisClient { + #getRandomClient(): RedisClientType { if (!this.#clientByKey.size) { throw new Error('Cluster is not connected'); } @@ -166,7 +168,7 @@ export default class RedisClusterSlots { return value; } - getClient(firstKey?: string, isReadonly?: boolean): RedisClient { + getClient(firstKey?: string, isReadonly?: boolean): RedisClientType { if (!firstKey) { return this.#getRandomClient(); } @@ -179,6 +181,18 @@ export default class RedisClusterSlots { return this.#getSlotClient(slot); } + getMasters(): Array> { + const masters = []; + + for (const client of this.#clientByKey.values()) { + if (client.options?.readonly) continue; + + masters.push(client); + } + + return masters; + } + async disconnect(): Promise { await Promise.all( [...this.#clientByKey.values()].map(client => client.disconnect()) diff --git a/lib/cluster.ts b/lib/cluster.ts index 078298be4b..665f85c1eb 100644 --- a/lib/cluster.ts +++ b/lib/cluster.ts @@ -1,11 +1,10 @@ import COMMANDS from './commands'; import { RedisCommand, RedisModules } from './commands'; -import RedisClient, { WithPlugins } from './client'; +import { ClientCommandOptions, RedisClientType, WithPlugins } from './client'; import { RedisSocketOptions } from './socket'; import RedisClusterSlots from './cluster-slots'; import { RedisLuaScript, RedisLuaScripts } from './lua-script'; -import { QueueCommandOptions } from './commands-queue'; -import { CommandOptions, isCommandOptions } from './command-options'; +import { commandOptions, CommandOptions, isCommandOptions } from './command-options'; export interface RedisClusterOptions { rootNodes: Array; @@ -26,6 +25,7 @@ export default class RedisClusternew RedisCluster(options); } - static commandOptions(options: QueueCommandOptions): CommandOptions { - return this.commandOptions(options); + static commandOptions(options: ClientCommandOptions): CommandOptions { + return commandOptions(options); } readonly #options: RedisClusterOptions; - readonly #slots: RedisClusterSlots; + readonly #slots: RedisClusterSlots; constructor(options: RedisClusterOptions) { this.#options = options; @@ -68,7 +68,8 @@ export default class RedisCluster(command: C, args: Array, redirections: number = 0): Promise> { + async sendCommand(command: C, args: Array, options?: ClientCommandOptions, redirections: number = 0): Promise> { const client = this.#getClient(command, args); try { - return await client.sendCommand(args); + return await client.sendCommand(args, options); } catch (err) { if (await this.#handleCommandError(err, client, redirections)) { - return this.sendCommand(command, args, redirections + 1); + return this.sendCommand(command, args, options, redirections + 1); } throw err; } } - async executeScript(script: S, args: Array, redirections: number = 0): Promise> { + async executeScript(script: S, args: Array, options?: ClientCommandOptions, redirections: number = 0): Promise> { const client = this.#getClient(script, args); try { - return await client.executeScript(script, args); + return await client.executeScript(script, args, options); } catch (err) { if (await this.#handleCommandError(err, client, redirections)) { - return this.executeScript(script, args, redirections + 1); + return this.executeScript(script, args, options, redirections + 1); } throw err; } } - #getClient(commandOrScript: RedisCommand | RedisLuaScript, args: Array): RedisClient { + #getClient(commandOrScript: RedisCommand | RedisLuaScript, args: Array): RedisClientType { return this.#slots.getClient( commandOrScript.FIRST_KEY_INDEX ? args[commandOrScript.FIRST_KEY_INDEX] : undefined, commandOrScript.IS_READ_ONLY ); } - async #handleCommandError(err: Error, client: RedisClient, redirections: number = 0): Promise { + async #handleCommandError(err: Error, client: RedisClientType, redirections: number = 0): Promise { if (redirections < (this.#options.maxCommandRedirections ?? 16)) { throw err; } @@ -128,6 +129,10 @@ export default class RedisCluster> { + return this.#slots.getMasters(); + } + disconnect(): Promise { return this.#slots.disconnect(); } diff --git a/lib/commands/BLMOVE.spec.ts b/lib/commands/BLMOVE.spec.ts new file mode 100644 index 0000000000..8ff289d3c3 --- /dev/null +++ b/lib/commands/BLMOVE.spec.ts @@ -0,0 +1,42 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient, itWithCluster, TestRedisClusters } from '../test-utils'; +import { transformArguments } from './BLMOVE'; +import RedisClient from '../client'; +import RedisCluster from '../cluster'; + +describe('BLMOVE', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('source', 'destination', 'LEFT', 'RIGHT', 0), + ['BLMOVE', 'source', 'destination', 'LEFT', 'RIGHT', '0'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.blMove', async client => { + const [blMoveReply] = await Promise.all([ + client.blMove(RedisClient.commandOptions({ + duplicateConnection: true + }), 'source', 'destination', 'LEFT', 'RIGHT', 0), + client.lPush('source', 'element') + ]); + + assert.equal( + blMoveReply, + 'element' + ); + }); + + itWithCluster(TestRedisClusters.OPEN, 'cluster.blMove', async cluster => { + const [blMoveReply] = await Promise.all([ + cluster.blMove(RedisCluster.commandOptions({ + duplicateConnection: true + }), '{tag}source', '{tag}destination', 'LEFT', 'RIGHT', 0), + cluster.lPush('{tag}source', 'element') + ]); + + assert.equal( + blMoveReply, + 'element' + ); + }); +}); diff --git a/lib/commands/BLMOVE.ts b/lib/commands/BLMOVE.ts new file mode 100644 index 0000000000..74a2eed4aa --- /dev/null +++ b/lib/commands/BLMOVE.ts @@ -0,0 +1,23 @@ +import { transformReplyStringNull } from './generic-transformers'; +import { LMoveSide } from './LMOVE'; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments( + source: string, + destination: string, + sourceDirection: LMoveSide, + destinationDirection: LMoveSide, + timeout: number +): Array { + return [ + 'BLMOVE', + source, + destination, + sourceDirection, + destinationDirection, + timeout.toString() + ]; +} + +export const transformReply = transformReplyStringNull; diff --git a/lib/commands/BLPOP.spec.ts b/lib/commands/BLPOP.spec.ts index 237c28f29f..100ebf6154 100644 --- a/lib/commands/BLPOP.spec.ts +++ b/lib/commands/BLPOP.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import { TestRedisServers, itWithClient, itWithCluster, TestRedisClusters } from '../test-utils'; import { transformArguments } from './BLPOP'; import RedisClient from '../client'; @@ -25,12 +25,32 @@ describe('BLPOP', () => { client.blPop(RedisClient.commandOptions({ duplicateConnection: true }), 'key', 0), - client.lPush('key', ['1', '2']) + client.lPush('key', 'element') ]); assert.deepEqual( popReply, - ['key', '2'] + { + key: 'key', + element: 'element' + } + ); + }); + + itWithCluster(TestRedisClusters.OPEN, 'cluster.blPop', async cluster => { + const [popReply] = await Promise.all([ + cluster.blPop(RedisClient.commandOptions({ + duplicateConnection: true + }), 'key', 0), + cluster.lPush('key', 'element') + ]); + + assert.deepEqual( + popReply, + { + key: 'key', + element: 'element' + } ); }); }); diff --git a/lib/commands/BLPOP.ts b/lib/commands/BLPOP.ts index eaaac886c3..159e8f7092 100644 --- a/lib/commands/BLPOP.ts +++ b/lib/commands/BLPOP.ts @@ -1,4 +1,4 @@ -export const FIRST_KEY_INDEX = 0; +export const FIRST_KEY_INDEX = 1; export function transformArguments(keys: string | Array, timeout: number): Array { const args = ['BLPOP']; @@ -14,8 +14,16 @@ export function transformArguments(keys: string | Array, timeout: number return args; } -type BLPOPReply = [list: string, value: string]; +type BLPOPReply = null | { + key: string; + element: string; +}; -export function transformReply(reply: BLPOPReply): BLPOPReply { - return reply; +export function transformReply(reply: null | [string, string]): BLPOPReply { + if (reply === null) return null; + + return { + key: reply[0], + element: reply[1] + }; } diff --git a/lib/commands/BRPOP.spec.ts b/lib/commands/BRPOP.spec.ts new file mode 100644 index 0000000000..78eaa55706 --- /dev/null +++ b/lib/commands/BRPOP.spec.ts @@ -0,0 +1,57 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient, itWithCluster, TestRedisClusters } from '../test-utils'; +import { transformArguments } from './BRPOP'; +import RedisClient from '../client'; +import RedisCluster from '../cluster'; + +describe('BRPOP', () => { + describe('transformArguments', () => { + it('single', () => { + assert.deepEqual( + transformArguments('key', 0), + ['BRPOP', 'key', '0'] + ); + }); + + it('multiple', () => { + assert.deepEqual( + transformArguments(['key1', 'key2'], 0), + ['BRPOP', 'key1', 'key2', '0'] + ); + }); + }); + + itWithClient(TestRedisServers.OPEN, 'client.brPop', async client => { + const [brPopReply] = await Promise.all([ + client.brPop(RedisClient.commandOptions({ + duplicateConnection: true + }), 'key', 0), + client.lPush('key', 'element') + ]); + + assert.deepEqual( + brPopReply, + { + key: 'key', + element: 'element' + } + ); + }); + + itWithCluster(TestRedisClusters.OPEN, 'cluster.brPop', async cluster => { + const [brPopReply] = await Promise.all([ + cluster.brPop(RedisCluster.commandOptions({ + duplicateConnection: true + }), 'key', 0), + cluster.lPush('key', 'element') + ]); + + assert.deepEqual( + brPopReply, + { + key: 'key', + element: 'element' + } + ); + }); +}); diff --git a/lib/commands/BRPOP.ts b/lib/commands/BRPOP.ts new file mode 100644 index 0000000000..fa7165941e --- /dev/null +++ b/lib/commands/BRPOP.ts @@ -0,0 +1,31 @@ +import { transformReplyStringArray } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string | Array, timeout: number): Array { + const args = ['BRPOP']; + + if (typeof key === 'string') { + args.push(key); + } else { + args.push(...key); + } + + args.push(timeout.toString()); + + return args; +} + +type BRPOPReply = null | { + key: string; + element: string; +}; + +export function transformReply(reply: null | [string, string]): BRPOPReply { + if (reply === null) return null; + + return { + key: reply[0], + element: reply[1] + }; +} diff --git a/lib/commands/BRPOPLPUSH.spec.ts b/lib/commands/BRPOPLPUSH.spec.ts new file mode 100644 index 0000000000..e17f6b496b --- /dev/null +++ b/lib/commands/BRPOPLPUSH.spec.ts @@ -0,0 +1,42 @@ +import { strict as assert } from 'assert'; +import RedisClient from '../client'; +import RedisCluster from '../cluster'; +import { TestRedisServers, itWithClient, itWithCluster, TestRedisClusters } from '../test-utils'; +import { transformArguments } from './BRPOPLPUSH'; + +describe('BRPOPLPUSH', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('source', 'destination', 0), + ['BRPOPLPUSH', 'source', 'destination', '0'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.brPopLPush', async client => { + const [popReply] = await Promise.all([ + client.brPopLPush(RedisClient.commandOptions({ + duplicateConnection: true + }), 'source', 'destination', 0), + client.lPush('source', 'element') + ]); + + assert.equal( + popReply, + 'element' + ); + }); + + itWithCluster(TestRedisClusters.OPEN, 'cluster.brPopLPush', async cluster => { + const [popReply] = await Promise.all([ + cluster.brPopLPush(RedisCluster.commandOptions({ + duplicateConnection: true + }), '{tag}source', '{tag}destination', 0), + cluster.lPush('{tag}source', 'element') + ]); + + assert.equal( + popReply, + 'element' + ); + }); +}); diff --git a/lib/commands/BRPOPLPUSH.ts b/lib/commands/BRPOPLPUSH.ts new file mode 100644 index 0000000000..8bfcc69051 --- /dev/null +++ b/lib/commands/BRPOPLPUSH.ts @@ -0,0 +1,9 @@ +import { transformReplyNumber, transformReplyNumberNull } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(source: string, destination: string, timeout: number): Array { + return ['BRPOPLPUSH', source, destination, timeout.toString()]; +} + +export const transformReply = transformReplyNumberNull; diff --git a/lib/commands/CLUSTER_RESET.spec.ts b/lib/commands/CLUSTER_RESET.spec.ts new file mode 100644 index 0000000000..6e9c8958d8 --- /dev/null +++ b/lib/commands/CLUSTER_RESET.spec.ts @@ -0,0 +1,27 @@ +import { strict as assert } from 'assert'; +import { transformArguments } from './CLUSTER_RESET'; + +describe('CLUSTER RESET', () => { + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + transformArguments(), + ['CLUSTER', 'RESET'] + ); + }); + + it('simple', () => { + assert.deepEqual( + transformArguments('HARD'), + ['CLUSTER', 'RESET', 'HARD'] + ); + }); + + it('simple', () => { + assert.deepEqual( + transformArguments('SOFT'), + ['CLUSTER', 'RESET', 'SOFT'] + ); + }); + }); +}); diff --git a/lib/commands/CLUSTER_RESET.ts b/lib/commands/CLUSTER_RESET.ts new file mode 100644 index 0000000000..ec27b45eeb --- /dev/null +++ b/lib/commands/CLUSTER_RESET.ts @@ -0,0 +1,15 @@ +import { transformReplyString } from './generic-transformers'; + +export type ClusterResetModes = 'HARD' | 'SOFT'; + +export function transformArguments(mode?: ClusterResetModes): Array { + const args = ['CLUSTER', 'RESET']; + + if (mode) { + args.push(mode); + } + + return args; +} + +export const transformReply = transformReplyString; diff --git a/lib/commands/HRANDFIELD_COUNT.ts b/lib/commands/HRANDFIELD_COUNT.ts index 095435b99a..d0f5e90905 100644 --- a/lib/commands/HRANDFIELD_COUNT.ts +++ b/lib/commands/HRANDFIELD_COUNT.ts @@ -1,3 +1,4 @@ +import { transformReplyStringArrayNull } from './generic-transformers'; import { transformArguments as transformHRandFieldArguments } from './HRANDFIELD'; export { FIRST_KEY_INDEX } from './HRANDFIELD'; @@ -9,6 +10,4 @@ export function transformArguments(key: string, count: number): Array { ]; } -export function transformReply(reply: Array | null): Array | null { - return reply; -} +export const transformReply = transformReplyStringArrayNull; diff --git a/lib/commands/LINDEX.spec.ts b/lib/commands/LINDEX.spec.ts new file mode 100644 index 0000000000..74a6706ecd --- /dev/null +++ b/lib/commands/LINDEX.spec.ts @@ -0,0 +1,26 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient, itWithCluster, TestRedisClusters } from '../test-utils'; +import { transformArguments } from './LINDEX'; + +describe('LINDEX', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', 'element'), + ['LINDEX', 'key', 'element'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.lIndex', async client => { + assert.equal( + await client.lIndex('key', 'element'), + null + ); + }); + + itWithCluster(TestRedisClusters.OPEN, 'cluster.lIndex', async cluster => { + assert.equal( + await cluster.lIndex('key', 'element'), + null + ); + }); +}); diff --git a/lib/commands/LINDEX.ts b/lib/commands/LINDEX.ts new file mode 100644 index 0000000000..0237a4705b --- /dev/null +++ b/lib/commands/LINDEX.ts @@ -0,0 +1,11 @@ +import { transformReplyNumberNull } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export const IS_READ_ONLY = true; + +export function transformArguments(key: string, element: string): Array { + return ['LINDEX', key, element]; +} + +export const transformReply = transformReplyNumberNull; diff --git a/lib/commands/LINSERT.spec.ts b/lib/commands/LINSERT.spec.ts new file mode 100644 index 0000000000..286e61d06d --- /dev/null +++ b/lib/commands/LINSERT.spec.ts @@ -0,0 +1,26 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient, itWithCluster, TestRedisClusters } from '../test-utils'; +import { transformArguments } from './LINSERT'; + +describe('LINSERT', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', 'BEFORE', 'pivot', 'element'), + ['LINSERT', 'key', 'BEFORE', 'pivot', 'element'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.lInsert', async client => { + assert.equal( + await client.lInsert('key', 'BEFORE', 'pivot', 'element'), + 0 + ); + }); + + itWithCluster(TestRedisClusters.OPEN, 'cluster.lLen', async cluster => { + assert.equal( + await cluster.lInsert('key', 'BEFORE', 'pivot', 'element'), + 0 + ); + }); +}); diff --git a/lib/commands/LINSERT.ts b/lib/commands/LINSERT.ts new file mode 100644 index 0000000000..40bd4e3d4d --- /dev/null +++ b/lib/commands/LINSERT.ts @@ -0,0 +1,22 @@ +import { transformReplyNumber } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +type LInsertPosition = 'BEFORE' | 'AFTER'; + +export function transformArguments( + key: string, + position: LInsertPosition, + pivot: string, + element: string +): Array { + return [ + 'LINSERT', + key, + position, + pivot, + element + ]; +} + +export const transformReply = transformReplyNumber; diff --git a/lib/commands/LLEN.spec.ts b/lib/commands/LLEN.spec.ts new file mode 100644 index 0000000000..6e4581ddd1 --- /dev/null +++ b/lib/commands/LLEN.spec.ts @@ -0,0 +1,26 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient, itWithCluster, TestRedisClusters } from '../test-utils'; +import { transformArguments } from './LLEN'; + +describe('LLEN', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key'), + ['LLEN', 'key'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.lLen', async client => { + assert.equal( + await client.lLen('key'), + 0 + ); + }); + + itWithCluster(TestRedisClusters.OPEN, 'cluster.lLen', async cluster => { + assert.equal( + await cluster.lLen('key'), + 0 + ); + }); +}); diff --git a/lib/commands/LLEN.ts b/lib/commands/LLEN.ts new file mode 100644 index 0000000000..61aae604c9 --- /dev/null +++ b/lib/commands/LLEN.ts @@ -0,0 +1,11 @@ +import { transformReplyNumber } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export const IS_READ_ONLY = true; + +export function transformArguments(key: string): Array { + return ['LLEN', key]; +} + +export const transformReply = transformReplyNumber; \ No newline at end of file diff --git a/lib/commands/LMOVE.spec.ts b/lib/commands/LMOVE.spec.ts new file mode 100644 index 0000000000..94d7be2076 --- /dev/null +++ b/lib/commands/LMOVE.spec.ts @@ -0,0 +1,26 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient, itWithCluster, TestRedisClusters } from '../test-utils'; +import { transformArguments } from './LMOVE'; + +describe('LMOVE', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('source', 'destination', 'LEFT', 'RIGHT'), + ['LMOVE', 'source', 'destination', 'LEFT', 'RIGHT'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.lMove', async client => { + assert.equal( + await client.lMove('source', 'destination', 'LEFT', 'RIGHT'), + null + ); + }); + + itWithCluster(TestRedisClusters.OPEN, 'cluster.lMove', async cluster => { + assert.equal( + await cluster.lMove('{tag}source', '{tag}destination', 'LEFT', 'RIGHT'), + null + ); + }); +}); diff --git a/lib/commands/LMOVE.ts b/lib/commands/LMOVE.ts new file mode 100644 index 0000000000..1e99297d81 --- /dev/null +++ b/lib/commands/LMOVE.ts @@ -0,0 +1,22 @@ +import { transformReplyStringNull } from './generic-transformers'; + +export type LMoveSide = 'LEFT' | 'RIGHT'; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments( + source: string, + destination: string, + sourceSide: LMoveSide, + destinationSide: LMoveSide +): Array { + return [ + 'LMOVE', + source, + destination, + sourceSide, + destinationSide, + ]; +} + +export const transformReply = transformReplyStringNull; diff --git a/lib/commands/LPOP.spec.ts b/lib/commands/LPOP.spec.ts new file mode 100644 index 0000000000..b593f65742 --- /dev/null +++ b/lib/commands/LPOP.spec.ts @@ -0,0 +1,26 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient, itWithCluster, TestRedisClusters } from '../test-utils'; +import { transformArguments } from './LPOP'; + +describe('LPOP', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key'), + ['LPOP', 'key'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.lPop', async client => { + assert.equal( + await client.lPop('key'), + null + ); + }); + + itWithCluster(TestRedisClusters.OPEN, 'cluster.lPop', async cluster => { + assert.equal( + await cluster.lPop('key'), + null + ); + }); +}); diff --git a/lib/commands/LPOP.ts b/lib/commands/LPOP.ts new file mode 100644 index 0000000000..30595a5491 --- /dev/null +++ b/lib/commands/LPOP.ts @@ -0,0 +1,9 @@ +import { transformReplyStringNull } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string): Array { + return ['LPOP', key]; +} + +export const transformReply = transformReplyStringNull; diff --git a/lib/commands/LPOP_COUNT.spec.ts b/lib/commands/LPOP_COUNT.spec.ts new file mode 100644 index 0000000000..6d28505ccc --- /dev/null +++ b/lib/commands/LPOP_COUNT.spec.ts @@ -0,0 +1,26 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient, itWithCluster, TestRedisClusters } from '../test-utils'; +import { transformArguments } from './LPOP_COUNT'; + +describe('LPOP COUNT', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', 1), + ['LPOP', 'key', '1'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.lPopCount', async client => { + assert.equal( + await client.lPopCount('key', 1), + null + ); + }); + + itWithCluster(TestRedisClusters.OPEN, 'cluster.lPop', async cluster => { + assert.equal( + await cluster.lPopCount('key', 1), + null + ); + }); +}); diff --git a/lib/commands/LPOP_COUNT.ts b/lib/commands/LPOP_COUNT.ts new file mode 100644 index 0000000000..432d2c47c0 --- /dev/null +++ b/lib/commands/LPOP_COUNT.ts @@ -0,0 +1,9 @@ +import { transformReplyStringArrayNull } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string, count: number): Array { + return ['LPOP', key, count.toString()]; +} + +export const transformReply = transformReplyStringArrayNull; diff --git a/lib/commands/LPUSH.spec.ts b/lib/commands/LPUSH.spec.ts index fdc2012b78..44cf8c12d5 100644 --- a/lib/commands/LPUSH.spec.ts +++ b/lib/commands/LPUSH.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { TestRedisServers, itWithClient } from '../test-utils'; +import { TestRedisServers, itWithClient, itWithCluster, TestRedisClusters } from '../test-utils'; import { transformArguments } from './LPUSH'; describe('LPUSH', () => { @@ -25,4 +25,11 @@ describe('LPUSH', () => { 1 ); }); + + itWithCluster(TestRedisClusters.OPEN, 'cluster.lPush', async cluster => { + assert.equal( + await cluster.lPush('key', 'field'), + 1 + ); + }); }); diff --git a/lib/commands/LPUSH.ts b/lib/commands/LPUSH.ts index 8b2699793b..4bc795b5a3 100644 --- a/lib/commands/LPUSH.ts +++ b/lib/commands/LPUSH.ts @@ -1,6 +1,6 @@ import { transformReplyNumber } from './generic-transformers'; -export const FIRST_KEY_INDEX = 0; +export const FIRST_KEY_INDEX = 1; export function transformArguments(key: string, elements: string | Array): Array { const args = [ diff --git a/lib/commands/LPUSHX.spec.ts b/lib/commands/LPUSHX.spec.ts new file mode 100644 index 0000000000..1150c4d64d --- /dev/null +++ b/lib/commands/LPUSHX.spec.ts @@ -0,0 +1,35 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient, itWithCluster, TestRedisClusters } from '../test-utils'; +import { transformArguments } from './LPUSHX'; + +describe('LPUSHX', () => { + describe('transformArguments', () => { + it('string', () => { + assert.deepEqual( + transformArguments('key', 'element'), + ['LPUSHX', 'key', 'element'] + ); + }); + + it('array', () => { + assert.deepEqual( + transformArguments('key', ['1', '2']), + ['LPUSHX', 'key', '1', '2'] + ); + }); + }); + + itWithClient(TestRedisServers.OPEN, 'client.lPushX', async client => { + assert.equal( + await client.lPushX('key', 'element'), + 0 + ); + }); + + itWithCluster(TestRedisClusters.OPEN, 'cluster.lPushX', async cluster => { + assert.equal( + await cluster.lPushX('key', 'element'), + 0 + ); + }); +}); diff --git a/lib/commands/LPUSHX.ts b/lib/commands/LPUSHX.ts new file mode 100644 index 0000000000..b54757f970 --- /dev/null +++ b/lib/commands/LPUSHX.ts @@ -0,0 +1,20 @@ +import { transformReplyNumber } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string, element: string | Array): Array { + const args = [ + 'LPUSHX', + key + ]; + + if (typeof element === 'string') { + args.push(element); + } else { + args.push(...element); + } + + return args; +} + +export const transformReply = transformReplyNumber; diff --git a/lib/commands/LRANGE.spec.ts b/lib/commands/LRANGE.spec.ts new file mode 100644 index 0000000000..843b7b8815 --- /dev/null +++ b/lib/commands/LRANGE.spec.ts @@ -0,0 +1,27 @@ + +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient, itWithCluster, TestRedisClusters } from '../test-utils'; +import { transformArguments } from './LRANGE'; + +describe('LRANGE', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', 0, -1), + ['LRANGE', 'key', '0', '-1'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.lRange', async client => { + assert.deepEqual( + await client.lRange('key', 0, -1), + [] + ); + }); + + itWithCluster(TestRedisClusters.OPEN, 'cluster.lRange', async cluster => { + assert.deepEqual( + await cluster.lRange('key', 0, -1), + [] + ); + }); +}); diff --git a/lib/commands/LRANGE.ts b/lib/commands/LRANGE.ts new file mode 100644 index 0000000000..cbed9a75de --- /dev/null +++ b/lib/commands/LRANGE.ts @@ -0,0 +1,16 @@ +import { transformReplyStringArray } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export const IS_READ_ONLY = true; + +export function transformArguments(key: string, start: number, stop: number): Array { + return [ + 'LRANGE', + key, + start.toString(), + stop.toString() + ]; +} + +export const transformReply = transformReplyStringArray; diff --git a/lib/commands/LREM.spec.ts b/lib/commands/LREM.spec.ts new file mode 100644 index 0000000000..e2f027ffeb --- /dev/null +++ b/lib/commands/LREM.spec.ts @@ -0,0 +1,27 @@ + +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient, itWithCluster, TestRedisClusters } from '../test-utils'; +import { transformArguments } from './LREM'; + +describe('LREM', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', 0, 'element'), + ['LREM', 'key', '0', 'element'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.lRem', async client => { + assert.equal( + await client.lRem('key', 0, 'element'), + 0 + ); + }); + + itWithCluster(TestRedisClusters.OPEN, 'cluster.lRem', async cluster => { + assert.equal( + await cluster.lRem('key', 0, 'element'), + 0 + ); + }); +}); diff --git a/lib/commands/LREM.ts b/lib/commands/LREM.ts new file mode 100644 index 0000000000..5eabbc9194 --- /dev/null +++ b/lib/commands/LREM.ts @@ -0,0 +1,14 @@ +import { transformReplyNumber } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string, count: number, element: string): Array { + return [ + 'LREM', + key, + count.toString(), + element + ]; +} + +export const transformReply = transformReplyNumber; diff --git a/lib/commands/LSET.spec.ts b/lib/commands/LSET.spec.ts new file mode 100644 index 0000000000..a5fe78cf4c --- /dev/null +++ b/lib/commands/LSET.spec.ts @@ -0,0 +1,28 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient, itWithCluster, TestRedisClusters } from '../test-utils'; +import { transformArguments } from './LSET'; + +describe('LSET', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', 0, 'element'), + ['LSET', 'key', '0', 'element'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.lSet', async client => { + await client.lPush('key', 'element'); + assert.equal( + await client.lSet('key', 0, 'element'), + 'OK' + ); + }); + + itWithCluster(TestRedisClusters.OPEN, 'cluster.lSet', async cluster => { + await cluster.lPush('key', 'element'); + assert.equal( + await cluster.lSet('key', 0, 'element'), + 'OK' + ); + }); +}); diff --git a/lib/commands/LSET.ts b/lib/commands/LSET.ts new file mode 100644 index 0000000000..0e910dd6a1 --- /dev/null +++ b/lib/commands/LSET.ts @@ -0,0 +1,14 @@ +import { transformReplyString } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string, index: number, element: string): Array { + return [ + 'LSET', + key, + index.toString(), + element + ]; +} + +export const transformReply = transformReplyString; diff --git a/lib/commands/LTRIM.spec.ts b/lib/commands/LTRIM.spec.ts new file mode 100644 index 0000000000..8092ba6af1 --- /dev/null +++ b/lib/commands/LTRIM.spec.ts @@ -0,0 +1,26 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient, itWithCluster, TestRedisClusters } from '../test-utils'; +import { transformArguments } from './LTRIM'; + +describe('LTRIM', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', 0, -1), + ['LTRIM', 'key', '0', '-1'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.lTrim', async client => { + assert.equal( + await client.lTrim('key', 0, -1), + 'OK' + ); + }); + + itWithCluster(TestRedisClusters.OPEN, 'cluster.lTrim', async cluster => { + assert.equal( + await cluster.lTrim('key', 0, -1), + 'OK' + ); + }); +}); diff --git a/lib/commands/LTRIM.ts b/lib/commands/LTRIM.ts new file mode 100644 index 0000000000..3ccfa751af --- /dev/null +++ b/lib/commands/LTRIM.ts @@ -0,0 +1,14 @@ +import { transformReplyString } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string, start: number, stop: number): Array { + return [ + 'LTRIM', + key, + start.toString(), + stop.toString() + ] +} + +export const transformReply = transformReplyString; diff --git a/lib/commands/RPOP.spec.ts b/lib/commands/RPOP.spec.ts new file mode 100644 index 0000000000..2a753ff1a6 --- /dev/null +++ b/lib/commands/RPOP.spec.ts @@ -0,0 +1,26 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient, itWithCluster, TestRedisClusters } from '../test-utils'; +import { transformArguments } from './RPOP'; + +describe('RPOP', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key'), + ['RPOP', 'key'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.rPop', async client => { + assert.equal( + await client.rPop('key'), + null + ); + }); + + itWithCluster(TestRedisClusters.OPEN, 'cluster.rPop', async cluster => { + assert.equal( + await cluster.rPop('key'), + null + ); + }); +}); diff --git a/lib/commands/RPOP.ts b/lib/commands/RPOP.ts new file mode 100644 index 0000000000..daccbf5d42 --- /dev/null +++ b/lib/commands/RPOP.ts @@ -0,0 +1,9 @@ +import { transformReplyStringNull } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string): Array { + return ['RPOP', key]; +} + +export const transformReply = transformReplyStringNull; diff --git a/lib/commands/RPOPLPUSH.spec.ts b/lib/commands/RPOPLPUSH.spec.ts new file mode 100644 index 0000000000..75b5f2e18f --- /dev/null +++ b/lib/commands/RPOPLPUSH.spec.ts @@ -0,0 +1,26 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient, itWithCluster, TestRedisClusters } from '../test-utils'; +import { transformArguments } from './RPOPLPUSH'; + +describe('RPOPLPUSH', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('source', 'destination'), + ['RPOPLPUSH', 'source', 'destination'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.rPopLPush', async client => { + assert.equal( + await client.rPopLPush('source', 'destination'), + null + ); + }); + + itWithCluster(TestRedisClusters.OPEN, 'cluster.rPopLPush', async cluster => { + assert.equal( + await cluster.rPopLPush('{tag}source', '{tag}destination'), + null + ); + }); +}); diff --git a/lib/commands/RPOPLPUSH.ts b/lib/commands/RPOPLPUSH.ts new file mode 100644 index 0000000000..db388906d3 --- /dev/null +++ b/lib/commands/RPOPLPUSH.ts @@ -0,0 +1,9 @@ +import { transformReplyNumberNull } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(source: string, destination: string): Array { + return ['RPOPLPUSH', source, destination]; +} + +export const transformReply = transformReplyNumberNull; diff --git a/lib/commands/RPOP_COUNT.spec.ts b/lib/commands/RPOP_COUNT.spec.ts new file mode 100644 index 0000000000..87d52b9f00 --- /dev/null +++ b/lib/commands/RPOP_COUNT.spec.ts @@ -0,0 +1,26 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient, itWithCluster, TestRedisClusters } from '../test-utils'; +import { transformArguments } from './RPOP_COUNT'; + +describe('RPOP COUNT', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments('key', 1), + ['RPOP', 'key', '1'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.rPopCount', async client => { + assert.equal( + await client.rPopCount('key', 1), + null + ); + }); + + itWithCluster(TestRedisClusters.OPEN, 'cluster.rPopCount', async cluster => { + assert.equal( + await cluster.rPopCount('key', 1), + null + ); + }); +}); diff --git a/lib/commands/RPOP_COUNT.ts b/lib/commands/RPOP_COUNT.ts new file mode 100644 index 0000000000..205704274f --- /dev/null +++ b/lib/commands/RPOP_COUNT.ts @@ -0,0 +1,9 @@ +import { transformReplyStringArrayNull } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string, count: number): Array { + return ['RPOP', key, count.toString()]; +} + +export const transformReply = transformReplyStringArrayNull; diff --git a/lib/commands/RPUSH.spec.ts b/lib/commands/RPUSH.spec.ts new file mode 100644 index 0000000000..4336d10c9a --- /dev/null +++ b/lib/commands/RPUSH.spec.ts @@ -0,0 +1,35 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient, itWithCluster, TestRedisClusters } from '../test-utils'; +import { transformArguments } from './RPUSH'; + +describe('RPUSH', () => { + describe('transformArguments', () => { + it('string', () => { + assert.deepEqual( + transformArguments('key', 'element'), + ['RPUSH', 'key', 'element'] + ); + }); + + it('array', () => { + assert.deepEqual( + transformArguments('key', ['1', '2']), + ['RPUSH', 'key', '1', '2'] + ); + }); + }); + + itWithClient(TestRedisServers.OPEN, 'client.rPush', async client => { + assert.equal( + await client.rPush('key', 'element'), + 1 + ); + }); + + itWithCluster(TestRedisClusters.OPEN, 'cluster.rPush', async cluster => { + assert.equal( + await cluster.rPush('key', 'element'), + 1 + ); + }); +}); diff --git a/lib/commands/RPUSH.ts b/lib/commands/RPUSH.ts new file mode 100644 index 0000000000..e2434a709f --- /dev/null +++ b/lib/commands/RPUSH.ts @@ -0,0 +1,20 @@ +import { transformReplyNumber } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string, element: string | Array): Array { + const args = [ + 'RPUSH', + key + ]; + + if (typeof element === 'string') { + args.push(element); + } else { + args.push(...element); + } + + return args; +} + +export const transformReply = transformReplyNumber; diff --git a/lib/commands/RPUSHX.spec.ts b/lib/commands/RPUSHX.spec.ts new file mode 100644 index 0000000000..18f91e8bef --- /dev/null +++ b/lib/commands/RPUSHX.spec.ts @@ -0,0 +1,35 @@ +import { strict as assert } from 'assert'; +import { TestRedisServers, itWithClient, itWithCluster, TestRedisClusters } from '../test-utils'; +import { transformArguments } from './RPUSHX'; + +describe('RPUSHX', () => { + describe('transformArguments', () => { + it('string', () => { + assert.deepEqual( + transformArguments('key', 'element'), + ['RPUSHX', 'key', 'element'] + ); + }); + + it('array', () => { + assert.deepEqual( + transformArguments('key', ['1', '2']), + ['RPUSHX', 'key', '1', '2'] + ); + }); + }); + + itWithClient(TestRedisServers.OPEN, 'client.rPushX', async client => { + assert.equal( + await client.rPushX('key', 'element'), + 0 + ); + }); + + itWithCluster(TestRedisClusters.OPEN, 'cluster.rPushX', async cluster => { + assert.equal( + await cluster.rPushX('key', 'element'), + 0 + ); + }); +}); diff --git a/lib/commands/RPUSHX.ts b/lib/commands/RPUSHX.ts new file mode 100644 index 0000000000..4b14f29253 --- /dev/null +++ b/lib/commands/RPUSHX.ts @@ -0,0 +1,20 @@ +import { transformReplyNumber } from './generic-transformers'; + +export const FIRST_KEY_INDEX = 1; + +export function transformArguments(key: string, element: string | Array): Array { + const args = [ + 'RPUSHX', + key + ]; + + if (typeof element === 'string') { + args.push(element); + } else { + args.push(...element); + } + + return args; +} + +export const transformReply = transformReplyNumber; diff --git a/lib/commands/ZRANDMEMBER_COUNT.ts b/lib/commands/ZRANDMEMBER_COUNT.ts index a03356e0e7..2363b69e99 100644 --- a/lib/commands/ZRANDMEMBER_COUNT.ts +++ b/lib/commands/ZRANDMEMBER_COUNT.ts @@ -1,3 +1,4 @@ +import { transformReplyStringArrayNull } from './generic-transformers'; import { transformArguments as transformZRandMemberArguments } from './ZRANDMEMBER'; export { FIRST_KEY_INDEX, IS_READ_ONLY } from './ZRANDMEMBER'; @@ -9,6 +10,4 @@ export function transformArguments(key: string, count: number): Array { ]; } -export function transformReply(reply: Array | null): Array | null { - return reply; -} +export const transformReply = transformReplyStringArrayNull; diff --git a/lib/commands/ZRANGE.ts b/lib/commands/ZRANGE.ts index 49d54fc73f..9037210d69 100644 --- a/lib/commands/ZRANGE.ts +++ b/lib/commands/ZRANGE.ts @@ -1,4 +1,4 @@ -import { transformReplyStringArray } from './generic-transformers'; +import { transformArgumentNumberInfinity, transformReplyStringArray } from './generic-transformers'; export const FIRST_KEY_INDEX = 1; @@ -13,12 +13,12 @@ interface ZRangeOptions { }; } -export function transformArguments(src: string, min: string | number, max: string | number, options?: ZRangeOptions): Array { +export function transformArguments(key: string, min: string | number, max: string | number, options?: ZRangeOptions): Array { const args = [ 'ZRANGE', - src, - typeof min === 'string' ? min : min.toString(), - typeof max === 'string' ? max : max.toString() + key, + typeof min === 'string' ? min : transformArgumentNumberInfinity(min), + typeof max === 'string' ? max : transformArgumentNumberInfinity(max) ]; switch (options?.BY) { diff --git a/lib/commands/generic-transformers.ts b/lib/commands/generic-transformers.ts index 76e76e0151..eb48f261f2 100644 --- a/lib/commands/generic-transformers.ts +++ b/lib/commands/generic-transformers.ts @@ -2,6 +2,10 @@ export function transformReplyNumber(reply: number): number { return reply; } +export function transformReplyNumberNull(reply: number | null): number | null { + return reply; +} + export function transformReplyString(reply: string): string { return reply; } @@ -14,6 +18,10 @@ export function transformReplyStringArray(reply: Array): Array { return reply; } +export function transformReplyStringArrayNull(reply: Array | null): Array | null { + return reply; +} + export function transformReplyBoolean(reply: number): boolean { return reply === 1; } diff --git a/lib/commands/index.ts b/lib/commands/index.ts index 3317a1e602..fe348e5b0f 100644 --- a/lib/commands/index.ts +++ b/lib/commands/index.ts @@ -2,7 +2,10 @@ import * as APPEND from './APPEND'; import * as AUTH from './AUTH'; import * as BITCOUNT from './BITCOUNT'; import * as BITFIELD from './BITFIELD'; +import * as BLMOVE from './BLMOVE'; import * as BLPOP from './BLPOP'; +import * as BRPOP from './BRPOP'; +import * as BRPOPLPUSH from './BRPOPLPUSH'; import * as BZPOPMAX from './BZPOPMAX'; import * as BZPOPMIN from './BZPOPMIN'; import * as CLIENT_INFO from './CLIENT_INFO'; @@ -11,6 +14,7 @@ import * as CLUSTER_FLUSHSLOTS from './CLUSTER_FLUSHSLOTS'; import * as CLUSTER_INFO from './CLUSTER_INFO'; import * as CLUSTER_NODES from './CLUSTER_NODES'; import * as CLUSTER_MEET from './CLUSTER_MEET'; +import * as CLUSTER_RESET from './CLUSTER_RESET'; import * as COPY from './COPY'; import * as DECR from './DECR'; import * as DECRBY from './DECRBY'; @@ -41,7 +45,18 @@ import * as INCR from './INCR'; import * as INCRBY from './INCRBY'; import * as INCRBYFLOAT from './INCRBYFLOAT'; import * as KEYS from './KEYS'; +import * as LINDEX from './LINDEX'; +import * as LINSERT from './LINSERT'; +import * as LLEN from './LLEN'; +import * as LMOVE from './LMOVE'; +import * as LPOP from './LPOP'; +import * as LPOP_COUNT from './LPOP_COUNT'; import * as LPUSH from './LPUSH'; +import * as LPUSHX from './LPUSHX'; +import * as LRANGE from './LRANGE'; +import * as LREM from './LREM'; +import * as LSET from './LSET'; +import * as LTRIM from './LTRIM'; import * as MOVE from './MOVE'; import * as PERSIST from './PERSIST'; import * as PEXPIRE from './PEXPIRE'; @@ -56,6 +71,11 @@ import * as RANDOMKEY from './RANDOMKEY'; import * as READONLY from './READONLY'; import * as RENAME from './RENAME'; import * as RENAMENX from './RENAMENX'; +import * as RPOP_COUNT from './RPOP_COUNT'; +import * as RPOP from './RPOP'; +import * as RPOPLPUSH from './RPOPLPUSH'; +import * as RPUSH from './RPUSH'; +import * as RPUSHX from './RPUSHX'; import * as SADD from './SADD'; import * as SCAN from './SCAN'; import * as SCARD from './SCARD'; @@ -145,8 +165,14 @@ export default { bitCount: BITCOUNT, BITFIELD, bitField: BITFIELD, + BLMOVE, + blMove: BLMOVE, BLPOP, blPop: BLPOP, + BRPOP, + brPop: BRPOP, + BRPOPLPUSH, + brPopLPush: BRPOPLPUSH, BZPOPMAX, bzPopMax: BZPOPMAX, BZPOPMIN, @@ -163,6 +189,8 @@ export default { clusterNodes: CLUSTER_NODES, CLUSTER_MEET, clusterMeet: CLUSTER_MEET, + CLUSTER_RESET, + clusterReset: CLUSTER_RESET, COPY, copy: COPY, DECR, @@ -223,8 +251,30 @@ export default { incrByFloat: INCRBYFLOAT, KEYS, keys: KEYS, + LINDEX, + lIndex: LINDEX, + LINSERT, + lInsert: LINSERT, + LLEN, + lLen: LLEN, + LMOVE, + lMove: LMOVE, + LPOP_COUNT, + lPopCount: LPOP_COUNT, + LPOP, + lPop: LPOP, LPUSH, lPush: LPUSH, + LPUSHX, + lPushX: LPUSHX, + LRANGE, + lRange: LRANGE, + LREM, + lRem: LREM, + LSET, + lSet: LSET, + LTRIM, + lTrim: LTRIM, MOVE, move: MOVE, PERSIST, @@ -253,6 +303,16 @@ export default { rename: RENAME, RENAMENX, renameNX: RENAMENX, + RPOP_COUNT, + rPopCount: RPOP_COUNT, + RPOP, + rPop: RPOP, + RPOPLPUSH, + rPopLPush: RPOPLPUSH, + RPUSH, + rPush: RPUSH, + RPUSHX, + rPushX: RPUSHX, SADD, sAdd: SADD, SCAN,