1
0
mirror of https://github.com/redis/node-redis.git synced 2025-08-06 02:15:48 +03:00

Merge branch 'master' of github.com:redis/node-redis into COMMAND_DOCS

This commit is contained in:
Avital-Fine
2022-04-03 14:08:19 +03:00
117 changed files with 2861 additions and 1054 deletions

View File

@@ -17,7 +17,7 @@ jobs:
fail-fast: false fail-fast: false
matrix: matrix:
node-version: ['12', '14', '16'] node-version: ['12', '14', '16']
redis-version: ['5', '6.0', '6.2'] redis-version: ['5', '6.0', '6.2', '7.0-rc2']
steps: steps:
- uses: actions/checkout@v2.3.4 - uses: actions/checkout@v2.3.4
with: with:

1367
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
{ {
"name": "redis", "name": "redis",
"description": "A modern, high performance Redis client", "description": "A modern, high performance Redis client",
"version": "4.0.4", "version": "4.0.6",
"license": "MIT", "license": "MIT",
"main": "./dist/index.js", "main": "./dist/index.js",
"types": "./dist/index.d.ts", "types": "./dist/index.d.ts",
@@ -24,17 +24,17 @@
}, },
"dependencies": { "dependencies": {
"@node-redis/bloom": "1.0.1", "@node-redis/bloom": "1.0.1",
"@node-redis/client": "1.0.4", "@node-redis/client": "1.0.5",
"@node-redis/graph": "1.0.0", "@node-redis/graph": "1.0.0",
"@node-redis/json": "1.0.2", "@node-redis/json": "1.0.2",
"@node-redis/search": "1.0.3", "@node-redis/search": "1.0.5",
"@node-redis/time-series": "1.0.2" "@node-redis/time-series": "1.0.2"
}, },
"devDependencies": { "devDependencies": {
"@tsconfig/node12": "^1.0.9", "@tsconfig/node12": "^1.0.9",
"gh-pages": "^3.2.3", "gh-pages": "^3.2.3",
"release-it": "^14.12.1", "release-it": "^14.13.1",
"typescript": "^4.5.4" "typescript": "^4.6.3"
}, },
"repository": { "repository": {
"type": "git", "type": "git",

View File

@@ -18,12 +18,12 @@
"devDependencies": { "devDependencies": {
"@istanbuljs/nyc-config-typescript": "^1.0.2", "@istanbuljs/nyc-config-typescript": "^1.0.2",
"@node-redis/test-utils": "*", "@node-redis/test-utils": "*",
"@types/node": "^17.0.13", "@types/node": "^17.0.23",
"nyc": "^15.1.0", "nyc": "^15.1.0",
"release-it": "^14.12.4", "release-it": "^14.13.1",
"source-map-support": "^0.5.21", "source-map-support": "^0.5.21",
"ts-node": "^10.4.0", "ts-node": "^10.7.0",
"typedoc": "^0.22.11", "typedoc": "^0.22.13",
"typescript": "^4.5.5" "typescript": "^4.6.3"
} }
} }

View File

@@ -23,16 +23,33 @@ import * as CLIENT_KILL from '../commands/CLIENT_KILL';
import * as CLIENT_SETNAME from '../commands/CLIENT_SETNAME'; import * as CLIENT_SETNAME from '../commands/CLIENT_SETNAME';
import * as CLIENT_INFO from '../commands/CLIENT_INFO'; import * as CLIENT_INFO from '../commands/CLIENT_INFO';
import * as CLUSTER_ADDSLOTS from '../commands/CLUSTER_ADDSLOTS'; import * as CLUSTER_ADDSLOTS from '../commands/CLUSTER_ADDSLOTS';
import * as CLUSTER_ADDSLOTSRANGE from '../commands/CLUSTER_ADDSLOTSRANGE';
import * as CLUSTER_BUMPEPOCH from '../commands/CLUSTER_BUMPEPOCH';
import * as CLUSTER_COUNT_FAILURE_REPORTS from '../commands/CLUSTER_COUNT-FAILURE-REPORTS';
import * as CLUSTER_COUNTKEYSINSLOT from '../commands/CLUSTER_COUNTKEYSINSLOT';
import * as CLUSTER_DELSLOTS from '../commands/CLUSTER_DELSLOTS';
import * as CLUSTER_DELSLOTSRANGE from '../commands/CLUSTER_DELSLOTSRANGE';
import * as CLUSTER_FAILOVER from '../commands/CLUSTER_FAILOVER';
import * as CLUSTER_FLUSHSLOTS from '../commands/CLUSTER_FLUSHSLOTS'; import * as CLUSTER_FLUSHSLOTS from '../commands/CLUSTER_FLUSHSLOTS';
import * as CLUSTER_FORGET from '../commands/CLUSTER_FORGET';
import * as CLUSTER_GETKEYSINSLOT from '../commands/CLUSTER_GETKEYSINSLOT';
import * as CLUSTER_INFO from '../commands/CLUSTER_INFO'; import * as CLUSTER_INFO from '../commands/CLUSTER_INFO';
import * as CLUSTER_NODES from '../commands/CLUSTER_NODES'; import * as CLUSTER_KEYSLOT from '../commands/CLUSTER_KEYSLOT';
import * as CLUSTER_LINKS from '../commands/CLUSTER_LINKS';
import * as CLUSTER_MEET from '../commands/CLUSTER_MEET'; import * as CLUSTER_MEET from '../commands/CLUSTER_MEET';
import * as CLUSTER_MYID from '../commands/CLUSTER_MYID';
import * as CLUSTER_NODES from '../commands/CLUSTER_NODES';
import * as CLUSTER_REPLICAS from '../commands/CLUSTER_REPLICAS';
import * as CLUSTER_REPLICATE from '../commands/CLUSTER_REPLICATE';
import * as CLUSTER_RESET from '../commands/CLUSTER_RESET'; import * as CLUSTER_RESET from '../commands/CLUSTER_RESET';
import * as CLUSTER_SAVECONFIG from '../commands/CLUSTER_SAVECONFIG';
import * as CLUSTER_SET_CONFIG_EPOCH from '../commands/CLUSTER_SET-CONFIG-EPOCH';
import * as CLUSTER_SETSLOT from '../commands/CLUSTER_SETSLOT'; import * as CLUSTER_SETSLOT from '../commands/CLUSTER_SETSLOT';
import * as CLUSTER_SLOTS from '../commands/CLUSTER_SLOTS'; import * as CLUSTER_SLOTS from '../commands/CLUSTER_SLOTS';
import * as COMMAND_COUNT from '../commands/COMMAND_COUNT'; import * as COMMAND_COUNT from '../commands/COMMAND_COUNT';
import * as COMMAND_DOCS from '../commands/COMMAND_DOCS'; import * as COMMAND_DOCS from '../commands/COMMAND_DOCS';
import * as COMMAND_GETKEYS from '../commands/COMMAND_GETKEYS'; import * as COMMAND_GETKEYS from '../commands/COMMAND_GETKEYS';
import * as COMMAND_GETKEYSANDFLAGS from '../commands/COMMAND_GETKEYSANDFLAGS';
import * as COMMAND_INFO from '../commands/COMMAND_INFO'; import * as COMMAND_INFO from '../commands/COMMAND_INFO';
import * as COMMAND_LIST from '../commands/COMMAND_LIST'; import * as COMMAND_LIST from '../commands/COMMAND_LIST';
import * as COMMAND from '../commands/COMMAND'; import * as COMMAND from '../commands/COMMAND';
@@ -82,6 +99,7 @@ import * as SWAPDB from '../commands/SWAPDB';
import * as TIME from '../commands/TIME'; import * as TIME from '../commands/TIME';
import * as UNWATCH from '../commands/UNWATCH'; import * as UNWATCH from '../commands/UNWATCH';
import * as WAIT from '../commands/WAIT'; import * as WAIT from '../commands/WAIT';
import * as LATENCY_DOCTOR from '../commands/LATENCY_DOCTOR';
export default { export default {
...CLUSTER_COMMANDS, ...CLUSTER_COMMANDS,
@@ -133,16 +151,48 @@ export default {
clientInfo: CLIENT_INFO, clientInfo: CLIENT_INFO,
CLUSTER_ADDSLOTS, CLUSTER_ADDSLOTS,
clusterAddSlots: CLUSTER_ADDSLOTS, clusterAddSlots: CLUSTER_ADDSLOTS,
CLUSTER_ADDSLOTSRANGE,
clusterAddSlotsRange: CLUSTER_ADDSLOTSRANGE,
CLUSTER_BUMPEPOCH,
clusterBumpEpoch: CLUSTER_BUMPEPOCH,
CLUSTER_COUNT_FAILURE_REPORTS,
clusterCountFailureReports: CLUSTER_COUNT_FAILURE_REPORTS,
CLUSTER_COUNTKEYSINSLOT,
clusterCountKeysInSlot: CLUSTER_COUNTKEYSINSLOT,
CLUSTER_DELSLOTS,
clusterDelSlots: CLUSTER_DELSLOTS,
CLUSTER_DELSLOTSRANGE,
clusterDelSlotsRange: CLUSTER_DELSLOTSRANGE,
CLUSTER_FAILOVER,
clusterFailover: CLUSTER_FAILOVER,
CLUSTER_FLUSHSLOTS, CLUSTER_FLUSHSLOTS,
clusterFlushSlots: CLUSTER_FLUSHSLOTS, clusterFlushSlots: CLUSTER_FLUSHSLOTS,
CLUSTER_FORGET,
clusterForget: CLUSTER_FORGET,
CLUSTER_GETKEYSINSLOT,
clusterGetKeysInSlot: CLUSTER_GETKEYSINSLOT,
CLUSTER_INFO, CLUSTER_INFO,
clusterInfo: CLUSTER_INFO, clusterInfo: CLUSTER_INFO,
CLUSTER_NODES, CLUSTER_KEYSLOT,
clusterNodes: CLUSTER_NODES, clusterKeySlot: CLUSTER_KEYSLOT,
CLUSTER_LINKS,
clusterLinks: CLUSTER_LINKS,
CLUSTER_MEET, CLUSTER_MEET,
clusterMeet: CLUSTER_MEET, clusterMeet: CLUSTER_MEET,
CLUSTER_MYID,
clusterMyId: CLUSTER_MYID,
CLUSTER_NODES,
clusterNodes: CLUSTER_NODES,
CLUSTER_REPLICAS,
clusterReplicas: CLUSTER_REPLICAS,
CLUSTER_REPLICATE,
clusterReplicate: CLUSTER_REPLICATE,
CLUSTER_RESET, CLUSTER_RESET,
clusterReset: CLUSTER_RESET, clusterReset: CLUSTER_RESET,
CLUSTER_SAVECONFIG,
clusterSaveConfig: CLUSTER_SAVECONFIG,
CLUSTER_SET_CONFIG_EPOCH,
clusterSetConfigEpoch: CLUSTER_SET_CONFIG_EPOCH,
CLUSTER_SETSLOT, CLUSTER_SETSLOT,
clusterSetSlot: CLUSTER_SETSLOT, clusterSetSlot: CLUSTER_SETSLOT,
CLUSTER_SLOTS, CLUSTER_SLOTS,
@@ -153,6 +203,8 @@ export default {
commandDocs: COMMAND_DOCS, commandDocs: COMMAND_DOCS,
COMMAND_GETKEYS, COMMAND_GETKEYS,
commandGetKeys: COMMAND_GETKEYS, commandGetKeys: COMMAND_GETKEYS,
COMMAND_GETKEYSANDFLAGS,
commandGetKeysAndFlags: COMMAND_GETKEYSANDFLAGS,
COMMAND_INFO, COMMAND_INFO,
commandInfo: COMMAND_INFO, commandInfo: COMMAND_INFO,
COMMAND_LIST, COMMAND_LIST,
@@ -187,6 +239,8 @@ export default {
keys: KEYS, keys: KEYS,
LASTSAVE, LASTSAVE,
lastSave: LASTSAVE, lastSave: LASTSAVE,
LATENCY_DOCTOR,
latencyDoctor: LATENCY_DOCTOR,
LOLWUT, LOLWUT,
lolwut: LOLWUT, lolwut: LOLWUT,
MEMOERY_DOCTOR, MEMOERY_DOCTOR,

View File

@@ -477,8 +477,11 @@ describe('Client', () => {
assert.ok(id !== isolatedId); assert.ok(id !== isolatedId);
}, GLOBAL.SERVERS.OPEN); }, GLOBAL.SERVERS.OPEN);
async function killClient<M extends RedisModules, S extends RedisScripts>(client: RedisClientType<M, S>): Promise<void> { async function killClient<M extends RedisModules, S extends RedisScripts>(
const onceErrorPromise = once(client, 'error'); client: RedisClientType<M, S>,
errorClient: RedisClientType<M, S> = client
): Promise<void> {
const onceErrorPromise = once(errorClient, 'error');
await client.sendCommand(['QUIT']); await client.sendCommand(['QUIT']);
await Promise.all([ await Promise.all([
onceErrorPromise, onceErrorPromise,
@@ -503,6 +506,10 @@ describe('Client', () => {
minimumDockerVersion: [6, 2] // CLIENT INFO minimumDockerVersion: [6, 2] // CLIENT INFO
}); });
testUtils.testWithClient('should propagated errors from "isolated" clients', client => {
return client.executeIsolated(isolated => killClient(isolated, client));
}, GLOBAL.SERVERS.OPEN);
testUtils.testWithClient('scanIterator', async client => { testUtils.testWithClient('scanIterator', async client => {
const promises = [], const promises = [],
keys = new Set(); keys = new Set();

View File

@@ -189,7 +189,7 @@ export default class RedisClient<M extends RedisModules, S extends RedisScripts>
create: async () => { create: async () => {
const duplicate = this.duplicate({ const duplicate = this.duplicate({
isolationPoolOptions: undefined isolationPoolOptions: undefined
}); }).on('error', err => this.emit('error', err));
await duplicate.connect(); await duplicate.connect();
return duplicate; return duplicate;
}, },

View File

@@ -6,7 +6,7 @@ describe('Socket', () => {
describe('reconnectStrategy', () => { describe('reconnectStrategy', () => {
let clock: SinonFakeTimers; let clock: SinonFakeTimers;
beforeEach(() => clock = useFakeTimers()); beforeEach(() => clock = useFakeTimers());
afterEach(() => clock.uninstall()); afterEach(() => clock.restore());
it('custom strategy', () => { it('custom strategy', () => {
const reconnectStrategy = spy((retries: number): number | Error => { const reconnectStrategy = spy((retries: number): number | Error => {

View File

@@ -21,6 +21,7 @@ import * as EVALSHA from '../commands/EVALSHA';
import * as EXISTS from '../commands/EXISTS'; import * as EXISTS from '../commands/EXISTS';
import * as EXPIRE from '../commands/EXPIRE'; import * as EXPIRE from '../commands/EXPIRE';
import * as EXPIREAT from '../commands/EXPIREAT'; import * as EXPIREAT from '../commands/EXPIREAT';
import * as EXPIRETIME from '../commands/EXPIRETIME';
import * as GEOADD from '../commands/GEOADD'; import * as GEOADD from '../commands/GEOADD';
import * as GEODIST from '../commands/GEODIST'; import * as GEODIST from '../commands/GEODIST';
import * as GEOHASH from '../commands/GEOHASH'; import * as GEOHASH from '../commands/GEOHASH';
@@ -72,9 +73,14 @@ import * as MGET from '../commands/MGET';
import * as MIGRATE from '../commands/MIGRATE'; import * as MIGRATE from '../commands/MIGRATE';
import * as MSET from '../commands/MSET'; import * as MSET from '../commands/MSET';
import * as MSETNX from '../commands/MSETNX'; import * as MSETNX from '../commands/MSETNX';
import * as OBJECT_ENCODING from '../commands/OBJECT_ENCODING';
import * as OBJECT_FREQ from '../commands/OBJECT_FREQ';
import * as OBJECT_IDLETIME from '../commands/OBJECT_IDLETIME';
import * as OBJECT_REFCOUNT from '../commands/OBJECT_REFCOUNT';
import * as PERSIST from '../commands/PERSIST'; import * as PERSIST from '../commands/PERSIST';
import * as PEXPIRE from '../commands/PEXPIRE'; import * as PEXPIRE from '../commands/PEXPIRE';
import * as PEXPIREAT from '../commands/PEXPIREAT'; import * as PEXPIREAT from '../commands/PEXPIREAT';
import * as PEXPIRETIME from '../commands/PEXPIRETIME';
import * as PFADD from '../commands/PFADD'; import * as PFADD from '../commands/PFADD';
import * as PFCOUNT from '../commands/PFCOUNT'; import * as PFCOUNT from '../commands/PFCOUNT';
import * as PFMERGE from '../commands/PFMERGE'; import * as PFMERGE from '../commands/PFMERGE';
@@ -98,11 +104,14 @@ import * as SETEX from '../commands/SETEX';
import * as SETNX from '../commands/SETNX'; import * as SETNX from '../commands/SETNX';
import * as SETRANGE from '../commands/SETRANGE'; import * as SETRANGE from '../commands/SETRANGE';
import * as SINTER from '../commands/SINTER'; import * as SINTER from '../commands/SINTER';
import * as SINTERCARD from '../commands/SINTERCARD';
import * as SINTERSTORE from '../commands/SINTERSTORE'; import * as SINTERSTORE from '../commands/SINTERSTORE';
import * as SISMEMBER from '../commands/SISMEMBER'; import * as SISMEMBER from '../commands/SISMEMBER';
import * as SMEMBERS from '../commands/SMEMBERS'; import * as SMEMBERS from '../commands/SMEMBERS';
import * as SMISMEMBER from '../commands/SMISMEMBER'; import * as SMISMEMBER from '../commands/SMISMEMBER';
import * as SMOVE from '../commands/SMOVE'; import * as SMOVE from '../commands/SMOVE';
import * as SORT_RO from '../commands/SORT_RO';
import * as SORT_STORE from '../commands/SORT_STORE';
import * as SORT from '../commands/SORT'; import * as SORT from '../commands/SORT';
import * as SPOP from '../commands/SPOP'; import * as SPOP from '../commands/SPOP';
import * as SRANDMEMBER_COUNT from '../commands/SRANDMEMBER_COUNT'; import * as SRANDMEMBER_COUNT from '../commands/SRANDMEMBER_COUNT';
@@ -149,6 +158,7 @@ import * as ZDIFFSTORE from '../commands/ZDIFFSTORE';
import * as ZINCRBY from '../commands/ZINCRBY'; import * as ZINCRBY from '../commands/ZINCRBY';
import * as ZINTER_WITHSCORES from '../commands/ZINTER_WITHSCORES'; import * as ZINTER_WITHSCORES from '../commands/ZINTER_WITHSCORES';
import * as ZINTER from '../commands/ZINTER'; import * as ZINTER from '../commands/ZINTER';
import * as ZINTERCARD from '../commands/ZINTERCARD';
import * as ZINTERSTORE from '../commands/ZINTERSTORE'; import * as ZINTERSTORE from '../commands/ZINTERSTORE';
import * as ZLEXCOUNT from '../commands/ZLEXCOUNT'; import * as ZLEXCOUNT from '../commands/ZLEXCOUNT';
import * as ZMSCORE from '../commands/ZMSCORE'; import * as ZMSCORE from '../commands/ZMSCORE';
@@ -222,6 +232,8 @@ export default {
expire: EXPIRE, expire: EXPIRE,
EXPIREAT, EXPIREAT,
expireAt: EXPIREAT, expireAt: EXPIREAT,
EXPIRETIME,
expireTime: EXPIRETIME,
GEOADD, GEOADD,
geoAdd: GEOADD, geoAdd: GEOADD,
GEODIST, GEODIST,
@@ -324,12 +336,22 @@ export default {
mSet: MSET, mSet: MSET,
MSETNX, MSETNX,
mSetNX: MSETNX, mSetNX: MSETNX,
OBJECT_ENCODING,
objectEncoding: OBJECT_ENCODING,
OBJECT_FREQ,
objectFreq: OBJECT_FREQ,
OBJECT_IDLETIME,
objectIdleTime: OBJECT_IDLETIME,
OBJECT_REFCOUNT,
objectRefCount: OBJECT_REFCOUNT,
PERSIST, PERSIST,
persist: PERSIST, persist: PERSIST,
PEXPIRE, PEXPIRE,
pExpire: PEXPIRE, pExpire: PEXPIRE,
PEXPIREAT, PEXPIREAT,
pExpireAt: PEXPIREAT, pExpireAt: PEXPIREAT,
PEXPIRETIME,
pExpireTime: PEXPIRETIME,
PFADD, PFADD,
pfAdd: PFADD, pfAdd: PFADD,
PFCOUNT, PFCOUNT,
@@ -366,6 +388,8 @@ export default {
sDiffStore: SDIFFSTORE, sDiffStore: SDIFFSTORE,
SINTER, SINTER,
sInter: SINTER, sInter: SINTER,
SINTERCARD,
sInterCard: SINTERCARD,
SINTERSTORE, SINTERSTORE,
sInterStore: SINTERSTORE, sInterStore: SINTERSTORE,
SET, SET,
@@ -386,6 +410,10 @@ export default {
smIsMember: SMISMEMBER, smIsMember: SMISMEMBER,
SMOVE, SMOVE,
sMove: SMOVE, sMove: SMOVE,
SORT_RO,
sortRo: SORT_RO,
SORT_STORE,
sortStore: SORT_STORE,
SORT, SORT,
sort: SORT, sort: SORT,
SPOP, SPOP,
@@ -478,6 +506,8 @@ export default {
zInterWithScores: ZINTER_WITHSCORES, zInterWithScores: ZINTER_WITHSCORES,
ZINTER, ZINTER,
zInter: ZINTER, zInter: ZINTER,
ZINTERCARD,
zInterCard: ZINTERCARD,
ZINTERSTORE, ZINTERSTORE,
zInterStore: ZINTERSTORE, zInterStore: ZINTERSTORE,
ZLEXCOUNT, ZLEXCOUNT,

View File

@@ -13,20 +13,32 @@ describe('ACL GETUSER', () => {
}); });
testUtils.testWithClient('client.aclGetUser', async client => { testUtils.testWithClient('client.aclGetUser', async client => {
const expectedReply: any = {
passwords: [],
commands: '+@all',
};
if (testUtils.isVersionGreaterThan([7])) {
expectedReply.flags = ['on', 'nopass'];
expectedReply.keys = '~*';
expectedReply.channels = '&*';
expectedReply.selectors = [];
} else {
expectedReply.keys = ['*'];
expectedReply.selectors = undefined;
if (testUtils.isVersionGreaterThan([6, 2])) {
expectedReply.flags = ['on', 'allkeys', 'allchannels', 'allcommands', 'nopass'];
expectedReply.channels = ['*'];
} else {
expectedReply.flags = ['on', 'allkeys', 'allcommands', 'nopass'];
expectedReply.channels = undefined;
}
}
assert.deepEqual( assert.deepEqual(
await client.aclGetUser('default'), await client.aclGetUser('default'),
{ expectedReply
passwords: [],
commands: '+@all',
keys: ['*'],
...(testUtils.isVersionGreaterThan([6, 2]) ? {
flags: ['on', 'allkeys', 'allchannels', 'allcommands', 'nopass'],
channels: ['*']
} : {
flags: ['on', 'allkeys', 'allcommands', 'nopass'],
channels: undefined
})
}
); );
}, GLOBAL.SERVERS.OPEN); }, GLOBAL.SERVERS.OPEN);
}); });

View File

@@ -5,24 +5,27 @@ export function transformArguments(username: RedisCommandArgument): RedisCommand
} }
type AclGetUserRawReply = [ type AclGetUserRawReply = [
_: RedisCommandArgument, 'flags',
flags: Array<RedisCommandArgument>, Array<RedisCommandArgument>,
_: RedisCommandArgument, 'passwords',
passwords: Array<RedisCommandArgument>, Array<RedisCommandArgument>,
_: RedisCommandArgument, 'commands',
commands: RedisCommandArgument, RedisCommandArgument,
_: RedisCommandArgument, 'keys',
keys: Array<RedisCommandArgument>, Array<RedisCommandArgument> | RedisCommandArgument,
_: RedisCommandArgument, 'channels',
channels: Array<RedisCommandArgument> Array<RedisCommandArgument> | RedisCommandArgument,
'selectors' | undefined,
Array<Array<string>> | undefined
]; ];
interface AclUser { interface AclUser {
flags: Array<RedisCommandArgument>; flags: Array<RedisCommandArgument>;
passwords: Array<RedisCommandArgument>; passwords: Array<RedisCommandArgument>;
commands: RedisCommandArgument; commands: RedisCommandArgument;
keys: Array<RedisCommandArgument>; keys: Array<RedisCommandArgument> | RedisCommandArgument;
channels: Array<RedisCommandArgument> channels: Array<RedisCommandArgument> | RedisCommandArgument;
selectors?: Array<Array<string>>;
} }
export function transformReply(reply: AclGetUserRawReply): AclUser { export function transformReply(reply: AclGetUserRawReply): AclUser {
@@ -31,6 +34,7 @@ export function transformReply(reply: AclGetUserRawReply): AclUser {
passwords: reply[3], passwords: reply[3],
commands: reply[5], commands: reply[5],
keys: reply[7], keys: reply[7],
channels: reply[9] channels: reply[9],
selectors: reply[11]
}; };
} }

View File

@@ -11,14 +11,27 @@ describe('BITCOUNT', () => {
); );
}); });
it('with range', () => { describe('with range', () => {
assert.deepEqual( it('simple', () => {
transformArguments('key', { assert.deepEqual(
start: 0, transformArguments('key', {
end: 1 start: 0,
}), end: 1
['BITCOUNT', 'key', '0', '1'] }),
); ['BITCOUNT', 'key', '0', '1']
);
});
it('with mode', () => {
assert.deepEqual(
transformArguments('key', {
start: 0,
end: 1,
mode: 'BIT'
}),
['BITCOUNT', 'key', '0', '1', 'BIT']
);
});
}); });
}); });

View File

@@ -7,6 +7,7 @@ export const IS_READ_ONLY = true;
interface BitCountRange { interface BitCountRange {
start: number; start: number;
end: number; end: number;
mode?: 'BYTE' | 'BIT';
} }
export function transformArguments( export function transformArguments(
@@ -20,6 +21,10 @@ export function transformArguments(
range.start.toString(), range.start.toString(),
range.end.toString() range.end.toString()
); );
if (range.mode) {
args.push(range.mode);
}
} }
return args; return args;

View File

@@ -18,12 +18,19 @@ describe('BITPOS', () => {
); );
}); });
it('with start, end', () => { it('with start and end', () => {
assert.deepEqual( assert.deepEqual(
transformArguments('key', 1, 1, -1), transformArguments('key', 1, 1, -1),
['BITPOS', 'key', '1', '1', '-1'] ['BITPOS', 'key', '1', '1', '-1']
); );
}); });
it('with start, end and mode', () => {
assert.deepEqual(
transformArguments('key', 1, 1, -1, 'BIT'),
['BITPOS', 'key', '1', '1', '-1', 'BIT']
);
});
}); });
testUtils.testWithClient('client.bitPos', async client => { testUtils.testWithClient('client.bitPos', async client => {

View File

@@ -9,7 +9,8 @@ export function transformArguments(
key: RedisCommandArgument, key: RedisCommandArgument,
bit: BitValue, bit: BitValue,
start?: number, start?: number,
end?: number end?: number,
mode?: 'BYTE' | 'BIT'
): RedisCommandArguments { ): RedisCommandArguments {
const args = ['BITPOS', key, bit.toString()]; const args = ['BITPOS', key, bit.toString()];
@@ -21,6 +22,10 @@ export function transformArguments(
args.push(end.toString()); args.push(end.toString());
} }
if (mode) {
args.push(mode);
}
return args; return args;
} }

View File

@@ -1,13 +1,11 @@
export function transformArguments(slots: number | Array<number>): Array<string> { import { RedisCommandArguments } from '.';
const args = ['CLUSTER', 'ADDSLOTS']; import { pushVerdictNumberArguments } from './generic-transformers';
if (typeof slots === 'number') { export function transformArguments(slots: number | Array<number>): RedisCommandArguments {
args.push(slots.toString()); return pushVerdictNumberArguments(
} else { ['CLUSTER', 'ADDSLOTS'],
args.push(...slots.map(String)); slots
} );
return args;
} }
export declare function transformReply(): string; export declare function transformReply(): string;

View File

@@ -0,0 +1,29 @@
import { strict as assert } from 'assert';
import { transformArguments } from './CLUSTER_ADDSLOTSRANGE';
describe('CLUSTER ADDSLOTSRANGE', () => {
describe('transformArguments', () => {
it('single', () => {
assert.deepEqual(
transformArguments({
start: 0,
end: 1
}),
['CLUSTER', 'ADDSLOTSRANGE', '0', '1']
);
});
it('multiple', () => {
assert.deepEqual(
transformArguments([{
start: 0,
end: 1
}, {
start: 2,
end: 3
}]),
['CLUSTER', 'ADDSLOTSRANGE', '0', '1', '2', '3']
);
});
});
});

View File

@@ -0,0 +1,13 @@
import { RedisCommandArguments } from '.';
import { pushSlotRangesArguments, SlotRange } from './generic-transformers';
export function transformArguments(
ranges: SlotRange | Array<SlotRange>
): RedisCommandArguments {
return pushSlotRangesArguments(
['CLUSTER', 'ADDSLOTSRANGE'],
ranges
);
}
export declare function transformReply(): 'OK';

View File

@@ -0,0 +1,19 @@
import { strict as assert } from 'assert';
import testUtils, { GLOBAL } from '../test-utils';
import { transformArguments } from './CLUSTER_BUMPEPOCH';
describe('CLUSTER BUMPEPOCH', () => {
it('transformArguments', () => {
assert.deepEqual(
transformArguments(),
['CLUSTER', 'BUMPEPOCH']
);
});
testUtils.testWithCluster('clusterNode.clusterBumpEpoch', async cluster => {
assert.equal(
typeof await cluster.getSlotMaster(0).client.clusterBumpEpoch(),
'string'
);
}, GLOBAL.SERVERS.OPEN);
});

View File

@@ -0,0 +1,5 @@
export function transformArguments(): Array<string> {
return ['CLUSTER', 'BUMPEPOCH'];
}
export declare function transformReply(): 'BUMPED' | 'STILL';

View File

@@ -0,0 +1,22 @@
import { strict as assert } from 'assert';
import testUtils, { GLOBAL } from '../test-utils';
import { transformArguments } from './CLUSTER_COUNT-FAILURE-REPORTS';
describe('CLUSTER COUNT-FAILURE-REPORTS', () => {
it('transformArguments', () => {
assert.deepEqual(
transformArguments('0'),
['CLUSTER', 'COUNT-FAILURE-REPORTS', '0']
);
});
testUtils.testWithCluster('clusterNode.clusterCountFailureReports', async cluster => {
const { client } = cluster.getSlotMaster(0);
assert.equal(
typeof await client.clusterCountFailureReports(
await client.clusterMyId()
),
'number'
);
}, GLOBAL.CLUSTERS.OPEN);
});

View File

@@ -0,0 +1,5 @@
export function transformArguments(nodeId: string): Array<string> {
return ['CLUSTER', 'COUNT-FAILURE-REPORTS', nodeId];
}
export declare function transformReply(): number;

View File

@@ -0,0 +1,19 @@
import { strict as assert } from 'assert';
import testUtils, { GLOBAL } from '../test-utils';
import { transformArguments } from './CLUSTER_COUNTKEYSINSLOT';
describe('CLUSTER COUNTKEYSINSLOT', () => {
it('transformArguments', () => {
assert.deepEqual(
transformArguments(0),
['CLUSTER', 'COUNTKEYSINSLOT', '0']
);
});
testUtils.testWithCluster('clusterNode.clusterCountKeysInSlot', async cluster => {
assert.equal(
typeof await cluster.getSlotMaster(0).client.clusterCountKeysInSlot(0),
'number'
);
}, GLOBAL.CLUSTERS.OPEN);
});

View File

@@ -0,0 +1,5 @@
export function transformArguments(slot: number): Array<string> {
return ['CLUSTER', 'COUNTKEYSINSLOT', slot.toString()];
}
export declare function transformReply(): number;

View File

@@ -0,0 +1,20 @@
import { strict as assert } from 'assert';
import { transformArguments } from './CLUSTER_DELSLOTS';
describe('CLUSTER DELSLOTS', () => {
describe('transformArguments', () => {
it('single', () => {
assert.deepEqual(
transformArguments(0),
['CLUSTER', 'DELSLOTS', '0']
);
});
it('multiple', () => {
assert.deepEqual(
transformArguments([0, 1]),
['CLUSTER', 'DELSLOTS', '0', '1']
);
});
});
});

View File

@@ -0,0 +1,11 @@
import { RedisCommandArguments } from '.';
import { pushVerdictNumberArguments } from './generic-transformers';
export function transformArguments(slots: number | Array<number>): RedisCommandArguments {
return pushVerdictNumberArguments(
['CLUSTER', 'DELSLOTS'],
slots
);
}
export declare function transformReply(): 'OK';

View File

@@ -0,0 +1,29 @@
import { strict as assert } from 'assert';
import { transformArguments } from './CLUSTER_DELSLOTSRANGE';
describe('CLUSTER DELSLOTSRANGE', () => {
describe('transformArguments', () => {
it('single', () => {
assert.deepEqual(
transformArguments({
start: 0,
end: 1
}),
['CLUSTER', 'DELSLOTSRANGE', '0', '1']
);
});
it('multiple', () => {
assert.deepEqual(
transformArguments([{
start: 0,
end: 1
}, {
start: 2,
end: 3
}]),
['CLUSTER', 'DELSLOTSRANGE', '0', '1', '2', '3']
);
});
});
});

View File

@@ -0,0 +1,13 @@
import { RedisCommandArguments } from '.';
import { pushSlotRangesArguments, SlotRange } from './generic-transformers';
export function transformArguments(
ranges: SlotRange | Array<SlotRange>
): RedisCommandArguments {
return pushSlotRangesArguments(
['CLUSTER', 'DELSLOTSRANGE'],
ranges
);
}
export declare function transformReply(): 'OK';

View File

@@ -0,0 +1,20 @@
import { strict as assert } from 'assert';
import { FailoverModes, transformArguments } from './CLUSTER_FAILOVER';
describe('CLUSTER FAILOVER', () => {
describe('transformArguments', () => {
it('simple', () => {
assert.deepEqual(
transformArguments(),
['CLUSTER', 'FAILOVER']
);
});
it('with mode', () => {
assert.deepEqual(
transformArguments(FailoverModes.FORCE),
['CLUSTER', 'FAILOVER', 'FORCE']
);
});
});
});

View File

@@ -0,0 +1,16 @@
export enum FailoverModes {
FORCE = 'FORCE',
TAKEOVER = 'TAKEOVER'
}
export function transformArguments(mode?: FailoverModes): Array<string> {
const args = ['CLUSTER', 'FAILOVER'];
if (mode) {
args.push(mode);
}
return args;
}
export declare function transformReply(): 'OK';

View File

@@ -2,4 +2,4 @@ export function transformArguments(): Array<string> {
return ['CLUSTER', 'FLUSHSLOTS']; return ['CLUSTER', 'FLUSHSLOTS'];
} }
export declare function transformReply(): string; export declare function transformReply(): 'OK';

View File

@@ -0,0 +1,11 @@
import { strict as assert } from 'assert';
import { transformArguments } from './CLUSTER_FORGET';
describe('CLUSTER FORGET', () => {
it('transformArguments', () => {
assert.deepEqual(
transformArguments('0'),
['CLUSTER', 'FORGET', '0']
);
});
});

View File

@@ -0,0 +1,5 @@
export function transformArguments(nodeId: string): Array<string> {
return ['CLUSTER', 'FORGET', nodeId];
}
export declare function transformReply(): 'OK';

View File

@@ -1,4 +1,5 @@
import { strict as assert } from 'assert'; import { strict as assert } from 'assert';
import testUtils, { GLOBAL } from '../test-utils';
import { transformArguments } from './CLUSTER_GETKEYSINSLOT'; import { transformArguments } from './CLUSTER_GETKEYSINSLOT';
describe('CLUSTER GETKEYSINSLOT', () => { describe('CLUSTER GETKEYSINSLOT', () => {
@@ -8,4 +9,12 @@ describe('CLUSTER GETKEYSINSLOT', () => {
['CLUSTER', 'GETKEYSINSLOT', '0', '10'] ['CLUSTER', 'GETKEYSINSLOT', '0', '10']
); );
}); });
testUtils.testWithCluster('clusterNode.clusterGetKeysInSlot', async cluster => {
const reply = await cluster.getSlotMaster(0).client.clusterGetKeysInSlot(0, 1);
assert.ok(Array.isArray(reply));
for (const item of reply) {
assert.equal(typeof item, 'string');
}
}, GLOBAL.CLUSTERS.OPEN);
}); });

View File

@@ -2,4 +2,4 @@ export function transformArguments(slot: number, count: number): Array<string> {
return ['CLUSTER', 'GETKEYSINSLOT', slot.toString(), count.toString()]; return ['CLUSTER', 'GETKEYSINSLOT', slot.toString(), count.toString()];
} }
export declare function transformReply(): string; export declare function transformReply(): Array<string>;

View File

@@ -1,4 +1,5 @@
import { strict as assert } from 'assert'; import { strict as assert } from 'assert';
import testUtils, { GLOBAL } from '../test-utils';
import { transformArguments, transformReply } from './CLUSTER_INFO'; import { transformArguments, transformReply } from './CLUSTER_INFO';
describe('CLUSTER INFO', () => { describe('CLUSTER INFO', () => {
@@ -43,4 +44,11 @@ describe('CLUSTER INFO', () => {
} }
); );
}); });
testUtils.testWithCluster('clusterNode.clusterInfo', async cluster => {
assert.notEqual(
await cluster.getSlotMaster(0).client.clusterInfo(),
null
);
}, GLOBAL.CLUSTERS.OPEN);
}); });

View File

@@ -0,0 +1,19 @@
import { strict as assert } from 'assert';
import testUtils, { GLOBAL } from '../test-utils';
import { transformArguments } from './CLUSTER_KEYSLOT';
describe('CLUSTER KEYSLOT', () => {
it('transformArguments', () => {
assert.deepEqual(
transformArguments('key'),
['CLUSTER', 'KEYSLOT', 'key']
);
});
testUtils.testWithCluster('clusterNode.clusterKeySlot', async cluster => {
assert.equal(
typeof await cluster.getSlotMaster(0).client.clusterKeySlot('key'),
'number'
);
}, GLOBAL.CLUSTERS.OPEN);
});

View File

@@ -0,0 +1,5 @@
export function transformArguments(key: string): Array<string> {
return ['CLUSTER', 'KEYSLOT', key];
}
export declare function transformReply(): number;

View File

@@ -0,0 +1,27 @@
import { strict as assert } from 'assert';
import testUtils, { GLOBAL } from '../test-utils';
import { transformArguments } from './CLUSTER_LINKS';
describe('CLUSTER LINKS', () => {
testUtils.isVersionGreaterThanHook([7, 0]);
it('transformArguments', () => {
assert.deepEqual(
transformArguments(),
['CLUSTER', 'LINKS']
);
});
testUtils.testWithCluster('clusterNode.clusterLinks', async cluster => {
const links = await cluster.getSlotMaster(0).client.clusterLinks();
assert.ok(Array.isArray(links));
for (const link of links) {
assert.equal(typeof link.direction, 'string');
assert.equal(typeof link.node, 'string');
assert.equal(typeof link.createTime, 'number');
assert.equal(typeof link.events, 'string');
assert.equal(typeof link.sendBufferAllocated, 'number');
assert.equal(typeof link.sendBufferUsed, 'number');
}
}, GLOBAL.CLUSTERS.OPEN);
});

View File

@@ -0,0 +1,38 @@
export function transformArguments(): Array<string> {
return ['CLUSTER', 'LINKS'];
}
type ClusterLinksRawReply = Array<[
'direction',
string,
'node',
string,
'createTime',
number,
'events',
string,
'send-buffer-allocated',
number,
'send-buffer-used',
number
]>;
type ClusterLinksReply = Array<{
direction: string;
node: string;
createTime: number;
events: string;
sendBufferAllocated: number;
sendBufferUsed: number;
}>;
export function transformReply(reply: ClusterLinksRawReply): ClusterLinksReply {
return reply.map(peerLink => ({
direction: peerLink[1],
node: peerLink[3],
createTime: Number(peerLink[5]),
events: peerLink[7],
sendBufferAllocated: Number(peerLink[9]),
sendBufferUsed: Number(peerLink[11])
}));
}

View File

@@ -2,4 +2,4 @@ export function transformArguments(ip: string, port: number): Array<string> {
return ['CLUSTER', 'MEET', ip, port.toString()]; return ['CLUSTER', 'MEET', ip, port.toString()];
} }
export declare function transformReply(): string; export declare function transformReply(): 'OK';

View File

@@ -0,0 +1,19 @@
import { strict as assert } from 'assert';
import testUtils, { GLOBAL } from '../test-utils';
import { transformArguments } from './CLUSTER_MYID';
describe('CLUSTER MYID', () => {
it('transformArguments', () => {
assert.deepEqual(
transformArguments(),
['CLUSTER', 'MYID']
);
});
testUtils.testWithCluster('clusterNode.clusterMyId', async cluster => {
assert.equal(
typeof await cluster.getSlotMaster(0).client.clusterMyId(),
'string'
);
}, GLOBAL.CLUSTERS.OPEN);
});

View File

@@ -0,0 +1,5 @@
export function transformArguments(): Array<string> {
return ['CLUSTER', 'MYID'];
}
export declare function transformReply(): string;

View File

@@ -0,0 +1,11 @@
import { strict as assert } from 'assert';
import { transformArguments } from './CLUSTER_REPLICAS';
describe('CLUSTER REPLICAS', () => {
it('transformArguments', () => {
assert.deepEqual(
transformArguments('0'),
['CLUSTER', 'REPLICAS', '0']
);
});
});

View File

@@ -0,0 +1,5 @@
export function transformArguments(nodeId: string): Array<string> {
return ['CLUSTER', 'REPLICAS', nodeId];
}
export { transformReply } from './CLUSTER_NODES';

View File

@@ -0,0 +1,11 @@
import { strict as assert } from 'assert';
import { transformArguments } from './CLUSTER_REPLICATE';
describe('CLUSTER REPLICATE', () => {
it('transformArguments', () => {
assert.deepEqual(
transformArguments('0'),
['CLUSTER', 'REPLICATE', '0']
);
});
});

View File

@@ -0,0 +1,5 @@
export function transformArguments(nodeId: string): Array<string> {
return ['CLUSTER', 'REPLICATE', nodeId];
}
export declare function transformReply(): 'OK';

View File

@@ -10,18 +10,11 @@ describe('CLUSTER RESET', () => {
); );
}); });
it('HARD', () => { it('with mode', () => {
assert.deepEqual( assert.deepEqual(
transformArguments('HARD'), transformArguments('HARD'),
['CLUSTER', 'RESET', 'HARD'] ['CLUSTER', 'RESET', 'HARD']
); );
}); });
it('SOFT', () => {
assert.deepEqual(
transformArguments('SOFT'),
['CLUSTER', 'RESET', 'SOFT']
);
});
}); });
}); });

View File

@@ -1,6 +1,4 @@
export type ClusterResetModes = 'HARD' | 'SOFT'; export function transformArguments(mode?: 'HARD' | 'SOFT'): Array<string> {
export function transformArguments(mode?: ClusterResetModes): Array<string> {
const args = ['CLUSTER', 'RESET']; const args = ['CLUSTER', 'RESET'];
if (mode) { if (mode) {

View File

@@ -0,0 +1,19 @@
import { strict as assert } from 'assert';
import testUtils, { GLOBAL } from '../test-utils';
import { transformArguments } from './CLUSTER_SAVECONFIG';
describe('CLUSTER SAVECONFIG', () => {
it('transformArguments', () => {
assert.deepEqual(
transformArguments(),
['CLUSTER', 'SAVECONFIG']
);
});
testUtils.testWithCluster('clusterNode.clusterSaveConfig', async cluster => {
assert.equal(
await cluster.getSlotMaster(0).client.clusterSaveConfig(),
'OK'
);
}, GLOBAL.CLUSTERS.OPEN);
});

View File

@@ -0,0 +1,5 @@
export function transformArguments(): Array<string> {
return ['CLUSTER', 'SAVECONFIG'];
}
export declare function transformReply(): 'OK';

View File

@@ -0,0 +1,11 @@
import { strict as assert } from 'assert';
import { transformArguments } from './CLUSTER_SET-CONFIG-EPOCH';
describe('CLUSTER SET-CONFIG-EPOCH', () => {
it('transformArguments', () => {
assert.deepEqual(
transformArguments(0),
['CLUSTER', 'SET-CONFIG-EPOCH', '0']
);
});
});

View File

@@ -0,0 +1,5 @@
export function transformArguments(configEpoch: number): Array<string> {
return ['CLUSTER', 'SET-CONFIG-EPOCH', configEpoch.toString()];
}
export declare function transformReply(): 'OK';

View File

@@ -5,7 +5,11 @@ export enum ClusterSlotStates {
NODE = 'NODE' NODE = 'NODE'
} }
export function transformArguments(slot: number, state: ClusterSlotStates, nodeId?: string): Array<string> { export function transformArguments(
slot: number,
state: ClusterSlotStates,
nodeId?: string
): Array<string> {
const args = ['CLUSTER', 'SETSLOT', slot.toString(), state]; const args = ['CLUSTER', 'SETSLOT', slot.toString(), state];
if (nodeId) { if (nodeId) {
@@ -15,4 +19,4 @@ export function transformArguments(slot: number, state: ClusterSlotStates, nodeI
return args; return args;
} }
export declare function transformReply(): string; export declare function transformReply(): 'OK';

View File

@@ -6,7 +6,12 @@ export function transformArguments(): RedisCommandArguments {
type ClusterSlotsRawNode = [ip: string, port: number, id: string]; type ClusterSlotsRawNode = [ip: string, port: number, id: string];
type ClusterSlotsRawReply = Array<[from: number, to: number, master: ClusterSlotsRawNode, ...replicas: Array<ClusterSlotsRawNode>]>; type ClusterSlotsRawReply = Array<[
from: number,
to: number,
master: ClusterSlotsRawNode,
...replicas: Array<ClusterSlotsRawNode>
]>;
type ClusterSlotsNode = { type ClusterSlotsNode = {
ip: string; ip: string;

View File

@@ -0,0 +1,24 @@
import { strict as assert } from 'assert';
import testUtils, { GLOBAL } from '../test-utils';
import { transformArguments } from './COMMAND_GETKEYSANDFLAGS';
describe('COMMAND GETKEYSANDFLAGS', () => {
testUtils.isVersionGreaterThanHook([7, 0]);
it('transformArguments', () => {
assert.deepEqual(
transformArguments(['GET', 'key']),
['COMMAND', 'GETKEYSANDFLAGS', 'GET', 'key']
);
});
testUtils.testWithClient('client.commandGetKeysAndFlags', async client => {
assert.deepEqual(
await client.commandGetKeysAndFlags(['GET', 'key']),
[{
key: 'key',
flags: ['RO', 'access']
}]
);
}, GLOBAL.SERVERS.OPEN);
});

View File

@@ -0,0 +1,24 @@
import { RedisCommandArgument, RedisCommandArguments } from '.';
export const IS_READ_ONLY = true;
export function transformArguments(args: Array<RedisCommandArgument>): RedisCommandArguments {
return ['COMMAND', 'GETKEYSANDFLAGS', ...args];
}
type KeysAndFlagsRawReply = Array<[
RedisCommandArgument,
RedisCommandArguments
]>;
type KeysAndFlagsReply = Array<{
key: RedisCommandArgument;
flags: RedisCommandArguments;
}>;
export function transformReply(reply: KeysAndFlagsRawReply): KeysAndFlagsReply {
return reply.map(([key, flags]) => ({
key,
flags
}));
}

View File

@@ -9,7 +9,11 @@ export function assertPingCommand(commandInfo: CommandReply | null | undefined):
{ {
name: 'ping', name: 'ping',
arity: -1, arity: -1,
flags: new Set([CommandFlags.STALE, CommandFlags.FAST]), flags: new Set(
testUtils.isVersionGreaterThan([7]) ?
[CommandFlags.FAST] :
[CommandFlags.STALE, CommandFlags.FAST]
),
firstKeyIndex: 0, firstKeyIndex: 0,
lastKeyIndex: 0, lastKeyIndex: 0,
step: 0, step: 0,

View File

@@ -2,10 +2,23 @@ import { strict as assert } from 'assert';
import { transformArguments } from './CONFIG_SET'; import { transformArguments } from './CONFIG_SET';
describe('CONFIG SET', () => { describe('CONFIG SET', () => {
it('transformArguments', () => { describe('transformArguments', () => {
assert.deepEqual( it('set one parameter (old version)', () => {
transformArguments('parameter', 'value'), assert.deepEqual(
['CONFIG', 'SET', 'parameter', 'value'] transformArguments('parameter', 'value'),
); ['CONFIG', 'SET', 'parameter', 'value']
);
});
it('set muiltiple parameters', () => {
assert.deepEqual(
transformArguments({
1: 'a',
2: 'b',
3: 'c'
}),
['CONFIG', 'SET', '1', 'a', '2', 'b', '3', 'c']
);
});
}); });
}); });

View File

@@ -1,5 +1,23 @@
export function transformArguments(parameter: string, value: string): Array<string> { import { RedisCommandArgument, RedisCommandArguments } from '.';
return ['CONFIG', 'SET', parameter, value];
type SingleParameter = [parameter: RedisCommandArgument, value: RedisCommandArgument];
type MultipleParameters = [config: Record<string, RedisCommandArgument>];
export function transformArguments(
...[parameterOrConfig, value]: SingleParameter | MultipleParameters
): RedisCommandArguments {
const args: RedisCommandArguments = ['CONFIG', 'SET'];
if (typeof parameterOrConfig === 'string') {
args.push(parameterOrConfig, value!);
} else {
for (const [key, value] of Object.entries(parameterOrConfig)) {
args.push(key, value);
}
}
return args;
} }
export declare function transformReply(): string; export declare function transformReply(): string;

View File

@@ -3,11 +3,20 @@ import testUtils, { GLOBAL } from '../test-utils';
import { transformArguments } from './EXPIRE'; import { transformArguments } from './EXPIRE';
describe('EXPIRE', () => { describe('EXPIRE', () => {
it('transformArguments', () => { describe('transformArguments', () => {
assert.deepEqual( it('simple', () => {
transformArguments('key', 1), assert.deepEqual(
['EXPIRE', 'key', '1'] transformArguments('key', 1),
); ['EXPIRE', 'key', '1']
);
});
it('with set option', () => {
assert.deepEqual(
transformArguments('key', 1, 'NX'),
['EXPIRE', 'key', '1', 'NX']
);
});
}); });
testUtils.testWithClient('client.expire', async client => { testUtils.testWithClient('client.expire', async client => {

View File

@@ -4,9 +4,16 @@ export const FIRST_KEY_INDEX = 1;
export function transformArguments( export function transformArguments(
key: RedisCommandArgument, key: RedisCommandArgument,
seconds: number seconds: number,
mode?: 'NX' | 'XX' | 'GT' | 'LT'
): RedisCommandArguments { ): RedisCommandArguments {
return ['EXPIRE', key, seconds.toString()]; const args = ['EXPIRE', key, seconds.toString()];
if (mode) {
args.push(mode);
}
return args;
} }
export { transformBooleanReply as transformReply } from './generic-transformers'; export { transformBooleanReply as transformReply } from './generic-transformers';

View File

@@ -18,6 +18,13 @@ describe('EXPIREAT', () => {
['EXPIREAT', 'key', Math.floor(d.getTime() / 1000).toString()] ['EXPIREAT', 'key', Math.floor(d.getTime() / 1000).toString()]
); );
}); });
it('with set option', () => {
assert.deepEqual(
transformArguments('key', 1, 'GT'),
['EXPIREAT', 'key', '1', 'GT']
);
});
}); });
testUtils.testWithClient('client.expireAt', async client => { testUtils.testWithClient('client.expireAt', async client => {

View File

@@ -5,13 +5,20 @@ export const FIRST_KEY_INDEX = 1;
export function transformArguments( export function transformArguments(
key: RedisCommandArgument, key: RedisCommandArgument,
timestamp: number | Date timestamp: number | Date,
mode?: 'NX' | 'XX' | 'GT' | 'LT'
): RedisCommandArguments { ): RedisCommandArguments {
return [ const args = [
'EXPIREAT', 'EXPIREAT',
key, key,
transformEXAT(timestamp) transformEXAT(timestamp)
]; ];
if (mode) {
args.push(mode);
}
return args;
} }
export { transformBooleanReply as transformReply } from './generic-transformers'; export { transformBooleanReply as transformReply } from './generic-transformers';

View File

@@ -0,0 +1,21 @@
import { strict as assert } from 'assert';
import testUtils, { GLOBAL } from '../test-utils';
import { transformArguments } from './EXPIRETIME';
describe('EXPIRETIME', () => {
testUtils.isVersionGreaterThanHook([7, 0]);
it('transformArguments', () => {
assert.deepEqual(
transformArguments('key'),
['EXPIRETIME', 'key']
);
});
testUtils.testWithClient('client.expireTime', async client => {
assert.equal(
await client.expireTime('key'),
-2
);
}, GLOBAL.SERVERS.OPEN);
});

View File

@@ -0,0 +1,9 @@
import { RedisCommandArgument, RedisCommandArguments } from '.';
export const FIRST_KEY_INDEX = 1;
export function transformArguments(key: RedisCommandArgument): RedisCommandArguments {
return ['EXPIRETIME', key];
}
export declare function transformReply(): number;

View File

@@ -0,0 +1,19 @@
import {strict as assert} from 'assert';
import testUtils, {GLOBAL} from '../test-utils';
import { transformArguments } from './LATENCY_DOCTOR';
describe('LATENCY DOCTOR', () => {
it('transformArguments', () => {
assert.deepEqual(
transformArguments(),
['LATENCY', 'DOCTOR']
);
});
testUtils.testWithClient('client.latencyDoctor', async client => {
assert.equal(
typeof (await client.latencyDoctor()),
'string'
);
}, GLOBAL.SERVERS.OPEN);
});

View File

@@ -0,0 +1,5 @@
export function transformArguments(): Array<string> {
return ['LATENCY', 'DOCTOR'];
}
export declare function transformReply(): string;

View File

@@ -0,0 +1,19 @@
import { strict as assert } from 'assert';
import testUtils, { GLOBAL } from '../test-utils';
import { transformArguments } from './OBJECT_ENCODING';
describe('OBJECT ENCODING', () => {
it('transformArguments', () => {
assert.deepEqual(
transformArguments('key'),
['OBJECT', 'ENCODING', 'key']
);
});
testUtils.testWithClient('client.objectEncoding', async client => {
assert.equal(
await client.objectEncoding('key'),
null
);
}, GLOBAL.SERVERS.OPEN);
});

View File

@@ -0,0 +1,11 @@
import { RedisCommandArgument, RedisCommandArguments } from '.';
export const FIRST_KEY_INDEX = 2;
export const IS_READ_ONLY = true;
export function transformArguments(key: RedisCommandArgument): RedisCommandArguments {
return ['OBJECT', 'ENCODING', key];
}
export declare function transformReply(): string | null;

View File

@@ -0,0 +1,19 @@
import { strict as assert } from 'assert';
import testUtils, { GLOBAL } from '../test-utils';
import { transformArguments } from './OBJECT_FREQ';
describe('OBJECT FREQ', () => {
it('transformArguments', () => {
assert.deepEqual(
transformArguments('key'),
['OBJECT', 'FREQ', 'key']
);
});
testUtils.testWithClient('client.objectFreq', async client => {
assert.equal(
await client.objectFreq('key'),
null
);
}, GLOBAL.SERVERS.OPEN);
});

View File

@@ -0,0 +1,11 @@
import { RedisCommandArgument, RedisCommandArguments } from '.';
export const FIRST_KEY_INDEX = 2;
export const IS_READ_ONLY = true;
export function transformArguments(key: RedisCommandArgument): RedisCommandArguments {
return ['OBJECT', 'FREQ', key];
}
export declare function transformReply(): number | null;

View File

@@ -0,0 +1,19 @@
import { strict as assert } from 'assert';
import testUtils, { GLOBAL } from '../test-utils';
import { transformArguments } from './OBJECT_IDLETIME';
describe('OBJECT IDLETIME', () => {
it('transformArguments', () => {
assert.deepEqual(
transformArguments('key'),
['OBJECT', 'IDLETIME', 'key']
);
});
testUtils.testWithClient('client.objectIdleTime', async client => {
assert.equal(
await client.objectIdleTime('key'),
null
);
}, GLOBAL.SERVERS.OPEN);
});

View File

@@ -0,0 +1,11 @@
import { RedisCommandArgument, RedisCommandArguments } from '.';
export const FIRST_KEY_INDEX = 2;
export const IS_READ_ONLY = true;
export function transformArguments(key: RedisCommandArgument): RedisCommandArguments {
return ['OBJECT', 'IDLETIME', key];
}
export declare function transformReply(): number | null;

View File

@@ -0,0 +1,19 @@
import { strict as assert } from 'assert';
import testUtils, { GLOBAL } from '../test-utils';
import { transformArguments } from './OBJECT_REFCOUNT';
describe('OBJECT REFCOUNT', () => {
it('transformArguments', () => {
assert.deepEqual(
transformArguments('key'),
['OBJECT', 'REFCOUNT', 'key']
);
});
testUtils.testWithClient('client.objectRefCount', async client => {
assert.equal(
await client.objectRefCount('key'),
null
);
}, GLOBAL.SERVERS.OPEN);
});

View File

@@ -0,0 +1,11 @@
import { RedisCommandArgument, RedisCommandArguments } from '.';
export const FIRST_KEY_INDEX = 2;
export const IS_READ_ONLY = true;
export function transformArguments(key: RedisCommandArgument): RedisCommandArguments {
return ['OBJECT', 'REFCOUNT', key];
}
export declare function transformReply(): number | null;

View File

@@ -3,11 +3,20 @@ import testUtils, { GLOBAL } from '../test-utils';
import { transformArguments } from './PEXPIRE'; import { transformArguments } from './PEXPIRE';
describe('PEXPIRE', () => { describe('PEXPIRE', () => {
it('transformArguments', () => { describe('transformArguments', () => {
assert.deepEqual( it('simple', () => {
transformArguments('key', 1), assert.deepEqual(
['PEXPIRE', 'key', '1'] transformArguments('key', 1),
); ['PEXPIRE', 'key', '1']
);
});
it('with set option', () => {
assert.deepEqual(
transformArguments('key', 1, 'GT'),
['PEXPIRE', 'key', '1', 'GT']
);
});
}); });
testUtils.testWithClient('client.pExpire', async client => { testUtils.testWithClient('client.pExpire', async client => {

View File

@@ -4,9 +4,16 @@ export const FIRST_KEY_INDEX = 1;
export function transformArguments( export function transformArguments(
key: RedisCommandArgument, key: RedisCommandArgument,
milliseconds: number milliseconds: number,
mode?: 'NX' | 'XX' | 'GT' | 'LT'
): RedisCommandArguments { ): RedisCommandArguments {
return ['PEXPIRE', key, milliseconds.toString()]; const args = ['PEXPIRE', key, milliseconds.toString()];
if (mode) {
args.push(mode);
}
return args;
} }
export { transformBooleanReply as transformReply } from './generic-transformers'; export { transformBooleanReply as transformReply } from './generic-transformers';

View File

@@ -18,6 +18,13 @@ describe('PEXPIREAT', () => {
['PEXPIREAT', 'key', d.getTime().toString()] ['PEXPIREAT', 'key', d.getTime().toString()]
); );
}); });
it('with set option', () => {
assert.deepEqual(
transformArguments('key', 1, 'XX'),
['PEXPIREAT', 'key', '1', 'XX']
);
});
}); });
testUtils.testWithClient('client.pExpireAt', async client => { testUtils.testWithClient('client.pExpireAt', async client => {

View File

@@ -5,13 +5,20 @@ export const FIRST_KEY_INDEX = 1;
export function transformArguments( export function transformArguments(
key: RedisCommandArgument, key: RedisCommandArgument,
millisecondsTimestamp: number | Date millisecondsTimestamp: number | Date,
mode?: 'NX' | 'XX' | 'GT' | 'LT'
): RedisCommandArguments { ): RedisCommandArguments {
return [ const args = [
'PEXPIREAT', 'PEXPIREAT',
key, key,
transformPXAT(millisecondsTimestamp) transformPXAT(millisecondsTimestamp)
]; ];
if (mode) {
args.push(mode);
}
return args;
} }
export { transformBooleanReply as transformReply } from './generic-transformers'; export { transformBooleanReply as transformReply } from './generic-transformers';

View File

@@ -0,0 +1,21 @@
import { strict as assert } from 'assert';
import testUtils, { GLOBAL } from '../test-utils';
import { transformArguments } from './PEXPIRETIME';
describe('PEXPIRETIME', () => {
testUtils.isVersionGreaterThanHook([7, 0]);
it('transformArguments', () => {
assert.deepEqual(
transformArguments('key'),
['PEXPIRETIME', 'key']
);
});
testUtils.testWithClient('client.pExpireTime', async client => {
assert.equal(
await client.pExpireTime('key'),
-2
);
}, GLOBAL.SERVERS.OPEN);
});

View File

@@ -0,0 +1,9 @@
import { RedisCommandArgument, RedisCommandArguments } from '.';
export const FIRST_KEY_INDEX = 1;
export function transformArguments(key: RedisCommandArgument): RedisCommandArguments {
return ['PEXPIRETIME', key];
}
export declare function transformReply(): number;

View File

@@ -0,0 +1,30 @@
import { strict as assert } from 'assert';
import testUtils, { GLOBAL } from '../test-utils';
import { transformArguments } from './SINTERCARD';
describe('SINTERCARD', () => {
testUtils.isVersionGreaterThanHook([7, 0]);
describe('transformArguments', () => {
it('simple', () => {
assert.deepEqual(
transformArguments(['1', '2']),
['SINTERCARD', '2', '1', '2']
);
});
it('with limit', () => {
assert.deepEqual(
transformArguments(['1', '2'], 1),
['SINTERCARD', '2', '1', '2', 'LIMIT', '1']
);
});
});
testUtils.testWithClient('client.sInterCard', async client => {
assert.deepEqual(
await client.sInterCard('key'),
0
);
}, GLOBAL.SERVERS.OPEN);
});

View File

@@ -0,0 +1,21 @@
import { RedisCommandArgument, RedisCommandArguments } from '.';
import { pushVerdictArgument } from './generic-transformers';
export const FIRST_KEY_INDEX = 2;
export const IS_READ_ONLY = true;
export function transformArguments(
keys: Array<RedisCommandArgument> | RedisCommandArgument,
limit?: number
): RedisCommandArguments {
const args = pushVerdictArgument(['SINTERCARD'], keys);
if (limit) {
args.push('LIMIT', limit.toString());
}
return args;
}
export declare function transformReply(): number;

View File

@@ -70,16 +70,7 @@ describe('SORT', () => {
); );
}); });
it('with STORE', () => { it('with BY, LIMIT, GET, DIRECTION, ALPHA', () => {
assert.deepEqual(
transformArguments('key', {
STORE: 'destination'
}),
['SORT', 'key', 'STORE', 'destination']
);
});
it('with BY, LIMIT, GET, DIRECTION, ALPHA, STORE', () => {
assert.deepEqual( assert.deepEqual(
transformArguments('key', { transformArguments('key', {
BY: 'pattern', BY: 'pattern',
@@ -89,10 +80,9 @@ describe('SORT', () => {
}, },
GET: 'pattern', GET: 'pattern',
DIRECTION: 'ASC', DIRECTION: 'ASC',
ALPHA: true, ALPHA: true
STORE: 'destination'
}), }),
['SORT', 'key', 'BY', 'pattern', 'LIMIT', '0', '1', 'GET', 'pattern', 'ASC', 'ALPHA', 'STORE', 'destination'] ['SORT', 'key', 'BY', 'pattern', 'LIMIT', '0', '1', 'GET', 'pattern', 'ASC', 'ALPHA']
); );
}); });
}); });

View File

@@ -1,56 +1,13 @@
import { RedisCommandArguments } from '.';
import { pushSortArguments, SortOptions } from './generic-transformers';
export const FIRST_KEY_INDEX = 1; export const FIRST_KEY_INDEX = 1;
export const IS_READ_ONLY = true; export function transformArguments(
key: string,
interface SortOptions { options?: SortOptions
BY?: string; ): RedisCommandArguments {
LIMIT?: { return pushSortArguments(['SORT', key], options);
offset: number;
count: number;
},
GET?: string | Array<string>;
DIRECTION?: 'ASC' | 'DESC';
ALPHA?: true;
STORE?: string;
} }
export function transformArguments(key: string, options?: SortOptions): Array<string> { export declare function transformReply(): Array<string>;
const args = ['SORT', key];
if (options?.BY) {
args.push('BY', options.BY);
}
if (options?.LIMIT) {
args.push(
'LIMIT',
options.LIMIT.offset.toString(),
options.LIMIT.count.toString()
);
}
if (options?.GET) {
for (const pattern of (typeof options.GET === 'string' ? [options.GET] : options.GET)) {
args.push('GET', pattern);
}
}
if (options?.DIRECTION) {
args.push(options.DIRECTION);
}
if (options?.ALPHA) {
args.push('ALPHA');
}
if (options?.STORE) {
args.push('STORE', options.STORE);
}
return args;
}
// integer when using `STORE`
export function transformReply(reply: Array<string> | number): Array<string> | number {
return reply;
}

View File

@@ -0,0 +1,98 @@
import { strict as assert } from 'assert';
import testUtils, { GLOBAL } from '../test-utils';
import { transformArguments } from './SORT_RO';
describe('SORT_RO', () => {
testUtils.isVersionGreaterThanHook([7, 0]);
describe('transformArguments', () => {
it('simple', () => {
assert.deepEqual(
transformArguments('key'),
['SORT_RO', 'key']
);
});
it('with BY', () => {
assert.deepEqual(
transformArguments('key', {
BY: 'pattern'
}),
['SORT_RO', 'key', 'BY', 'pattern']
);
});
it('with LIMIT', () => {
assert.deepEqual(
transformArguments('key', {
LIMIT: {
offset: 0,
count: 1
}
}),
['SORT_RO', 'key', 'LIMIT', '0', '1']
);
});
describe('with GET', () => {
it('string', () => {
assert.deepEqual(
transformArguments('key', {
GET: 'pattern'
}),
['SORT_RO', 'key', 'GET', 'pattern']
);
});
it('array', () => {
assert.deepEqual(
transformArguments('key', {
GET: ['1', '2']
}),
['SORT_RO', 'key', 'GET', '1', 'GET', '2']
);
});
});
it('with DIRECTION', () => {
assert.deepEqual(
transformArguments('key', {
DIRECTION: 'ASC'
}),
['SORT_RO', 'key', 'ASC']
);
});
it('with ALPHA', () => {
assert.deepEqual(
transformArguments('key', {
ALPHA: true
}),
['SORT_RO', 'key', 'ALPHA']
);
});
it('with BY, LIMIT, GET, DIRECTION, ALPHA', () => {
assert.deepEqual(
transformArguments('key', {
BY: 'pattern',
LIMIT: {
offset: 0,
count: 1
},
GET: 'pattern',
DIRECTION: 'ASC',
ALPHA: true,
}),
['SORT_RO', 'key', 'BY', 'pattern', 'LIMIT', '0', '1', 'GET', 'pattern', 'ASC', 'ALPHA']
);
});
});
testUtils.testWithClient('client.sortRo', async client => {
assert.deepEqual(
await client.sortRo('key'),
[]
);
}, GLOBAL.SERVERS.OPEN);
});

View File

@@ -0,0 +1,15 @@
import { RedisCommandArguments } from '.';
import { pushSortArguments, SortOptions } from "./generic-transformers";
export const FIRST_KEY_INDEX = 1;
export const IS_READ_ONLY = true;
export function transformArguments(
key: string,
options?: SortOptions
): RedisCommandArguments {
return pushSortArguments(['SORT_RO', key], options);
}
export declare function transformReply(): Array<string>;

View File

@@ -0,0 +1,96 @@
import { strict as assert } from 'assert';
import testUtils, { GLOBAL } from '../test-utils';
import { transformArguments } from './SORT_STORE';
describe('SORT STORE', () => {
describe('transformArguments', () => {
it('simple', () => {
assert.deepEqual(
transformArguments('source', 'destination'),
['SORT', 'source', 'STORE', 'destination']
);
});
it('with BY', () => {
assert.deepEqual(
transformArguments('source', 'destination', {
BY: 'pattern'
}),
['SORT', 'source', 'BY', 'pattern', 'STORE', 'destination']
);
});
it('with LIMIT', () => {
assert.deepEqual(
transformArguments('source', 'destination', {
LIMIT: {
offset: 0,
count: 1
}
}),
['SORT', 'source', 'LIMIT', '0', '1', 'STORE', 'destination']
);
});
describe('with GET', () => {
it('string', () => {
assert.deepEqual(
transformArguments('source', 'destination', {
GET: 'pattern'
}),
['SORT', 'source', 'GET', 'pattern', 'STORE', 'destination']
);
});
it('array', () => {
assert.deepEqual(
transformArguments('source', 'destination', {
GET: ['1', '2']
}),
['SORT', 'source', 'GET', '1', 'GET', '2', 'STORE', 'destination']
);
});
});
it('with DIRECTION', () => {
assert.deepEqual(
transformArguments('source', 'destination', {
DIRECTION: 'ASC'
}),
['SORT', 'source', 'ASC', 'STORE', 'destination']
);
});
it('with ALPHA', () => {
assert.deepEqual(
transformArguments('source', 'destination', {
ALPHA: true
}),
['SORT', 'source', 'ALPHA', 'STORE', 'destination']
);
});
it('with BY, LIMIT, GET, DIRECTION, ALPHA', () => {
assert.deepEqual(
transformArguments('source', 'destination', {
BY: 'pattern',
LIMIT: {
offset: 0,
count: 1
},
GET: 'pattern',
DIRECTION: 'ASC',
ALPHA: true
}),
['SORT', 'source', 'BY', 'pattern', 'LIMIT', '0', '1', 'GET', 'pattern', 'ASC', 'ALPHA', 'STORE', 'destination']
);
});
});
testUtils.testWithClient('client.sortStore', async client => {
assert.equal(
await client.sortStore('source', 'destination'),
0
);
}, GLOBAL.SERVERS.OPEN);
});

View File

@@ -0,0 +1,17 @@
import { RedisCommandArguments } from '.';
import { SortOptions } from './generic-transformers';
import { transformArguments as transformSortArguments } from './SORT';
export const FIRST_KEY_INDEX = 1;
export function transformArguments(
source: string,
destination: string,
options?: SortOptions
): RedisCommandArguments {
const args = transformSortArguments(source, options);
args.push('STORE', destination);
return args;
}
export declare function transformReply(): number;

View File

@@ -0,0 +1,30 @@
import { strict as assert } from 'assert';
import testUtils, { GLOBAL } from '../test-utils';
import { transformArguments } from './ZINTERCARD';
describe('ZINTERCARD', () => {
testUtils.isVersionGreaterThanHook([7, 0]);
describe('transformArguments', () => {
it('simple', () => {
assert.deepEqual(
transformArguments(['1', '2']),
['ZINTERCARD', '2', '1', '2']
);
});
it('with limit', () => {
assert.deepEqual(
transformArguments(['1', '2'], 1),
['ZINTERCARD', '2', '1', '2', 'LIMIT', '1']
);
});
});
testUtils.testWithClient('client.zInterCard', async client => {
assert.deepEqual(
await client.zInterCard('key'),
0
);
}, GLOBAL.SERVERS.OPEN);
});

View File

@@ -0,0 +1,21 @@
import { RedisCommandArgument, RedisCommandArguments } from '.';
import { pushVerdictArgument } from './generic-transformers';
export const FIRST_KEY_INDEX = 2;
export const IS_READ_ONLY = true;
export function transformArguments(
keys: Array<RedisCommandArgument> | RedisCommandArgument,
limit?: number
): RedisCommandArguments {
const args = pushVerdictArgument(['ZINTERCARD'], keys);
if (limit) {
args.push('LIMIT', limit.toString());
}
return args;
}
export declare function transformReply(): number;

View File

@@ -19,11 +19,13 @@ import {
transformPXAT, transformPXAT,
pushEvalArguments, pushEvalArguments,
pushVerdictArguments, pushVerdictArguments,
pushVerdictNumberArguments,
pushVerdictArgument, pushVerdictArgument,
pushOptionalVerdictArgument, pushOptionalVerdictArgument,
transformCommandReply, transformCommandReply,
CommandFlags, CommandFlags,
CommandCategories CommandCategories,
pushSlotRangesArguments
} from './generic-transformers'; } from './generic-transformers';
describe('Generic Transformers', () => { describe('Generic Transformers', () => {
@@ -578,6 +580,22 @@ describe('Generic Transformers', () => {
}); });
}); });
describe('pushVerdictNumberArguments', () => {
it('number', () => {
assert.deepEqual(
pushVerdictNumberArguments([], 0),
['0']
);
});
it('array', () => {
assert.deepEqual(
pushVerdictNumberArguments([], [0, 1]),
['0', '1']
);
});
});
describe('pushVerdictArgument', () => { describe('pushVerdictArgument', () => {
it('string', () => { it('string', () => {
assert.deepEqual( assert.deepEqual(
@@ -639,4 +657,29 @@ describe('Generic Transformers', () => {
} }
); );
}); });
describe('pushSlotRangesArguments', () => {
it('single range', () => {
assert.deepEqual(
pushSlotRangesArguments([], {
start: 0,
end: 1
}),
['0', '1']
);
});
it('multiple ranges', () => {
assert.deepEqual(
pushSlotRangesArguments([], [{
start: 0,
end: 1
}, {
start: 2,
end: 3
}]),
['0', '1', '2', '3']
);
});
});
}); });

View File

@@ -322,6 +322,21 @@ export function pushVerdictArguments(args: RedisCommandArguments, value: RedisCo
return args; return args;
} }
export function pushVerdictNumberArguments(
args: RedisCommandArguments,
value: number | Array<number>
): RedisCommandArguments {
if (Array.isArray(value)) {
for (const item of value) {
args.push(item.toString());
}
} else {
args.push(value.toString());
}
return args;
}
export function pushVerdictArgument( export function pushVerdictArgument(
args: RedisCommandArguments, args: RedisCommandArguments,
value: RedisCommandArgument | Array<RedisCommandArgument> value: RedisCommandArgument | Array<RedisCommandArgument>
@@ -422,3 +437,77 @@ export function transformCommandReply(
categories: new Set(categories) categories: new Set(categories)
}; };
} }
export interface SortOptions {
BY?: string;
LIMIT?: {
offset: number;
count: number;
},
GET?: string | Array<string>;
DIRECTION?: 'ASC' | 'DESC';
ALPHA?: true;
}
export function pushSortArguments(
args: RedisCommandArguments,
options?: SortOptions
): RedisCommandArguments {
if (options?.BY) {
args.push('BY', options.BY);
}
if (options?.LIMIT) {
args.push(
'LIMIT',
options.LIMIT.offset.toString(),
options.LIMIT.count.toString()
);
}
if (options?.GET) {
for (const pattern of (typeof options.GET === 'string' ? [options.GET] : options.GET)) {
args.push('GET', pattern);
}
}
if (options?.DIRECTION) {
args.push(options.DIRECTION);
}
if (options?.ALPHA) {
args.push('ALPHA');
}
return args;
}
export interface SlotRange {
start: number;
end: number;
}
function pushSlotRangeArguments(
args: RedisCommandArguments,
range: SlotRange
): void {
args.push(
range.start.toString(),
range.end.toString()
);
}
export function pushSlotRangesArguments(
args: RedisCommandArguments,
ranges: SlotRange | Array<SlotRange>
): RedisCommandArguments {
if (Array.isArray(ranges)) {
for (const range of ranges) {
pushSlotRangeArguments(args, range);
}
} else {
pushSlotRangeArguments(args, ranges);
}
return args;
}

View File

@@ -1,6 +1,6 @@
{ {
"name": "@node-redis/client", "name": "@node-redis/client",
"version": "1.0.4", "version": "1.0.5",
"license": "MIT", "license": "MIT",
"main": "./dist/index.js", "main": "./dist/index.js",
"types": "./dist/index.d.ts", "types": "./dist/index.d.ts",
@@ -22,20 +22,20 @@
"devDependencies": { "devDependencies": {
"@istanbuljs/nyc-config-typescript": "^1.0.2", "@istanbuljs/nyc-config-typescript": "^1.0.2",
"@node-redis/test-utils": "*", "@node-redis/test-utils": "*",
"@types/node": "^17.0.13", "@types/node": "^17.0.23",
"@types/redis-parser": "^3.0.0", "@types/redis-parser": "^3.0.0",
"@types/sinon": "^10.0.9", "@types/sinon": "^10.0.11",
"@types/yallist": "^4.0.1", "@types/yallist": "^4.0.1",
"@typescript-eslint/eslint-plugin": "^5.10.1", "@typescript-eslint/eslint-plugin": "^5.16.0",
"@typescript-eslint/parser": "^5.10.1", "@typescript-eslint/parser": "^5.16.0",
"eslint": "^8.8.0", "eslint": "^8.12.0",
"nyc": "^15.1.0", "nyc": "^15.1.0",
"release-it": "^14.12.4", "release-it": "^14.13.1",
"sinon": "^13.0.0", "sinon": "^13.0.1",
"source-map-support": "^0.5.21", "source-map-support": "^0.5.21",
"ts-node": "^10.4.0", "ts-node": "^10.7.0",
"typedoc": "^0.22.11", "typedoc": "^0.22.13",
"typescript": "^4.5.5" "typescript": "^4.6.3"
}, },
"engines": { "engines": {
"node": ">=12" "node": ">=12"

View File

@@ -18,12 +18,12 @@
"devDependencies": { "devDependencies": {
"@istanbuljs/nyc-config-typescript": "^1.0.2", "@istanbuljs/nyc-config-typescript": "^1.0.2",
"@node-redis/test-utils": "*", "@node-redis/test-utils": "*",
"@types/node": "^17.0.13", "@types/node": "^17.0.23",
"nyc": "^15.1.0", "nyc": "^15.1.0",
"release-it": "^14.12.4", "release-it": "^14.13.1",
"source-map-support": "^0.5.21", "source-map-support": "^0.5.21",
"ts-node": "^10.4.0", "ts-node": "^10.7.0",
"typedoc": "^0.22.11", "typedoc": "^0.22.13",
"typescript": "^4.5.5" "typescript": "^4.6.3"
} }
} }

View File

@@ -18,12 +18,12 @@
"devDependencies": { "devDependencies": {
"@istanbuljs/nyc-config-typescript": "^1.0.2", "@istanbuljs/nyc-config-typescript": "^1.0.2",
"@node-redis/test-utils": "*", "@node-redis/test-utils": "*",
"@types/node": "^17.0.13", "@types/node": "^17.0.23",
"nyc": "^15.1.0", "nyc": "^15.1.0",
"release-it": "^14.12.4", "release-it": "^14.13.1",
"source-map-support": "^0.5.21", "source-map-support": "^0.5.21",
"ts-node": "^10.4.0", "ts-node": "^10.7.0",
"typedoc": "^0.22.11", "typedoc": "^0.22.13",
"typescript": "^4.5.5" "typescript": "^4.6.3"
} }
} }

View File

@@ -434,6 +434,26 @@ describe('AGGREGATE', () => {
); );
}); });
}); });
it('with PARAMS', () => {
assert.deepEqual(
transformArguments('index', '*', {
PARAMS: {
param: 'value'
}
}),
['FT.AGGREGATE', 'index', '*', 'PARAMS', '2', 'param', 'value']
);
});
it('with DIALECT', () => {
assert.deepEqual(
transformArguments('index', '*', {
DIALECT: 1
}),
['FT.AGGREGATE', 'index', '*', 'DIALECT', '1']
);
});
}); });
testUtils.testWithClient('client.ft.aggregate', async client => { testUtils.testWithClient('client.ft.aggregate', async client => {

View File

@@ -1,6 +1,6 @@
import { RedisCommandArgument, RedisCommandArguments } from '@node-redis/client/dist/lib/commands'; import { RedisCommandArgument, RedisCommandArguments } from '@node-redis/client/dist/lib/commands';
import { pushVerdictArgument, transformTuplesReply } from '@node-redis/client/dist/lib/commands/generic-transformers'; import { pushVerdictArgument, transformTuplesReply } from '@node-redis/client/dist/lib/commands/generic-transformers';
import { PropertyName, pushArgumentsWithLength, pushSortByArguments, SortByProperty } from '.'; import { Params, PropertyName, pushArgumentsWithLength, pushParamsArgs, pushSortByArguments, SortByProperty } from '.';
export enum AggregateSteps { export enum AggregateSteps {
GROUPBY = 'GROUPBY', GROUPBY = 'GROUPBY',
@@ -122,6 +122,8 @@ export interface AggregateOptions {
VERBATIM?: true; VERBATIM?: true;
LOAD?: LoadField | Array<LoadField>; LOAD?: LoadField | Array<LoadField>;
STEPS?: Array<GroupByStep | SortStep | ApplyStep | LimitStep | FilterStep>; STEPS?: Array<GroupByStep | SortStep | ApplyStep | LimitStep | FilterStep>;
PARAMS?: Params;
DIALECT?: number;
} }
export function transformArguments( export function transformArguments(
@@ -129,17 +131,16 @@ export function transformArguments(
query: string, query: string,
options?: AggregateOptions options?: AggregateOptions
): RedisCommandArguments { ): RedisCommandArguments {
return pushAggregatehOptions(
const args = ['FT.AGGREGATE', index, query]; ['FT.AGGREGATE', index, query],
pushAggregatehOptions(args, options); options
return args; );
} }
export function pushAggregatehOptions( export function pushAggregatehOptions(
args: RedisCommandArguments, args: RedisCommandArguments,
options?: AggregateOptions options?: AggregateOptions
): RedisCommandArguments { ): RedisCommandArguments {
if (options?.VERBATIM) { if (options?.VERBATIM) {
args.push('VERBATIM'); args.push('VERBATIM');
} }
@@ -202,6 +203,12 @@ export function pushAggregatehOptions(
} }
} }
pushParamsArgs(args, options?.PARAMS);
if (options?.DIALECT) {
args.push('DIALECT', options.DIALECT.toString());
}
return args; return args;
} }
@@ -257,7 +264,6 @@ function pushGroupByReducer(args: RedisCommandArguments, reducer: GroupByReducer
} }
} }
}); });
break; break;
} }

View File

@@ -1,7 +1,7 @@
import { strict as assert } from 'assert'; import { strict as assert } from 'assert';
import testUtils, { GLOBAL } from '../test-utils'; import testUtils, { GLOBAL } from '../test-utils';
import { transformArguments } from './CREATE'; import { transformArguments } from './CREATE';
import { SchemaFieldTypes, SchemaTextFieldPhonetics, RedisSearchLanguages } from '.'; import { SchemaFieldTypes, SchemaTextFieldPhonetics, RedisSearchLanguages, VectorAlgorithms } from '.';
describe('CREATE', () => { describe('CREATE', () => {
describe('transformArguments', () => { describe('transformArguments', () => {
@@ -126,6 +126,52 @@ describe('CREATE', () => {
}); });
}); });
describe('VECTOR', () => {
it('Flat algorithm', () => {
assert.deepEqual(
transformArguments('index', {
field: {
type: SchemaFieldTypes.VECTOR,
ALGORITHM: VectorAlgorithms.FLAT,
TYPE: 'FLOAT32',
DIM: 2,
DISTANCE_METRIC: 'L2',
INITIAL_CAP: 1000000,
BLOCK_SIZE: 1000
}
}),
[
'FT.CREATE', 'index', 'SCHEMA', 'field', 'VECTOR', 'FLAT', '10', 'TYPE',
'FLOAT32', 'DIM', '2', 'DISTANCE_METRIC', 'L2', 'INITIAL_CAP', '1000000',
'BLOCK_SIZE', '1000'
]
);
});
it('HNSW algorithm', () => {
assert.deepEqual(
transformArguments('index', {
field: {
type: SchemaFieldTypes.VECTOR,
ALGORITHM: VectorAlgorithms.HNSW,
TYPE: 'FLOAT32',
DIM: 2,
DISTANCE_METRIC: 'L2',
INITIAL_CAP: 1000000,
M: 40,
EF_CONSTRUCTION: 250,
EF_RUNTIME: 20
}
}),
[
'FT.CREATE', 'index', 'SCHEMA', 'field', 'VECTOR', 'HNSW', '14', 'TYPE',
'FLOAT32', 'DIM', '2', 'DISTANCE_METRIC', 'L2', 'INITIAL_CAP', '1000000',
'M', '40', 'EF_CONSTRUCTION', '250', 'EF_RUNTIME', '20'
]
);
});
});
describe('with generic options', () => { describe('with generic options', () => {
it('with AS', () => { it('with AS', () => {
assert.deepEqual( assert.deepEqual(

Some files were not shown because too many files have changed in this diff Show More