You've already forked node-redis
mirror of
https://github.com/redis/node-redis.git
synced 2025-08-09 00:22:08 +03:00
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
This commit is contained in:
@@ -193,14 +193,14 @@ export default class RedisClient<M extends RedisModules = RedisModules, S extend
|
||||
}
|
||||
}
|
||||
|
||||
async executeScript<S extends RedisLuaScript>(script: S, args: Array<string>): Promise<ReturnType<S['transformReply']>> {
|
||||
async executeScript<S extends RedisLuaScript>(script: S, args: Array<string>, options?: ClientCommandOptions): Promise<ReturnType<S['transformReply']>> {
|
||||
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<M extends RedisModules = RedisModules, S extend
|
||||
script.SCRIPT,
|
||||
script.NUMBER_OF_KEYS.toString(),
|
||||
...args
|
||||
]);
|
||||
], options);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -1,19 +1,21 @@
|
||||
import calculateSlot from 'cluster-key-slot';
|
||||
import RedisClient from './client';
|
||||
import RedisClient, { RedisClientType } from './client';
|
||||
import { RedisSocketOptions } from './socket';
|
||||
import { RedisClusterMasterNode, RedisClusterReplicaNode } from './commands/CLUSTER_NODES';
|
||||
import { RedisClusterOptions } from './cluster';
|
||||
import { RedisModules } from './commands';
|
||||
import { RedisLuaScripts } from './lua-script';
|
||||
|
||||
interface SlotClients {
|
||||
master: RedisClient;
|
||||
replicas: Array<RedisClient>;
|
||||
iterator: IterableIterator<RedisClient> | undefined;
|
||||
interface SlotClients<M extends RedisModules, S extends RedisLuaScripts> {
|
||||
master: RedisClientType<M, S>;
|
||||
replicas: Array<RedisClientType<M, S>>;
|
||||
iterator: IterableIterator<RedisClientType<M, S>> | undefined;
|
||||
}
|
||||
|
||||
export default class RedisClusterSlots {
|
||||
export default class RedisClusterSlots<M extends RedisModules, S extends RedisLuaScripts> {
|
||||
readonly #options: RedisClusterOptions;
|
||||
readonly #clientByKey = new Map<string, RedisClient>();
|
||||
readonly #slots: Array<SlotClients> = [];
|
||||
readonly #clientByKey = new Map<string, RedisClientType<M, S>>();
|
||||
readonly #slots: Array<SlotClients<M, S>> = [];
|
||||
|
||||
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<void> {
|
||||
async discover(startWith: RedisClientType<M, S>): Promise<void> {
|
||||
try {
|
||||
await this.#discoverNodes(startWith.options?.socket);
|
||||
return;
|
||||
@@ -99,7 +101,7 @@ export default class RedisClusterSlots {
|
||||
}
|
||||
}
|
||||
|
||||
#initiateClientForNode(node: RedisClusterMasterNode | RedisClusterReplicaNode, readonly: boolean, clientsInUse: Set<string>, promises: Array<Promise<void>>): RedisClient {
|
||||
#initiateClientForNode(node: RedisClusterMasterNode | RedisClusterReplicaNode, readonly: boolean, clientsInUse: Set<string>, promises: Array<Promise<void>>): RedisClientType<M, S> {
|
||||
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<M, S> {
|
||||
return this.#slots[slot].master;
|
||||
}
|
||||
|
||||
*#slotIterator(slotNumber: number): IterableIterator<RedisClient> {
|
||||
*#slotIterator(slotNumber: number): IterableIterator<RedisClientType<M, S>> {
|
||||
const slot = this.#slots[slotNumber];
|
||||
yield slot.master;
|
||||
|
||||
@@ -131,7 +133,7 @@ export default class RedisClusterSlots {
|
||||
}
|
||||
}
|
||||
|
||||
#getSlotClient(slotNumber: number): RedisClient {
|
||||
#getSlotClient(slotNumber: number): RedisClientType<M, S> {
|
||||
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<RedisClient>;
|
||||
#randomClientIterator?: IterableIterator<RedisClientType<M, S>>;
|
||||
|
||||
#getRandomClient(): RedisClient {
|
||||
#getRandomClient(): RedisClientType<M, S> {
|
||||
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<M, S> {
|
||||
if (!firstKey) {
|
||||
return this.#getRandomClient();
|
||||
}
|
||||
@@ -179,6 +181,18 @@ export default class RedisClusterSlots {
|
||||
return this.#getSlotClient(slot);
|
||||
}
|
||||
|
||||
getMasters(): Array<RedisClientType<M, S>> {
|
||||
const masters = [];
|
||||
|
||||
for (const client of this.#clientByKey.values()) {
|
||||
if (client.options?.readonly) continue;
|
||||
|
||||
masters.push(client);
|
||||
}
|
||||
|
||||
return masters;
|
||||
}
|
||||
|
||||
async disconnect(): Promise<void> {
|
||||
await Promise.all(
|
||||
[...this.#clientByKey.values()].map(client => client.disconnect())
|
||||
|
@@ -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<M = RedisModules, S = RedisLuaScripts> {
|
||||
rootNodes: Array<RedisSocketOptions>;
|
||||
@@ -26,6 +25,7 @@ export default class RedisCluster<M extends RedisModules = RedisModules, S exten
|
||||
await this.sendCommand(
|
||||
command,
|
||||
command.transformArguments(...(options ? args.slice(1) : args)),
|
||||
options
|
||||
)
|
||||
);
|
||||
};
|
||||
@@ -35,12 +35,12 @@ export default class RedisCluster<M extends RedisModules = RedisModules, S exten
|
||||
return <any>new RedisCluster(options);
|
||||
}
|
||||
|
||||
static commandOptions(options: QueueCommandOptions): CommandOptions<QueueCommandOptions> {
|
||||
return this.commandOptions(options);
|
||||
static commandOptions(options: ClientCommandOptions): CommandOptions<ClientCommandOptions> {
|
||||
return commandOptions(options);
|
||||
}
|
||||
|
||||
readonly #options: RedisClusterOptions;
|
||||
readonly #slots: RedisClusterSlots;
|
||||
readonly #slots: RedisClusterSlots<M, S>;
|
||||
|
||||
constructor(options: RedisClusterOptions<M, S>) {
|
||||
this.#options = options;
|
||||
@@ -68,7 +68,8 @@ export default class RedisCluster<M extends RedisModules = RedisModules, S exten
|
||||
return script.transformReply(
|
||||
await this.executeScript(
|
||||
script,
|
||||
script.transformArguments(...(options ? args.slice(1) : args))
|
||||
script.transformArguments(...(options ? args.slice(1) : args)),
|
||||
options
|
||||
)
|
||||
);
|
||||
};
|
||||
@@ -79,42 +80,42 @@ export default class RedisCluster<M extends RedisModules = RedisModules, S exten
|
||||
return this.#slots.connect();
|
||||
}
|
||||
|
||||
async sendCommand<C extends RedisCommand>(command: C, args: Array<string>, redirections: number = 0): Promise<ReturnType<C['transformReply']>> {
|
||||
async sendCommand<C extends RedisCommand>(command: C, args: Array<string>, options?: ClientCommandOptions, redirections: number = 0): Promise<ReturnType<C['transformReply']>> {
|
||||
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<S extends RedisLuaScript>(script: S, args: Array<string>, redirections: number = 0): Promise<ReturnType<S['transformReply']>> {
|
||||
async executeScript<S extends RedisLuaScript>(script: S, args: Array<string>, options?: ClientCommandOptions, redirections: number = 0): Promise<ReturnType<S['transformReply']>> {
|
||||
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<string>): RedisClient {
|
||||
#getClient(commandOrScript: RedisCommand | RedisLuaScript, args: Array<string>): RedisClientType<M, S> {
|
||||
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<boolean> {
|
||||
async #handleCommandError(err: Error, client: RedisClientType<M, S>, redirections: number = 0): Promise<boolean> {
|
||||
if (redirections < (this.#options.maxCommandRedirections ?? 16)) {
|
||||
throw err;
|
||||
}
|
||||
@@ -128,6 +129,10 @@ export default class RedisCluster<M extends RedisModules = RedisModules, S exten
|
||||
throw err;
|
||||
}
|
||||
|
||||
getMasters(): Array<RedisClientType<M, S>> {
|
||||
return this.#slots.getMasters();
|
||||
}
|
||||
|
||||
disconnect(): Promise<void> {
|
||||
return this.#slots.disconnect();
|
||||
}
|
||||
|
42
lib/commands/BLMOVE.spec.ts
Normal file
42
lib/commands/BLMOVE.spec.ts
Normal file
@@ -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'
|
||||
);
|
||||
});
|
||||
});
|
23
lib/commands/BLMOVE.ts
Normal file
23
lib/commands/BLMOVE.ts
Normal file
@@ -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<string> {
|
||||
return [
|
||||
'BLMOVE',
|
||||
source,
|
||||
destination,
|
||||
sourceDirection,
|
||||
destinationDirection,
|
||||
timeout.toString()
|
||||
];
|
||||
}
|
||||
|
||||
export const transformReply = transformReplyStringNull;
|
@@ -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'
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
@@ -1,4 +1,4 @@
|
||||
export const FIRST_KEY_INDEX = 0;
|
||||
export const FIRST_KEY_INDEX = 1;
|
||||
|
||||
export function transformArguments(keys: string | Array<string>, timeout: number): Array<string> {
|
||||
const args = ['BLPOP'];
|
||||
@@ -14,8 +14,16 @@ export function transformArguments(keys: string | Array<string>, 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]
|
||||
};
|
||||
}
|
||||
|
57
lib/commands/BRPOP.spec.ts
Normal file
57
lib/commands/BRPOP.spec.ts
Normal file
@@ -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'
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
31
lib/commands/BRPOP.ts
Normal file
31
lib/commands/BRPOP.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import { transformReplyStringArray } from './generic-transformers';
|
||||
|
||||
export const FIRST_KEY_INDEX = 1;
|
||||
|
||||
export function transformArguments(key: string | Array<string>, timeout: number): Array<string> {
|
||||
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]
|
||||
};
|
||||
}
|
42
lib/commands/BRPOPLPUSH.spec.ts
Normal file
42
lib/commands/BRPOPLPUSH.spec.ts
Normal file
@@ -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'
|
||||
);
|
||||
});
|
||||
});
|
9
lib/commands/BRPOPLPUSH.ts
Normal file
9
lib/commands/BRPOPLPUSH.ts
Normal file
@@ -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<string> {
|
||||
return ['BRPOPLPUSH', source, destination, timeout.toString()];
|
||||
}
|
||||
|
||||
export const transformReply = transformReplyNumberNull;
|
27
lib/commands/CLUSTER_RESET.spec.ts
Normal file
27
lib/commands/CLUSTER_RESET.spec.ts
Normal file
@@ -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']
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
15
lib/commands/CLUSTER_RESET.ts
Normal file
15
lib/commands/CLUSTER_RESET.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { transformReplyString } from './generic-transformers';
|
||||
|
||||
export type ClusterResetModes = 'HARD' | 'SOFT';
|
||||
|
||||
export function transformArguments(mode?: ClusterResetModes): Array<string> {
|
||||
const args = ['CLUSTER', 'RESET'];
|
||||
|
||||
if (mode) {
|
||||
args.push(mode);
|
||||
}
|
||||
|
||||
return args;
|
||||
}
|
||||
|
||||
export const transformReply = transformReplyString;
|
@@ -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<string> {
|
||||
];
|
||||
}
|
||||
|
||||
export function transformReply(reply: Array<string> | null): Array<string> | null {
|
||||
return reply;
|
||||
}
|
||||
export const transformReply = transformReplyStringArrayNull;
|
||||
|
26
lib/commands/LINDEX.spec.ts
Normal file
26
lib/commands/LINDEX.spec.ts
Normal file
@@ -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
|
||||
);
|
||||
});
|
||||
});
|
11
lib/commands/LINDEX.ts
Normal file
11
lib/commands/LINDEX.ts
Normal file
@@ -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<string> {
|
||||
return ['LINDEX', key, element];
|
||||
}
|
||||
|
||||
export const transformReply = transformReplyNumberNull;
|
26
lib/commands/LINSERT.spec.ts
Normal file
26
lib/commands/LINSERT.spec.ts
Normal file
@@ -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
|
||||
);
|
||||
});
|
||||
});
|
22
lib/commands/LINSERT.ts
Normal file
22
lib/commands/LINSERT.ts
Normal file
@@ -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<string> {
|
||||
return [
|
||||
'LINSERT',
|
||||
key,
|
||||
position,
|
||||
pivot,
|
||||
element
|
||||
];
|
||||
}
|
||||
|
||||
export const transformReply = transformReplyNumber;
|
26
lib/commands/LLEN.spec.ts
Normal file
26
lib/commands/LLEN.spec.ts
Normal file
@@ -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
|
||||
);
|
||||
});
|
||||
});
|
11
lib/commands/LLEN.ts
Normal file
11
lib/commands/LLEN.ts
Normal file
@@ -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<string> {
|
||||
return ['LLEN', key];
|
||||
}
|
||||
|
||||
export const transformReply = transformReplyNumber;
|
26
lib/commands/LMOVE.spec.ts
Normal file
26
lib/commands/LMOVE.spec.ts
Normal file
@@ -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
|
||||
);
|
||||
});
|
||||
});
|
22
lib/commands/LMOVE.ts
Normal file
22
lib/commands/LMOVE.ts
Normal file
@@ -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<string> {
|
||||
return [
|
||||
'LMOVE',
|
||||
source,
|
||||
destination,
|
||||
sourceSide,
|
||||
destinationSide,
|
||||
];
|
||||
}
|
||||
|
||||
export const transformReply = transformReplyStringNull;
|
26
lib/commands/LPOP.spec.ts
Normal file
26
lib/commands/LPOP.spec.ts
Normal file
@@ -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
|
||||
);
|
||||
});
|
||||
});
|
9
lib/commands/LPOP.ts
Normal file
9
lib/commands/LPOP.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import { transformReplyStringNull } from './generic-transformers';
|
||||
|
||||
export const FIRST_KEY_INDEX = 1;
|
||||
|
||||
export function transformArguments(key: string): Array<string> {
|
||||
return ['LPOP', key];
|
||||
}
|
||||
|
||||
export const transformReply = transformReplyStringNull;
|
26
lib/commands/LPOP_COUNT.spec.ts
Normal file
26
lib/commands/LPOP_COUNT.spec.ts
Normal file
@@ -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
|
||||
);
|
||||
});
|
||||
});
|
9
lib/commands/LPOP_COUNT.ts
Normal file
9
lib/commands/LPOP_COUNT.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import { transformReplyStringArrayNull } from './generic-transformers';
|
||||
|
||||
export const FIRST_KEY_INDEX = 1;
|
||||
|
||||
export function transformArguments(key: string, count: number): Array<string> {
|
||||
return ['LPOP', key, count.toString()];
|
||||
}
|
||||
|
||||
export const transformReply = transformReplyStringArrayNull;
|
@@ -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
|
||||
);
|
||||
});
|
||||
});
|
||||
|
@@ -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<string>): Array<string> {
|
||||
const args = [
|
||||
|
35
lib/commands/LPUSHX.spec.ts
Normal file
35
lib/commands/LPUSHX.spec.ts
Normal file
@@ -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
|
||||
);
|
||||
});
|
||||
});
|
20
lib/commands/LPUSHX.ts
Normal file
20
lib/commands/LPUSHX.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import { transformReplyNumber } from './generic-transformers';
|
||||
|
||||
export const FIRST_KEY_INDEX = 1;
|
||||
|
||||
export function transformArguments(key: string, element: string | Array<string>): Array<string> {
|
||||
const args = [
|
||||
'LPUSHX',
|
||||
key
|
||||
];
|
||||
|
||||
if (typeof element === 'string') {
|
||||
args.push(element);
|
||||
} else {
|
||||
args.push(...element);
|
||||
}
|
||||
|
||||
return args;
|
||||
}
|
||||
|
||||
export const transformReply = transformReplyNumber;
|
27
lib/commands/LRANGE.spec.ts
Normal file
27
lib/commands/LRANGE.spec.ts
Normal file
@@ -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),
|
||||
[]
|
||||
);
|
||||
});
|
||||
});
|
16
lib/commands/LRANGE.ts
Normal file
16
lib/commands/LRANGE.ts
Normal file
@@ -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<string> {
|
||||
return [
|
||||
'LRANGE',
|
||||
key,
|
||||
start.toString(),
|
||||
stop.toString()
|
||||
];
|
||||
}
|
||||
|
||||
export const transformReply = transformReplyStringArray;
|
27
lib/commands/LREM.spec.ts
Normal file
27
lib/commands/LREM.spec.ts
Normal file
@@ -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
|
||||
);
|
||||
});
|
||||
});
|
14
lib/commands/LREM.ts
Normal file
14
lib/commands/LREM.ts
Normal file
@@ -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<string> {
|
||||
return [
|
||||
'LREM',
|
||||
key,
|
||||
count.toString(),
|
||||
element
|
||||
];
|
||||
}
|
||||
|
||||
export const transformReply = transformReplyNumber;
|
28
lib/commands/LSET.spec.ts
Normal file
28
lib/commands/LSET.spec.ts
Normal file
@@ -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'
|
||||
);
|
||||
});
|
||||
});
|
14
lib/commands/LSET.ts
Normal file
14
lib/commands/LSET.ts
Normal file
@@ -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<string> {
|
||||
return [
|
||||
'LSET',
|
||||
key,
|
||||
index.toString(),
|
||||
element
|
||||
];
|
||||
}
|
||||
|
||||
export const transformReply = transformReplyString;
|
26
lib/commands/LTRIM.spec.ts
Normal file
26
lib/commands/LTRIM.spec.ts
Normal file
@@ -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'
|
||||
);
|
||||
});
|
||||
});
|
14
lib/commands/LTRIM.ts
Normal file
14
lib/commands/LTRIM.ts
Normal file
@@ -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<string> {
|
||||
return [
|
||||
'LTRIM',
|
||||
key,
|
||||
start.toString(),
|
||||
stop.toString()
|
||||
]
|
||||
}
|
||||
|
||||
export const transformReply = transformReplyString;
|
26
lib/commands/RPOP.spec.ts
Normal file
26
lib/commands/RPOP.spec.ts
Normal file
@@ -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
|
||||
);
|
||||
});
|
||||
});
|
9
lib/commands/RPOP.ts
Normal file
9
lib/commands/RPOP.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import { transformReplyStringNull } from './generic-transformers';
|
||||
|
||||
export const FIRST_KEY_INDEX = 1;
|
||||
|
||||
export function transformArguments(key: string): Array<string> {
|
||||
return ['RPOP', key];
|
||||
}
|
||||
|
||||
export const transformReply = transformReplyStringNull;
|
26
lib/commands/RPOPLPUSH.spec.ts
Normal file
26
lib/commands/RPOPLPUSH.spec.ts
Normal file
@@ -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
|
||||
);
|
||||
});
|
||||
});
|
9
lib/commands/RPOPLPUSH.ts
Normal file
9
lib/commands/RPOPLPUSH.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import { transformReplyNumberNull } from './generic-transformers';
|
||||
|
||||
export const FIRST_KEY_INDEX = 1;
|
||||
|
||||
export function transformArguments(source: string, destination: string): Array<string> {
|
||||
return ['RPOPLPUSH', source, destination];
|
||||
}
|
||||
|
||||
export const transformReply = transformReplyNumberNull;
|
26
lib/commands/RPOP_COUNT.spec.ts
Normal file
26
lib/commands/RPOP_COUNT.spec.ts
Normal file
@@ -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
|
||||
);
|
||||
});
|
||||
});
|
9
lib/commands/RPOP_COUNT.ts
Normal file
9
lib/commands/RPOP_COUNT.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import { transformReplyStringArrayNull } from './generic-transformers';
|
||||
|
||||
export const FIRST_KEY_INDEX = 1;
|
||||
|
||||
export function transformArguments(key: string, count: number): Array<string> {
|
||||
return ['RPOP', key, count.toString()];
|
||||
}
|
||||
|
||||
export const transformReply = transformReplyStringArrayNull;
|
35
lib/commands/RPUSH.spec.ts
Normal file
35
lib/commands/RPUSH.spec.ts
Normal file
@@ -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
|
||||
);
|
||||
});
|
||||
});
|
20
lib/commands/RPUSH.ts
Normal file
20
lib/commands/RPUSH.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import { transformReplyNumber } from './generic-transformers';
|
||||
|
||||
export const FIRST_KEY_INDEX = 1;
|
||||
|
||||
export function transformArguments(key: string, element: string | Array<string>): Array<string> {
|
||||
const args = [
|
||||
'RPUSH',
|
||||
key
|
||||
];
|
||||
|
||||
if (typeof element === 'string') {
|
||||
args.push(element);
|
||||
} else {
|
||||
args.push(...element);
|
||||
}
|
||||
|
||||
return args;
|
||||
}
|
||||
|
||||
export const transformReply = transformReplyNumber;
|
35
lib/commands/RPUSHX.spec.ts
Normal file
35
lib/commands/RPUSHX.spec.ts
Normal file
@@ -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
|
||||
);
|
||||
});
|
||||
});
|
20
lib/commands/RPUSHX.ts
Normal file
20
lib/commands/RPUSHX.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import { transformReplyNumber } from './generic-transformers';
|
||||
|
||||
export const FIRST_KEY_INDEX = 1;
|
||||
|
||||
export function transformArguments(key: string, element: string | Array<string>): Array<string> {
|
||||
const args = [
|
||||
'RPUSHX',
|
||||
key
|
||||
];
|
||||
|
||||
if (typeof element === 'string') {
|
||||
args.push(element);
|
||||
} else {
|
||||
args.push(...element);
|
||||
}
|
||||
|
||||
return args;
|
||||
}
|
||||
|
||||
export const transformReply = transformReplyNumber;
|
@@ -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<string> {
|
||||
];
|
||||
}
|
||||
|
||||
export function transformReply(reply: Array<string> | null): Array<string> | null {
|
||||
return reply;
|
||||
}
|
||||
export const transformReply = transformReplyStringArrayNull;
|
||||
|
@@ -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<string> {
|
||||
export function transformArguments(key: string, min: string | number, max: string | number, options?: ZRangeOptions): Array<string> {
|
||||
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) {
|
||||
|
@@ -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<string>): Array<string> {
|
||||
return reply;
|
||||
}
|
||||
|
||||
export function transformReplyStringArrayNull(reply: Array<string> | null): Array<string> | null {
|
||||
return reply;
|
||||
}
|
||||
|
||||
export function transformReplyBoolean(reply: number): boolean {
|
||||
return reply === 1;
|
||||
}
|
||||
|
@@ -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,
|
||||
|
Reference in New Issue
Block a user