You've already forked node-redis
mirror of
https://github.com/redis/node-redis.git
synced 2025-08-07 13:22:56 +03:00
new "transform arguments" API for better key and metadata extraction (#2733)
* Parser support with all commands * remove "dist" from all imports for consistency * address most of my review comments * small tweak to multi type mapping handling * tweak multi commands / fix addScript cases * nits * addressed all in person review comments * revert addCommand/addScript changes to multi-commands addCommand needs to be there for sendCommand like ability within a multi. If its there, it might as well be used by createCommand() et al, to avoid repeating code. addScript is there (even though only used once), but now made private to keep the logic for bookkeeping near each other.
This commit is contained in:
@@ -1,11 +1,12 @@
|
||||
import { strict as assert } from 'node:assert';
|
||||
import testUtils, { GLOBAL } from '../../test-utils';
|
||||
import ADD from './ADD';
|
||||
import { parseArgs } from '@redis/client/lib/commands/generic-transformers';
|
||||
|
||||
describe('CF.ADD', () => {
|
||||
it('transformArguments', () => {
|
||||
assert.deepEqual(
|
||||
ADD.transformArguments('key', 'item'),
|
||||
parseArgs(ADD, 'key', 'item'),
|
||||
['CF.ADD', 'key', 'item']
|
||||
);
|
||||
});
|
||||
|
@@ -1,11 +1,13 @@
|
||||
import { RedisArgument, Command } from '@redis/client/dist/lib/RESP/types';
|
||||
import { transformBooleanReply } from '@redis/client/dist/lib/commands/generic-transformers';
|
||||
import { CommandParser } from '@redis/client/lib/client/parser';
|
||||
import { RedisArgument, Command } from '@redis/client/lib/RESP/types';
|
||||
import { transformBooleanReply } from '@redis/client/lib/commands/generic-transformers';
|
||||
|
||||
export default {
|
||||
FIRST_KEY_INDEX: 1,
|
||||
IS_READ_ONLY: false,
|
||||
transformArguments(key: RedisArgument, item: RedisArgument) {
|
||||
return ['CF.ADD', key, item];
|
||||
parseCommand(parser: CommandParser, key: RedisArgument, item: RedisArgument) {
|
||||
parser.push('CF.ADD');
|
||||
parser.pushKey(key);
|
||||
parser.push(item);
|
||||
},
|
||||
transformReply: transformBooleanReply
|
||||
} as const satisfies Command;
|
||||
|
@@ -1,11 +1,12 @@
|
||||
import { strict as assert } from 'node:assert';
|
||||
import testUtils, { GLOBAL } from '../../test-utils';
|
||||
import ADDNX from './ADDNX';
|
||||
import { parseArgs } from '@redis/client/lib/commands/generic-transformers';
|
||||
|
||||
describe('CF.ADDNX', () => {
|
||||
it('transformArguments', () => {
|
||||
assert.deepEqual(
|
||||
ADDNX.transformArguments('key', 'item'),
|
||||
parseArgs(ADDNX, 'key', 'item'),
|
||||
['CF.ADDNX', 'key', 'item']
|
||||
);
|
||||
});
|
||||
|
@@ -1,11 +1,13 @@
|
||||
import { RedisArgument, Command } from '@redis/client/dist/lib/RESP/types';
|
||||
import { transformBooleanReply } from '@redis/client/dist/lib/commands/generic-transformers';
|
||||
import { CommandParser } from '@redis/client/lib/client/parser';
|
||||
import { RedisArgument, Command } from '@redis/client/lib/RESP/types';
|
||||
import { transformBooleanReply } from '@redis/client/lib/commands/generic-transformers';
|
||||
|
||||
export default {
|
||||
FIRST_KEY_INDEX: 1,
|
||||
IS_READ_ONLY: false,
|
||||
transformArguments(key: RedisArgument, item: RedisArgument) {
|
||||
return ['CF.ADDNX', key, item];
|
||||
parseCommand(parser: CommandParser, key: RedisArgument, item: RedisArgument) {
|
||||
parser.push('CF.ADDNX');
|
||||
parser.pushKey(key);
|
||||
parser.push(item);
|
||||
},
|
||||
transformReply: transformBooleanReply
|
||||
} as const satisfies Command;
|
||||
|
@@ -1,11 +1,12 @@
|
||||
import { strict as assert } from 'node:assert';
|
||||
import testUtils, { GLOBAL } from '../../test-utils';
|
||||
import COUNT from './COUNT';
|
||||
import { parseArgs } from '@redis/client/lib/commands/generic-transformers';
|
||||
|
||||
describe('CF.COUNT', () => {
|
||||
it('transformArguments', () => {
|
||||
assert.deepEqual(
|
||||
COUNT.transformArguments('key', 'item'),
|
||||
parseArgs(COUNT, 'key', 'item'),
|
||||
['CF.COUNT', 'key', 'item']
|
||||
);
|
||||
});
|
||||
|
@@ -1,10 +1,12 @@
|
||||
import { RedisArgument, NumberReply, Command } from '@redis/client/dist/lib/RESP/types';
|
||||
import { CommandParser } from '@redis/client/lib/client/parser';
|
||||
import { RedisArgument, NumberReply, Command } from '@redis/client/lib/RESP/types';
|
||||
|
||||
export default {
|
||||
FIRST_KEY_INDEX: 1,
|
||||
IS_READ_ONLY: true,
|
||||
transformArguments(key: RedisArgument, item: RedisArgument) {
|
||||
return ['CF.COUNT', key, item];
|
||||
parseCommand(parser: CommandParser, key: RedisArgument, item: RedisArgument) {
|
||||
parser.push('CF.COUNT');
|
||||
parser.pushKey(key);
|
||||
parser.push(item);
|
||||
},
|
||||
transformReply: undefined as unknown as () => NumberReply
|
||||
} as const satisfies Command;
|
||||
|
@@ -1,11 +1,12 @@
|
||||
import { strict as assert } from 'node:assert';
|
||||
import testUtils, { GLOBAL } from '../../test-utils';
|
||||
import DEL from './DEL';
|
||||
import { parseArgs } from '@redis/client/lib/commands/generic-transformers';
|
||||
|
||||
describe('CF.DEL', () => {
|
||||
it('transformArguments', () => {
|
||||
assert.deepEqual(
|
||||
DEL.transformArguments('key', 'item'),
|
||||
parseArgs(DEL, 'key', 'item'),
|
||||
['CF.DEL', 'key', 'item']
|
||||
);
|
||||
});
|
||||
|
@@ -1,11 +1,13 @@
|
||||
import { RedisArgument, Command } from '@redis/client/dist/lib/RESP/types';
|
||||
import { transformBooleanReply } from '@redis/client/dist/lib/commands/generic-transformers';
|
||||
import { CommandParser } from '@redis/client/lib/client/parser';
|
||||
import { RedisArgument, Command } from '@redis/client/lib/RESP/types';
|
||||
import { transformBooleanReply } from '@redis/client/lib/commands/generic-transformers';
|
||||
|
||||
export default {
|
||||
FIRST_KEY_INDEX: 1,
|
||||
IS_READ_ONLY: false,
|
||||
transformArguments(key: RedisArgument, item: RedisArgument) {
|
||||
return ['CF.DEL', key, item];
|
||||
parseCommand(parser: CommandParser, key: RedisArgument, item: RedisArgument) {
|
||||
parser.push('CF.DEL');
|
||||
parser.pushKey(key);
|
||||
parser.push(item);
|
||||
},
|
||||
transformReply: transformBooleanReply
|
||||
} as const satisfies Command;
|
||||
|
@@ -1,11 +1,12 @@
|
||||
import { strict as assert } from 'node:assert';
|
||||
import testUtils, { GLOBAL } from '../../test-utils';
|
||||
import EXISTS from './EXISTS';
|
||||
import { parseArgs } from '@redis/client/lib/commands/generic-transformers';
|
||||
|
||||
describe('CF.EXISTS', () => {
|
||||
it('transformArguments', () => {
|
||||
assert.deepEqual(
|
||||
EXISTS.transformArguments('key', 'item'),
|
||||
parseArgs(EXISTS, 'key', 'item'),
|
||||
['CF.EXISTS', 'key', 'item']
|
||||
);
|
||||
});
|
||||
|
@@ -1,11 +1,13 @@
|
||||
import { RedisArgument, Command } from '@redis/client/dist/lib/RESP/types';
|
||||
import { transformBooleanReply } from '@redis/client/dist/lib/commands/generic-transformers';
|
||||
import { CommandParser } from '@redis/client/lib/client/parser';
|
||||
import { RedisArgument, Command } from '@redis/client/lib/RESP/types';
|
||||
import { transformBooleanReply } from '@redis/client/lib/commands/generic-transformers';
|
||||
|
||||
export default {
|
||||
FIRST_KEY_INDEX: 1,
|
||||
IS_READ_ONLY: false,
|
||||
transformArguments(key: RedisArgument, item: RedisArgument) {
|
||||
return ['CF.EXISTS', key, item];
|
||||
parseCommand(parser: CommandParser, key: RedisArgument, item: RedisArgument) {
|
||||
parser.push('CF.EXISTS');
|
||||
parser.pushKey(key);
|
||||
parser.push(item);
|
||||
},
|
||||
transformReply: transformBooleanReply
|
||||
} as const satisfies Command;
|
||||
|
@@ -1,11 +1,12 @@
|
||||
import { strict as assert } from 'node:assert';
|
||||
import testUtils, { GLOBAL } from '../../test-utils';
|
||||
import INFO from './INFO';
|
||||
import { parseArgs } from '@redis/client/lib/commands/generic-transformers';
|
||||
|
||||
describe('CF.INFO', () => {
|
||||
it('transformArguments', () => {
|
||||
assert.deepEqual(
|
||||
INFO.transformArguments('cuckoo'),
|
||||
parseArgs(INFO, 'cuckoo'),
|
||||
['CF.INFO', 'cuckoo']
|
||||
);
|
||||
});
|
||||
|
@@ -1,4 +1,5 @@
|
||||
import { RedisArgument, Command, NumberReply, TuplesToMapReply, UnwrapReply, Resp2Reply, SimpleStringReply, TypeMapping } from '@redis/client/dist/lib/RESP/types';
|
||||
import { CommandParser } from '@redis/client/lib/client/parser';
|
||||
import { RedisArgument, Command, NumberReply, TuplesToMapReply, UnwrapReply, Resp2Reply, SimpleStringReply, TypeMapping } from '@redis/client/lib/RESP/types';
|
||||
import { transformInfoV2Reply } from '../bloom';
|
||||
|
||||
export type CfInfoReplyMap = TuplesToMapReply<[
|
||||
@@ -13,10 +14,10 @@ export type CfInfoReplyMap = TuplesToMapReply<[
|
||||
]>;
|
||||
|
||||
export default {
|
||||
FIRST_KEY_INDEX: 1,
|
||||
IS_READ_ONLY: true,
|
||||
transformArguments(key: RedisArgument) {
|
||||
return ['CF.INFO', key];
|
||||
parseCommand(parser: CommandParser, key: RedisArgument) {
|
||||
parser.push('CF.INFO');
|
||||
parser.pushKey(key);
|
||||
},
|
||||
transformReply: {
|
||||
2: (reply: UnwrapReply<Resp2Reply<CfInfoReplyMap>>, _, typeMapping?: TypeMapping): CfInfoReplyMap => {
|
||||
|
@@ -1,11 +1,12 @@
|
||||
import { strict as assert } from 'node:assert';
|
||||
import testUtils, { GLOBAL } from '../../test-utils';
|
||||
import INSERT from './INSERT';
|
||||
import { parseArgs } from '@redis/client/lib/commands/generic-transformers';
|
||||
|
||||
describe('CF.INSERT', () => {
|
||||
it('transformArguments', () => {
|
||||
assert.deepEqual(
|
||||
INSERT.transformArguments('key', 'item', {
|
||||
parseArgs(INSERT, 'key', 'item', {
|
||||
CAPACITY: 100,
|
||||
NOCREATE: true
|
||||
}),
|
||||
|
@@ -1,34 +1,37 @@
|
||||
import { Command, RedisArgument } from '@redis/client/dist/lib/RESP/types';
|
||||
import { RedisVariadicArgument, pushVariadicArguments, transformBooleanArrayReply } from '@redis/client/dist/lib/commands/generic-transformers';
|
||||
import { CommandParser } from '@redis/client/lib/client/parser';
|
||||
import { Command, RedisArgument } from '@redis/client/lib/RESP/types';
|
||||
import { RedisVariadicArgument, transformBooleanArrayReply } from '@redis/client/lib/commands/generic-transformers';
|
||||
|
||||
export interface CfInsertOptions {
|
||||
CAPACITY?: number;
|
||||
NOCREATE?: boolean;
|
||||
}
|
||||
|
||||
export function transofrmCfInsertArguments(
|
||||
command: RedisArgument,
|
||||
export function parseCfInsertArguments(
|
||||
parser: CommandParser,
|
||||
key: RedisArgument,
|
||||
items: RedisVariadicArgument,
|
||||
options?: CfInsertOptions
|
||||
) {
|
||||
const args = [command, key];
|
||||
parser.pushKey(key);
|
||||
|
||||
if (options?.CAPACITY !== undefined) {
|
||||
args.push('CAPACITY', options.CAPACITY.toString());
|
||||
parser.push('CAPACITY', options.CAPACITY.toString());
|
||||
}
|
||||
|
||||
if (options?.NOCREATE) {
|
||||
args.push('NOCREATE');
|
||||
parser.push('NOCREATE');
|
||||
}
|
||||
|
||||
args.push('ITEMS');
|
||||
return pushVariadicArguments(args, items);
|
||||
parser.push('ITEMS');
|
||||
parser.pushVariadic(items);
|
||||
}
|
||||
|
||||
export default {
|
||||
FIRST_KEY_INDEX: 1,
|
||||
IS_READ_ONLY: false,
|
||||
transformArguments: transofrmCfInsertArguments.bind(undefined, 'CF.INSERT'),
|
||||
parseCommand(...args: Parameters<typeof parseCfInsertArguments>) {
|
||||
args[0].push('CF.INSERT');
|
||||
parseCfInsertArguments(...args);
|
||||
},
|
||||
transformReply: transformBooleanArrayReply
|
||||
} as const satisfies Command;
|
||||
|
@@ -1,11 +1,12 @@
|
||||
import { strict as assert } from 'node:assert';
|
||||
import testUtils, { GLOBAL } from '../../test-utils';
|
||||
import INSERTNX from './INSERTNX';
|
||||
import { parseArgs } from '@redis/client/lib/commands/generic-transformers';
|
||||
|
||||
describe('CF.INSERTNX', () => {
|
||||
it('transformArguments', () => {
|
||||
assert.deepEqual(
|
||||
INSERTNX.transformArguments('key', 'item', {
|
||||
parseArgs(INSERTNX, 'key', 'item', {
|
||||
CAPACITY: 100,
|
||||
NOCREATE: true
|
||||
}),
|
||||
|
@@ -1,9 +1,11 @@
|
||||
import { Command } from '@redis/client/dist/lib/RESP/types';
|
||||
import INSERT, { transofrmCfInsertArguments } from './INSERT';
|
||||
import { Command } from '@redis/client/lib/RESP/types';
|
||||
import INSERT, { parseCfInsertArguments } from './INSERT';
|
||||
|
||||
export default {
|
||||
FIRST_KEY_INDEX: INSERT.FIRST_KEY_INDEX,
|
||||
IS_READ_ONLY: INSERT.IS_READ_ONLY,
|
||||
transformArguments: transofrmCfInsertArguments.bind(undefined, 'CF.INSERTNX'),
|
||||
parseCommand(...args: Parameters<typeof parseCfInsertArguments>) {
|
||||
args[0].push('CF.INSERTNX');
|
||||
parseCfInsertArguments(...args);
|
||||
},
|
||||
transformReply: INSERT.transformReply
|
||||
} as const satisfies Command;
|
||||
|
@@ -2,11 +2,12 @@ import { strict as assert } from 'node:assert';
|
||||
import testUtils, { GLOBAL } from '../../test-utils';
|
||||
import LOADCHUNK from './LOADCHUNK';
|
||||
import { RESP_TYPES } from '@redis/client';
|
||||
import { parseArgs } from '@redis/client/lib/commands/generic-transformers';
|
||||
|
||||
describe('CF.LOADCHUNK', () => {
|
||||
it('transformArguments', () => {
|
||||
assert.deepEqual(
|
||||
LOADCHUNK.transformArguments('item', 0, ''),
|
||||
parseArgs(LOADCHUNK, 'item', 0, ''),
|
||||
['CF.LOADCHUNK', 'item', '0', '']
|
||||
);
|
||||
});
|
||||
|
@@ -1,10 +1,12 @@
|
||||
import { SimpleStringReply, Command, RedisArgument } from '@redis/client/dist/lib/RESP/types';
|
||||
import { CommandParser } from '@redis/client/lib/client/parser';
|
||||
import { SimpleStringReply, Command, RedisArgument } from '@redis/client/lib/RESP/types';
|
||||
|
||||
export default {
|
||||
FIRST_KEY_INDEX: 1,
|
||||
IS_READ_ONLY: false,
|
||||
transformArguments(key: RedisArgument, iterator: number, chunk: RedisArgument) {
|
||||
return ['CF.LOADCHUNK', key, iterator.toString(), chunk];
|
||||
parseCommand(parser: CommandParser, key: RedisArgument, iterator: number, chunk: RedisArgument) {
|
||||
parser.push('CF.LOADCHUNK');
|
||||
parser.pushKey(key);
|
||||
parser.push(iterator.toString(), chunk);
|
||||
},
|
||||
transformReply: undefined as unknown as () => SimpleStringReply<'OK'>
|
||||
} as const satisfies Command;
|
||||
|
@@ -1,19 +1,20 @@
|
||||
import { strict as assert } from 'node:assert';
|
||||
import testUtils, { GLOBAL } from '../../test-utils';
|
||||
import RESERVE from './RESERVE';
|
||||
import { parseArgs } from '@redis/client/lib/commands/generic-transformers';
|
||||
|
||||
describe('CF.RESERVE', () => {
|
||||
describe('transformArguments', () => {
|
||||
it('simple', () => {
|
||||
assert.deepEqual(
|
||||
RESERVE.transformArguments('key', 4),
|
||||
parseArgs(RESERVE, 'key', 4),
|
||||
['CF.RESERVE', 'key', '4']
|
||||
);
|
||||
});
|
||||
|
||||
it('with EXPANSION', () => {
|
||||
assert.deepEqual(
|
||||
RESERVE.transformArguments('key', 4, {
|
||||
parseArgs(RESERVE, 'key', 4, {
|
||||
EXPANSION: 1
|
||||
}),
|
||||
['CF.RESERVE', 'key', '4', 'EXPANSION', '1']
|
||||
@@ -22,7 +23,7 @@ describe('CF.RESERVE', () => {
|
||||
|
||||
it('with BUCKETSIZE', () => {
|
||||
assert.deepEqual(
|
||||
RESERVE.transformArguments('key', 4, {
|
||||
parseArgs(RESERVE, 'key', 4, {
|
||||
BUCKETSIZE: 2
|
||||
}),
|
||||
['CF.RESERVE', 'key', '4', 'BUCKETSIZE', '2']
|
||||
@@ -31,7 +32,7 @@ describe('CF.RESERVE', () => {
|
||||
|
||||
it('with MAXITERATIONS', () => {
|
||||
assert.deepEqual(
|
||||
RESERVE.transformArguments('key', 4, {
|
||||
parseArgs(RESERVE, 'key', 4, {
|
||||
MAXITERATIONS: 1
|
||||
}),
|
||||
['CF.RESERVE', 'key', '4', 'MAXITERATIONS', '1']
|
||||
|
@@ -1,4 +1,5 @@
|
||||
import { RedisArgument, SimpleStringReply, Command } from '@redis/client/dist/lib/RESP/types';
|
||||
import { CommandParser } from '@redis/client/lib/client/parser';
|
||||
import { RedisArgument, SimpleStringReply, Command } from '@redis/client/lib/RESP/types';
|
||||
|
||||
export interface CfReserveOptions {
|
||||
BUCKETSIZE?: number;
|
||||
@@ -7,28 +8,28 @@ export interface CfReserveOptions {
|
||||
}
|
||||
|
||||
export default {
|
||||
FIRST_KEY_INDEX: 1,
|
||||
IS_READ_ONLY: false,
|
||||
transformArguments(
|
||||
parseCommand(
|
||||
parser: CommandParser,
|
||||
key: RedisArgument,
|
||||
capacity: number,
|
||||
options?: CfReserveOptions
|
||||
) {
|
||||
const args = ['CF.RESERVE', key, capacity.toString()];
|
||||
parser.push('CF.RESERVE');
|
||||
parser.pushKey(key);
|
||||
parser.push(capacity.toString());
|
||||
|
||||
if (options?.BUCKETSIZE !== undefined) {
|
||||
args.push('BUCKETSIZE', options.BUCKETSIZE.toString());
|
||||
parser.push('BUCKETSIZE', options.BUCKETSIZE.toString());
|
||||
}
|
||||
|
||||
if (options?.MAXITERATIONS !== undefined) {
|
||||
args.push('MAXITERATIONS', options.MAXITERATIONS.toString());
|
||||
parser.push('MAXITERATIONS', options.MAXITERATIONS.toString());
|
||||
}
|
||||
|
||||
if (options?.EXPANSION !== undefined) {
|
||||
args.push('EXPANSION', options.EXPANSION.toString());
|
||||
parser.push('EXPANSION', options.EXPANSION.toString());
|
||||
}
|
||||
|
||||
return args;
|
||||
},
|
||||
transformReply: undefined as unknown as () => SimpleStringReply<'OK'>
|
||||
} as const satisfies Command;
|
||||
|
@@ -1,11 +1,12 @@
|
||||
import { strict as assert } from 'node:assert';
|
||||
import testUtils, { GLOBAL } from '../../test-utils';
|
||||
import SCANDUMP from './SCANDUMP';
|
||||
import { parseArgs } from '@redis/client/lib/commands/generic-transformers';
|
||||
|
||||
describe('CF.SCANDUMP', () => {
|
||||
it('transformArguments', () => {
|
||||
assert.deepEqual(
|
||||
SCANDUMP.transformArguments('key', 0),
|
||||
parseArgs(SCANDUMP, 'key', 0),
|
||||
['CF.SCANDUMP', 'key', '0']
|
||||
);
|
||||
});
|
||||
|
@@ -1,10 +1,12 @@
|
||||
import { RedisArgument, TuplesReply, NumberReply, BlobStringReply, NullReply, UnwrapReply, Command } from '@redis/client/dist/lib/RESP/types';
|
||||
import { CommandParser } from '@redis/client/lib/client/parser';
|
||||
import { RedisArgument, TuplesReply, NumberReply, BlobStringReply, NullReply, UnwrapReply, Command } from '@redis/client/lib/RESP/types';
|
||||
|
||||
export default {
|
||||
FIRST_KEY_INDEX: 1,
|
||||
IS_READ_ONLY: true,
|
||||
transformArguments(key: RedisArgument, iterator: number) {
|
||||
return ['CF.SCANDUMP', key, iterator.toString()];
|
||||
parseCommand(parser: CommandParser, key: RedisArgument, iterator: number) {
|
||||
parser.push('CF.SCANDUMP');
|
||||
parser.pushKey(key);
|
||||
parser.push(iterator.toString());
|
||||
},
|
||||
transformReply(reply: UnwrapReply<TuplesReply<[NumberReply, BlobStringReply | NullReply]>>) {
|
||||
return {
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import type { RedisCommands } from '@redis/client/dist/lib/RESP/types';
|
||||
import type { RedisCommands } from '@redis/client/lib/RESP/types';
|
||||
import ADD from './ADD';
|
||||
import ADDNX from './ADDNX';
|
||||
import COUNT from './COUNT';
|
||||
|
Reference in New Issue
Block a user