diff --git a/lib/commands/COMMAND.spec.ts b/lib/commands/COMMAND.spec.ts new file mode 100644 index 0000000000..e2c563862f --- /dev/null +++ b/lib/commands/COMMAND.spec.ts @@ -0,0 +1,28 @@ +import { strict as assert } from 'assert'; +import { itWithClient, TestRedisServers } from '../test-utils'; +import { transformArguments } from './COMMAND'; +import { CommandCategories, CommandFlags } from './generic-transformers'; + +describe('COMMAND', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments(), + ['COMMAND'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.command', async client => { + assert.deepEqual( + (await client.command()).find(command => command.name === 'ping'), + { + name: 'ping', + arity: -1, + flags: new Set([CommandFlags.STALE, CommandFlags.FAST]), + firstKeyIndex: 0, + lastKeyIndex: 0, + step: 0, + categories: new Set([CommandCategories.FAST, CommandCategories.CONNECTION]) + } + ); + }); +}); diff --git a/lib/commands/COMMAND.ts b/lib/commands/COMMAND.ts new file mode 100644 index 0000000000..f72cc3f37d --- /dev/null +++ b/lib/commands/COMMAND.ts @@ -0,0 +1,12 @@ +import { TransformArgumentsReply } from '.'; +import { CommandRawReply, CommandReply, transformCommandReply } from './generic-transformers'; + +export const IS_READ_ONLY = true; + +export function transformArguments(): TransformArgumentsReply { + return ['COMMAND']; +} + +export function transformReply(reply: Array): Array { + return reply.map(transformCommandReply); +} diff --git a/lib/commands/COMMAND_COUNT.spec.ts b/lib/commands/COMMAND_COUNT.spec.ts new file mode 100644 index 0000000000..61ba1bf254 --- /dev/null +++ b/lib/commands/COMMAND_COUNT.spec.ts @@ -0,0 +1,26 @@ +import { strict as assert } from 'assert'; +import { itWithClient, itWithCluster, TestRedisClusters, TestRedisServers } from '../test-utils'; +import { transformArguments } from './COMMAND_COUNT'; + +describe('COMMAND COUNT', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments(), + ['COMMAND', 'COUNT'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.commandCount', async client => { + assert.equal( + typeof await client.commandCount(), + 'number' + ); + }); + + itWithCluster(TestRedisClusters.OPEN, 'cluster.commandCount', async cluster => { + assert.equal( + typeof await cluster.commandCount(), + 'number' + ); + }); +}); diff --git a/lib/commands/COMMAND_COUNT.ts b/lib/commands/COMMAND_COUNT.ts new file mode 100644 index 0000000000..4cdec7bebf --- /dev/null +++ b/lib/commands/COMMAND_COUNT.ts @@ -0,0 +1,9 @@ +import { TransformArgumentsReply } from '.'; + +export const IS_READ_ONLY = true; + +export function transformArguments(): TransformArgumentsReply { + return ['COMMAND', 'COUNT']; +} + +declare function transformReply(): number; diff --git a/lib/commands/COMMAND_GETKEYS.spec.ts b/lib/commands/COMMAND_GETKEYS.spec.ts new file mode 100644 index 0000000000..37e9178158 --- /dev/null +++ b/lib/commands/COMMAND_GETKEYS.spec.ts @@ -0,0 +1,26 @@ +import { strict as assert } from 'assert'; +import { itWithClient, itWithCluster, TestRedisClusters, TestRedisServers } from '../test-utils'; +import { transformArguments } from './COMMAND_GETKEYS'; + +describe('COMMAND GETKEYS', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments(['GET', 'key']), + ['COMMAND', 'GETKEYS', 'GET', 'key'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.commandGetKeys', async client => { + assert.deepEqual( + await client.commandGetKeys(['GET', 'key']), + ['key'] + ); + }); + + itWithCluster(TestRedisClusters.OPEN, 'cluster.commandGetKeys', async cluster => { + assert.deepEqual( + await cluster.commandGetKeys(['GET', 'key']), + ['key'] + ); + }); +}); diff --git a/lib/commands/COMMAND_GETKEYS.ts b/lib/commands/COMMAND_GETKEYS.ts new file mode 100644 index 0000000000..0b8f38e3d0 --- /dev/null +++ b/lib/commands/COMMAND_GETKEYS.ts @@ -0,0 +1,9 @@ +import { TransformArgumentsReply } from '.'; + +export const IS_READ_ONLY = true; + +export function transformArguments(args: Array): TransformArgumentsReply { + return ['COMMAND', 'GETKEYS', ...args]; +} + +declare function transformReply(): Array; diff --git a/lib/commands/COMMAND_INFO.spec.ts b/lib/commands/COMMAND_INFO.spec.ts new file mode 100644 index 0000000000..c4deec0d22 --- /dev/null +++ b/lib/commands/COMMAND_INFO.spec.ts @@ -0,0 +1,28 @@ +import { strict as assert } from 'assert'; +import { itWithClient, TestRedisServers } from '../test-utils'; +import { transformArguments } from './COMMAND_INFO'; +import { CommandCategories, CommandFlags } from './generic-transformers'; + +describe('COMMAND INFO', () => { + it('transformArguments', () => { + assert.deepEqual( + transformArguments(['PING']), + ['COMMAND', 'INFO', 'PING'] + ); + }); + + itWithClient(TestRedisServers.OPEN, 'client.commandInfo', async client => { + assert.deepEqual( + await client.commandInfo(['PING']), + [{ + name: 'ping', + arity: -1, + flags: new Set([CommandFlags.STALE, CommandFlags.FAST]), + firstKeyIndex: 0, + lastKeyIndex: 0, + step: 0, + categories: new Set([CommandCategories.FAST, CommandCategories.CONNECTION]) + }] + ); + }); +}); diff --git a/lib/commands/COMMAND_INFO.ts b/lib/commands/COMMAND_INFO.ts new file mode 100644 index 0000000000..274c57d6ae --- /dev/null +++ b/lib/commands/COMMAND_INFO.ts @@ -0,0 +1,12 @@ +import { TransformArgumentsReply } from '.'; +import { CommandRawReply, CommandReply, transformCommandReply } from './generic-transformers'; + +export const IS_READ_ONLY = true; + +export function transformArguments(commands: Array): TransformArgumentsReply { + return ['COMMAND', 'INFO', ...commands]; +} + +export function transformReply(reply: Array): Array { + return reply.map(command => command ? transformCommandReply(command) : null); +} diff --git a/lib/commands/generic-transformers.spec.ts b/lib/commands/generic-transformers.spec.ts index 9bde6ebb3a..bdc3ee938c 100644 --- a/lib/commands/generic-transformers.spec.ts +++ b/lib/commands/generic-transformers.spec.ts @@ -21,7 +21,10 @@ import { pushStringTuplesArguments, pushVerdictArguments, pushVerdictArgument, - pushOptionalVerdictArgument + pushOptionalVerdictArgument, + transformCommandReply, + CommandFlags, + CommandCategories } from './generic-transformers'; describe('Generic Transformers', () => { @@ -626,4 +629,27 @@ describe('Generic Transformers', () => { ); }); }); + + it('transformCommandReply', () => { + assert.deepEqual( + transformCommandReply([ + 'ping', + -1, + [CommandFlags.STALE, CommandFlags.FAST], + 0, + 0, + 0, + [CommandCategories.FAST, CommandCategories.CONNECTION] + ]), + { + name: 'ping', + arity: -1, + flags: new Set([CommandFlags.STALE, CommandFlags.FAST]), + firstKeyIndex: 0, + lastKeyIndex: 0, + step: 0, + categories: new Set([CommandCategories.FAST, CommandCategories.CONNECTION]) + } + ); + }); }); diff --git a/lib/commands/generic-transformers.ts b/lib/commands/generic-transformers.ts index 84a6c97b81..98e6750f76 100644 --- a/lib/commands/generic-transformers.ts +++ b/lib/commands/generic-transformers.ts @@ -335,3 +335,79 @@ export function pushOptionalVerdictArgument(args: TransformArgumentsReply, name: return pushVerdictArgument(args, value); } + +export enum CommandFlags { + WRITE = 'write', // command may result in modifications + READONLY = 'readonly', // command will never modify keys + DENYOOM = 'denyoom', // reject command if currently out of memory + ADMIN = 'admin', // server admin command + PUBSUB = 'pubsub', // pubsub-related command + NOSCRIPT = 'noscript', // deny this command from scripts + RANDOM = 'random', // command has random results, dangerous for scripts + SORT_FOR_SCRIPT = 'sort_for_script', // if called from script, sort output + LOADING = 'loading', // allow command while database is loading + STALE = 'stale', // allow command while replica has stale data + SKIP_MONITOR = 'skip_monitor', // do not show this command in MONITOR + ASKING = 'asking', // cluster related - accept even if importing + FAST = 'fast', // command operates in constant or log(N) time. Used for latency monitoring. + MOVABLEKEYS = 'movablekeys' // keys have no pre-determined position. You must discover keys yourself. +} + +export enum CommandCategories { + KEYSPACE = '@keyspace', + READ = '@read', + WRITE = '@write', + SET = '@set', + SORTEDSET = '@sortedset', + LIST = '@list', + HASH = '@hash', + STRING = '@string', + BITMAP = '@bitmap', + HYPERLOGLOG = '@hyperloglog', + GEO = '@geo', + STREAM = '@stream', + PUBSUB = '@pubsub', + ADMIN = '@admin', + FAST = '@fast', + SLOW = '@slow', + BLOCKING = '@blocking', + DANGEROUS = '@dangerous', + CONNECTION = '@connection', + TRANSACTION = '@transaction', + SCRIPTING = '@scripting' +} + +export type CommandRawReply = [ + name: string, + arity: number, + flags: Array, + firstKeyIndex: number, + lastKeyIndex: number, + step: number, + categories: Array +]; + +export type CommandReply = { + name: string, + arity: number, + flags: Set, + firstKeyIndex: number, + lastKeyIndex: number, + step: number, + categories: Set +}; + +export function transformCommandReply( + this: void, + [name, arity, flags, firstKeyIndex, lastKeyIndex, step, categories]: CommandRawReply +): CommandReply { + return { + name, + arity, + flags: new Set(flags), + firstKeyIndex, + lastKeyIndex, + step, + categories: new Set(categories) + }; +} diff --git a/lib/commands/index.ts b/lib/commands/index.ts index 03a44ad794..014aff9e3a 100644 --- a/lib/commands/index.ts +++ b/lib/commands/index.ts @@ -35,6 +35,10 @@ import * as CLUSTER_MEET from './CLUSTER_MEET'; import * as CLUSTER_RESET from './CLUSTER_RESET'; import * as CLUSTER_SETSLOT from './CLUSTER_SETSLOT'; import * as CLUSTER_SLOTS from './CLUSTER_SLOTS'; +import * as COMMAND_COUNT from './COMMAND_COUNT'; +import * as COMMAND_GETKEYS from './COMMAND_GETKEYS'; +import * as COMMAND_INFO from './COMMAND_INFO'; +import * as COMMAND from './COMMAND'; import * as CONFIG_GET from './CONFIG_GET'; import * as CONFIG_RESETASTAT from './CONFIG_RESETSTAT'; import * as CONFIG_REWRITE from './CONFIG_REWRITE'; @@ -323,6 +327,14 @@ export default { clusterSetSlot: CLUSTER_SETSLOT, CLUSTER_SLOTS, clusterSlots: CLUSTER_SLOTS, + COMMAND_COUNT, + commandCount: COMMAND_COUNT, + COMMAND_GETKEYS, + commandGetKeys: COMMAND_GETKEYS, + COMMAND_INFO, + commandInfo: COMMAND_INFO, + COMMAND, + command: COMMAND, CONFIG_GET, configGet: CONFIG_GET, CONFIG_RESETASTAT,