You've already forked node-redis
mirror of
https://github.com/redis/node-redis.git
synced 2025-08-04 15:02:09 +03:00
Add support for redis functions (#2020)
* fix #1906 - implement BITFIELD_RO * initial support for redis functions * fix test utils * redis functions commands and tests * upgrade deps * fix "Property 'uninstall' does not exist on type 'SinonFakeTimers'" * upgrade dockers version * Merge branch 'master' of github.com:redis/node-redis into functions * fix FUNCTION LIST WITHCODE and FUNCTION STATS * upgrade deps * set minimum version for FCALL and FCALL_RO * fix FUNCTION LOAD * FUNCTION LOAD * fix FUNCTION LOAD & FUNCTION LIST & FUNCTION LOAD WITHCODE * fix FUNCTION_LIST_WITHCODE test
This commit is contained in:
@ -1,4 +1,6 @@
|
||||
import { EvalOptions, pushEvalArguments } from './generic-transformers';
|
||||
import { evalFirstKeyIndex, EvalOptions, pushEvalArguments } from './generic-transformers';
|
||||
|
||||
export const FIRST_KEY_INDEX = evalFirstKeyIndex;
|
||||
|
||||
export function transformArguments(script: string, options?: EvalOptions): Array<string> {
|
||||
return pushEvalArguments(['EVAL', script], options);
|
||||
|
@ -1,4 +1,6 @@
|
||||
import { EvalOptions, pushEvalArguments } from './generic-transformers';
|
||||
import { evalFirstKeyIndex, EvalOptions, pushEvalArguments } from './generic-transformers';
|
||||
|
||||
export const FIRST_KEY_INDEX = evalFirstKeyIndex;
|
||||
|
||||
export function transformArguments(sha1: string, options?: EvalOptions): Array<string> {
|
||||
return pushEvalArguments(['EVALSHA', sha1], options);
|
||||
|
17
packages/client/lib/commands/EVALSHA_RO.spec.ts
Normal file
17
packages/client/lib/commands/EVALSHA_RO.spec.ts
Normal file
@ -0,0 +1,17 @@
|
||||
import { strict as assert } from 'assert';
|
||||
import testUtils from '../test-utils';
|
||||
import { transformArguments } from './EVALSHA_RO';
|
||||
|
||||
describe('EVALSHA_RO', () => {
|
||||
testUtils.isVersionGreaterThanHook([7, 0]);
|
||||
|
||||
it('transformArguments', () => {
|
||||
assert.deepEqual(
|
||||
transformArguments('sha1', {
|
||||
keys: ['key'],
|
||||
arguments: ['argument']
|
||||
}),
|
||||
['EVALSHA_RO', 'sha1', '1', 'key', 'argument']
|
||||
);
|
||||
});
|
||||
});
|
9
packages/client/lib/commands/EVALSHA_RO.ts
Normal file
9
packages/client/lib/commands/EVALSHA_RO.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import { evalFirstKeyIndex, EvalOptions, pushEvalArguments } from './generic-transformers';
|
||||
|
||||
export const FIRST_KEY_INDEX = evalFirstKeyIndex;
|
||||
|
||||
export const IS_READ_ONLY = true;
|
||||
|
||||
export function transformArguments(sha1: string, options?: EvalOptions): Array<string> {
|
||||
return pushEvalArguments(['EVALSHA_RO', sha1], options);
|
||||
}
|
31
packages/client/lib/commands/EVAL_RO.spec.ts
Normal file
31
packages/client/lib/commands/EVAL_RO.spec.ts
Normal file
@ -0,0 +1,31 @@
|
||||
import { strict as assert } from 'assert';
|
||||
import testUtils, { GLOBAL } from '../test-utils';
|
||||
import { transformArguments } from './EVAL_RO';
|
||||
|
||||
describe('EVAL_RO', () => {
|
||||
testUtils.isVersionGreaterThanHook([7, 0]);
|
||||
|
||||
it('transformArguments', () => {
|
||||
assert.deepEqual(
|
||||
transformArguments('return KEYS[1] + ARGV[1]', {
|
||||
keys: ['key'],
|
||||
arguments: ['argument']
|
||||
}),
|
||||
['EVAL_RO', 'return KEYS[1] + ARGV[1]', '1', 'key', 'argument']
|
||||
);
|
||||
});
|
||||
|
||||
testUtils.testWithClient('client.evalRo', async client => {
|
||||
assert.equal(
|
||||
await client.evalRo('return 1'),
|
||||
1
|
||||
);
|
||||
}, GLOBAL.SERVERS.OPEN);
|
||||
|
||||
testUtils.testWithCluster('cluster.evalRo', async cluster => {
|
||||
assert.equal(
|
||||
await cluster.evalRo('return 1'),
|
||||
1
|
||||
);
|
||||
}, GLOBAL.CLUSTERS.OPEN);
|
||||
});
|
9
packages/client/lib/commands/EVAL_RO.ts
Normal file
9
packages/client/lib/commands/EVAL_RO.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import { evalFirstKeyIndex, EvalOptions, pushEvalArguments } from './generic-transformers';
|
||||
|
||||
export const FIRST_KEY_INDEX = evalFirstKeyIndex;
|
||||
|
||||
export const IS_READ_ONLY = true;
|
||||
|
||||
export function transformArguments(script: string, options?: EvalOptions): Array<string> {
|
||||
return pushEvalArguments(['EVAL_RO', script], options);
|
||||
}
|
29
packages/client/lib/commands/FCALL.spec.ts
Normal file
29
packages/client/lib/commands/FCALL.spec.ts
Normal file
@ -0,0 +1,29 @@
|
||||
import { strict as assert } from 'assert';
|
||||
import testUtils, { GLOBAL } from '../test-utils';
|
||||
import { MATH_FUNCTION, loadMathFunction } from '../client/index.spec';
|
||||
import { transformArguments } from './FCALL';
|
||||
|
||||
describe('FCALL', () => {
|
||||
testUtils.isVersionGreaterThanHook([7, 0]);
|
||||
|
||||
it('transformArguments', () => {
|
||||
assert.deepEqual(
|
||||
transformArguments('function', {
|
||||
keys: ['key'],
|
||||
arguments: ['argument']
|
||||
}),
|
||||
['FCALL', 'function', '1', 'key', 'argument']
|
||||
);
|
||||
});
|
||||
|
||||
testUtils.testWithClient('client.fCall', async client => {
|
||||
await loadMathFunction(client);
|
||||
|
||||
assert.equal(
|
||||
await client.fCall(MATH_FUNCTION.library.square.NAME, {
|
||||
arguments: ['2']
|
||||
}),
|
||||
4
|
||||
);
|
||||
}, GLOBAL.SERVERS.OPEN);
|
||||
});
|
7
packages/client/lib/commands/FCALL.ts
Normal file
7
packages/client/lib/commands/FCALL.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import { evalFirstKeyIndex, EvalOptions, pushEvalArguments } from './generic-transformers';
|
||||
|
||||
export const FIRST_KEY_INDEX = evalFirstKeyIndex;
|
||||
|
||||
export function transformArguments(fn: string, options?: EvalOptions): Array<string> {
|
||||
return pushEvalArguments(['FCALL', fn], options);
|
||||
}
|
29
packages/client/lib/commands/FCALL_RO.spec.ts
Normal file
29
packages/client/lib/commands/FCALL_RO.spec.ts
Normal file
@ -0,0 +1,29 @@
|
||||
import { strict as assert } from 'assert';
|
||||
import testUtils, { GLOBAL } from '../test-utils';
|
||||
import { MATH_FUNCTION, loadMathFunction } from '../client/index.spec';
|
||||
import { transformArguments } from './FCALL_RO';
|
||||
|
||||
describe('FCALL_RO', () => {
|
||||
testUtils.isVersionGreaterThanHook([7, 0]);
|
||||
|
||||
it('transformArguments', () => {
|
||||
assert.deepEqual(
|
||||
transformArguments('function', {
|
||||
keys: ['key'],
|
||||
arguments: ['argument']
|
||||
}),
|
||||
['FCALL_RO', 'function', '1', 'key', 'argument']
|
||||
);
|
||||
});
|
||||
|
||||
testUtils.testWithClient('client.fCallRo', async client => {
|
||||
await loadMathFunction(client);
|
||||
|
||||
assert.equal(
|
||||
await client.fCallRo(MATH_FUNCTION.library.square.NAME, {
|
||||
arguments: ['2']
|
||||
}),
|
||||
4
|
||||
);
|
||||
}, GLOBAL.SERVERS.OPEN);
|
||||
});
|
9
packages/client/lib/commands/FCALL_RO.ts
Normal file
9
packages/client/lib/commands/FCALL_RO.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import { evalFirstKeyIndex, EvalOptions, pushEvalArguments } from './generic-transformers';
|
||||
|
||||
export const FIRST_KEY_INDEX = evalFirstKeyIndex;
|
||||
|
||||
export const IS_READ_ONLY = true;
|
||||
|
||||
export function transformArguments(fn: string, options?: EvalOptions): Array<string> {
|
||||
return pushEvalArguments(['FCALL_RO', fn], options);
|
||||
}
|
24
packages/client/lib/commands/FUNCTION_DELETE.spec.ts
Normal file
24
packages/client/lib/commands/FUNCTION_DELETE.spec.ts
Normal file
@ -0,0 +1,24 @@
|
||||
import { strict as assert } from 'assert';
|
||||
import { MATH_FUNCTION, loadMathFunction } from '../client/index.spec';
|
||||
import testUtils, { GLOBAL } from '../test-utils';
|
||||
import { transformArguments } from './FUNCTION_DELETE';
|
||||
|
||||
describe('FUNCTION DELETE', () => {
|
||||
testUtils.isVersionGreaterThanHook([7, 0]);
|
||||
|
||||
it('transformArguments', () => {
|
||||
assert.deepEqual(
|
||||
transformArguments('library'),
|
||||
['FUNCTION', 'DELETE', 'library']
|
||||
);
|
||||
});
|
||||
|
||||
testUtils.testWithClient('client.functionDelete', async client => {
|
||||
await loadMathFunction(client);
|
||||
|
||||
assert.equal(
|
||||
await client.functionDelete(MATH_FUNCTION.name),
|
||||
'OK'
|
||||
);
|
||||
}, GLOBAL.SERVERS.OPEN);
|
||||
});
|
7
packages/client/lib/commands/FUNCTION_DELETE.ts
Normal file
7
packages/client/lib/commands/FUNCTION_DELETE.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import { RedisCommandArguments } from '.';
|
||||
|
||||
export function transformArguments(library: string): RedisCommandArguments {
|
||||
return ['FUNCTION', 'DELETE', library];
|
||||
}
|
||||
|
||||
export declare function transformReply(): 'OK';
|
21
packages/client/lib/commands/FUNCTION_DUMP.spec.ts
Normal file
21
packages/client/lib/commands/FUNCTION_DUMP.spec.ts
Normal file
@ -0,0 +1,21 @@
|
||||
import { strict as assert } from 'assert';
|
||||
import testUtils, { GLOBAL } from '../test-utils';
|
||||
import { transformArguments } from './FUNCTION_DUMP';
|
||||
|
||||
describe('FUNCTION DUMP', () => {
|
||||
testUtils.isVersionGreaterThanHook([7, 0]);
|
||||
|
||||
it('transformArguments', () => {
|
||||
assert.deepEqual(
|
||||
transformArguments(),
|
||||
['FUNCTION', 'DUMP']
|
||||
);
|
||||
});
|
||||
|
||||
testUtils.testWithClient('client.functionDump', async client => {
|
||||
assert.equal(
|
||||
typeof await client.functionDump(),
|
||||
'string'
|
||||
);
|
||||
}, GLOBAL.SERVERS.OPEN);
|
||||
});
|
7
packages/client/lib/commands/FUNCTION_DUMP.ts
Normal file
7
packages/client/lib/commands/FUNCTION_DUMP.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import { RedisCommandArgument, RedisCommandArguments } from '.';
|
||||
|
||||
export function transformArguments(): RedisCommandArguments {
|
||||
return ['FUNCTION', 'DUMP'];
|
||||
}
|
||||
|
||||
export declare function transformReply(): RedisCommandArgument;
|
30
packages/client/lib/commands/FUNCTION_FLUSH.spec.ts
Normal file
30
packages/client/lib/commands/FUNCTION_FLUSH.spec.ts
Normal file
@ -0,0 +1,30 @@
|
||||
import { strict as assert } from 'assert';
|
||||
import testUtils, { GLOBAL } from '../test-utils';
|
||||
import { transformArguments } from './FUNCTION_FLUSH';
|
||||
|
||||
describe('FUNCTION FLUSH', () => {
|
||||
testUtils.isVersionGreaterThanHook([7, 0]);
|
||||
|
||||
describe('transformArguments', () => {
|
||||
it('simple', () => {
|
||||
assert.deepEqual(
|
||||
transformArguments(),
|
||||
['FUNCTION', 'FLUSH']
|
||||
);
|
||||
});
|
||||
|
||||
it('with mode', () => {
|
||||
assert.deepEqual(
|
||||
transformArguments('SYNC'),
|
||||
['FUNCTION', 'FLUSH', 'SYNC']
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
testUtils.testWithClient('client.functionFlush', async client => {
|
||||
assert.equal(
|
||||
await client.functionFlush(),
|
||||
'OK'
|
||||
);
|
||||
}, GLOBAL.SERVERS.OPEN);
|
||||
});
|
13
packages/client/lib/commands/FUNCTION_FLUSH.ts
Normal file
13
packages/client/lib/commands/FUNCTION_FLUSH.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import { RedisCommandArguments } from '.';
|
||||
|
||||
export function transformArguments(mode?: 'ASYNC' | 'SYNC'): RedisCommandArguments {
|
||||
const args = ['FUNCTION', 'FLUSH'];
|
||||
|
||||
if (mode) {
|
||||
args.push(mode);
|
||||
}
|
||||
|
||||
return args;
|
||||
}
|
||||
|
||||
export declare function transformReply(): 'OK';
|
14
packages/client/lib/commands/FUNCTION_KILL.spec.ts
Normal file
14
packages/client/lib/commands/FUNCTION_KILL.spec.ts
Normal file
@ -0,0 +1,14 @@
|
||||
import { strict as assert } from 'assert';
|
||||
import testUtils from '../test-utils';
|
||||
import { transformArguments } from './FUNCTION_KILL';
|
||||
|
||||
describe('FUNCTION KILL', () => {
|
||||
testUtils.isVersionGreaterThanHook([7, 0]);
|
||||
|
||||
it('transformArguments', () => {
|
||||
assert.deepEqual(
|
||||
transformArguments(),
|
||||
['FUNCTION', 'KILL']
|
||||
);
|
||||
});
|
||||
});
|
7
packages/client/lib/commands/FUNCTION_KILL.ts
Normal file
7
packages/client/lib/commands/FUNCTION_KILL.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import { RedisCommandArguments } from '.';
|
||||
|
||||
export function transformArguments(): RedisCommandArguments {
|
||||
return ['FUNCTION', 'KILL'];
|
||||
}
|
||||
|
||||
export declare function transformReply(): 'OK';
|
41
packages/client/lib/commands/FUNCTION_LIST.spec.ts
Normal file
41
packages/client/lib/commands/FUNCTION_LIST.spec.ts
Normal file
@ -0,0 +1,41 @@
|
||||
import { strict as assert } from 'assert';
|
||||
import testUtils, { GLOBAL } from '../test-utils';
|
||||
import { MATH_FUNCTION, loadMathFunction } from '../client/index.spec';
|
||||
import { transformArguments } from './FUNCTION_LIST';
|
||||
|
||||
describe('FUNCTION LIST', () => {
|
||||
testUtils.isVersionGreaterThanHook([7, 0]);
|
||||
|
||||
describe('transformArguments', () => {
|
||||
it('simple', () => {
|
||||
assert.deepEqual(
|
||||
transformArguments(),
|
||||
['FUNCTION', 'LIST']
|
||||
);
|
||||
});
|
||||
|
||||
it('with pattern', () => {
|
||||
assert.deepEqual(
|
||||
transformArguments('patter*'),
|
||||
['FUNCTION', 'LIST', 'patter*']
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
testUtils.testWithClient('client.functionList', async client => {
|
||||
await loadMathFunction(client);
|
||||
|
||||
assert.deepEqual(
|
||||
await client.functionList(),
|
||||
[{
|
||||
libraryName: MATH_FUNCTION.name,
|
||||
engine: MATH_FUNCTION.engine,
|
||||
functions: [{
|
||||
name: MATH_FUNCTION.library.square.NAME,
|
||||
description: null,
|
||||
flags: ['no-writes']
|
||||
}]
|
||||
}]
|
||||
);
|
||||
}, GLOBAL.SERVERS.OPEN);
|
||||
});
|
16
packages/client/lib/commands/FUNCTION_LIST.ts
Normal file
16
packages/client/lib/commands/FUNCTION_LIST.ts
Normal file
@ -0,0 +1,16 @@
|
||||
import { RedisCommandArguments } from '.';
|
||||
import { FunctionListItemReply, FunctionListRawItemReply, transformFunctionListItemReply } from './generic-transformers';
|
||||
|
||||
export function transformArguments(pattern?: string): RedisCommandArguments {
|
||||
const args = ['FUNCTION', 'LIST'];
|
||||
|
||||
if (pattern) {
|
||||
args.push(pattern);
|
||||
}
|
||||
|
||||
return args;
|
||||
}
|
||||
|
||||
export function transformReply(reply: Array<FunctionListRawItemReply>): Array<FunctionListItemReply> {
|
||||
return reply.map(transformFunctionListItemReply);
|
||||
}
|
42
packages/client/lib/commands/FUNCTION_LIST_WITHCODE.spec.ts
Normal file
42
packages/client/lib/commands/FUNCTION_LIST_WITHCODE.spec.ts
Normal file
@ -0,0 +1,42 @@
|
||||
import { strict as assert } from 'assert';
|
||||
import testUtils, { GLOBAL } from '../test-utils';
|
||||
import { MATH_FUNCTION, loadMathFunction } from '../client/index.spec';
|
||||
import { transformArguments } from './FUNCTION_LIST_WITHCODE';
|
||||
|
||||
describe('FUNCTION LIST WITHCODE', () => {
|
||||
testUtils.isVersionGreaterThanHook([7, 0]);
|
||||
|
||||
describe('transformArguments', () => {
|
||||
it('simple', () => {
|
||||
assert.deepEqual(
|
||||
transformArguments(),
|
||||
['FUNCTION', 'LIST', 'WITHCODE']
|
||||
);
|
||||
});
|
||||
|
||||
it('with pattern', () => {
|
||||
assert.deepEqual(
|
||||
transformArguments('patter*'),
|
||||
['FUNCTION', 'LIST', 'patter*', 'WITHCODE']
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
testUtils.testWithClient('client.functionListWithCode', async client => {
|
||||
await loadMathFunction(client);
|
||||
|
||||
assert.deepEqual(
|
||||
await client.functionListWithCode(),
|
||||
[{
|
||||
libraryName: MATH_FUNCTION.name,
|
||||
engine: MATH_FUNCTION.engine,
|
||||
functions: [{
|
||||
name: MATH_FUNCTION.library.square.NAME,
|
||||
description: null,
|
||||
flags: ['no-writes']
|
||||
}],
|
||||
libraryCode: MATH_FUNCTION.code
|
||||
}]
|
||||
);
|
||||
}, GLOBAL.SERVERS.OPEN);
|
||||
});
|
26
packages/client/lib/commands/FUNCTION_LIST_WITHCODE.ts
Normal file
26
packages/client/lib/commands/FUNCTION_LIST_WITHCODE.ts
Normal file
@ -0,0 +1,26 @@
|
||||
import { RedisCommandArguments } from '.';
|
||||
import { transformArguments as transformFunctionListArguments } from './FUNCTION_LIST';
|
||||
import { FunctionListItemReply, FunctionListRawItemReply, transformFunctionListItemReply } from './generic-transformers';
|
||||
|
||||
export function transformArguments(pattern?: string): RedisCommandArguments {
|
||||
const args = transformFunctionListArguments(pattern);
|
||||
args.push('WITHCODE');
|
||||
return args;
|
||||
}
|
||||
|
||||
type FunctionListWithCodeRawItemReply = [
|
||||
...FunctionListRawItemReply,
|
||||
'library_code',
|
||||
string
|
||||
];
|
||||
|
||||
interface FunctionListWithCodeItemReply extends FunctionListItemReply {
|
||||
libraryCode: string;
|
||||
}
|
||||
|
||||
export function transformReply(reply: Array<FunctionListWithCodeRawItemReply>): Array<FunctionListWithCodeItemReply> {
|
||||
return reply.map(library => ({
|
||||
...transformFunctionListItemReply(library as unknown as FunctionListRawItemReply),
|
||||
libraryCode: library[7]
|
||||
}));
|
||||
}
|
36
packages/client/lib/commands/FUNCTION_LOAD.spec.ts
Normal file
36
packages/client/lib/commands/FUNCTION_LOAD.spec.ts
Normal file
@ -0,0 +1,36 @@
|
||||
import { strict as assert } from 'assert';
|
||||
import testUtils, { GLOBAL } from '../test-utils';
|
||||
import { MATH_FUNCTION } from '../client/index.spec';
|
||||
import { transformArguments } from './FUNCTION_LOAD';
|
||||
|
||||
describe('FUNCTION LOAD', () => {
|
||||
testUtils.isVersionGreaterThanHook([7, 0]);
|
||||
|
||||
describe('transformArguments', () => {
|
||||
it('simple', () => {
|
||||
assert.deepEqual(
|
||||
transformArguments( 'code'),
|
||||
['FUNCTION', 'LOAD', 'code']
|
||||
);
|
||||
});
|
||||
|
||||
it('with REPLACE', () => {
|
||||
assert.deepEqual(
|
||||
transformArguments('code', {
|
||||
REPLACE: true
|
||||
}),
|
||||
['FUNCTION', 'LOAD', 'REPLACE', 'code']
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
testUtils.testWithClient('client.functionLoad', async client => {
|
||||
assert.equal(
|
||||
await client.functionLoad(
|
||||
MATH_FUNCTION.code,
|
||||
{ REPLACE: true }
|
||||
),
|
||||
MATH_FUNCTION.name
|
||||
);
|
||||
}, GLOBAL.SERVERS.OPEN);
|
||||
});
|
22
packages/client/lib/commands/FUNCTION_LOAD.ts
Normal file
22
packages/client/lib/commands/FUNCTION_LOAD.ts
Normal file
@ -0,0 +1,22 @@
|
||||
import { RedisCommandArguments } from '.';
|
||||
|
||||
interface FunctionLoadOptions {
|
||||
REPLACE?: boolean;
|
||||
}
|
||||
|
||||
export function transformArguments(
|
||||
code: string,
|
||||
options?: FunctionLoadOptions
|
||||
): RedisCommandArguments {
|
||||
const args = ['FUNCTION', 'LOAD'];
|
||||
|
||||
if (options?.REPLACE) {
|
||||
args.push('REPLACE');
|
||||
}
|
||||
|
||||
args.push(code);
|
||||
|
||||
return args;
|
||||
}
|
||||
|
||||
export declare function transformReply(): string;
|
37
packages/client/lib/commands/FUNCTION_RESTORE.spec.ts
Normal file
37
packages/client/lib/commands/FUNCTION_RESTORE.spec.ts
Normal file
@ -0,0 +1,37 @@
|
||||
import { strict as assert } from 'assert';
|
||||
import testUtils, { GLOBAL } from '../test-utils';
|
||||
import { transformArguments } from './FUNCTION_RESTORE';
|
||||
|
||||
describe('FUNCTION RESTORE', () => {
|
||||
testUtils.isVersionGreaterThanHook([7, 0]);
|
||||
|
||||
describe('transformArguments', () => {
|
||||
it('simple', () => {
|
||||
assert.deepEqual(
|
||||
transformArguments('dump'),
|
||||
['FUNCTION', 'RESTORE', 'dump']
|
||||
);
|
||||
});
|
||||
|
||||
it('with mode', () => {
|
||||
assert.deepEqual(
|
||||
transformArguments('dump', 'APPEND'),
|
||||
['FUNCTION', 'RESTORE', 'dump', 'APPEND']
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
testUtils.testWithClient('client.functionRestore', async client => {
|
||||
assert.equal(
|
||||
await client.functionRestore(
|
||||
await client.functionDump(
|
||||
client.commandOptions({
|
||||
returnBuffers: true
|
||||
})
|
||||
),
|
||||
'FLUSH'
|
||||
),
|
||||
'OK'
|
||||
);
|
||||
}, GLOBAL.SERVERS.OPEN);
|
||||
});
|
16
packages/client/lib/commands/FUNCTION_RESTORE.ts
Normal file
16
packages/client/lib/commands/FUNCTION_RESTORE.ts
Normal file
@ -0,0 +1,16 @@
|
||||
import { RedisCommandArgument, RedisCommandArguments } from '.';
|
||||
|
||||
export function transformArguments(
|
||||
dump: RedisCommandArgument,
|
||||
mode?: 'FLUSH' | 'APPEND' | 'REPLACE'
|
||||
): RedisCommandArguments {
|
||||
const args = ['FUNCTION', 'RESTORE', dump];
|
||||
|
||||
if (mode) {
|
||||
args.push(mode);
|
||||
}
|
||||
|
||||
return args;
|
||||
}
|
||||
|
||||
export declare function transformReply(): 'OK';
|
25
packages/client/lib/commands/FUNCTION_STATS.spec.ts
Normal file
25
packages/client/lib/commands/FUNCTION_STATS.spec.ts
Normal file
@ -0,0 +1,25 @@
|
||||
import { strict as assert } from 'assert';
|
||||
import testUtils, { GLOBAL } from '../test-utils';
|
||||
import { transformArguments } from './FUNCTION_STATS';
|
||||
|
||||
describe('FUNCTION STATS', () => {
|
||||
testUtils.isVersionGreaterThanHook([7, 0]);
|
||||
|
||||
it('transformArguments', () => {
|
||||
assert.deepEqual(
|
||||
transformArguments(),
|
||||
['FUNCTION', 'STATS']
|
||||
);
|
||||
});
|
||||
|
||||
testUtils.testWithClient('client.functionStats', async client => {
|
||||
const stats = await client.functionStats();
|
||||
assert.equal(stats.runningScript, null);
|
||||
assert.equal(typeof stats.engines, 'object');
|
||||
for (const [engine, { librariesCount, functionsCount }] of Object.entries(stats.engines)) {
|
||||
assert.equal(typeof engine, 'string');
|
||||
assert.equal(typeof librariesCount, 'number');
|
||||
assert.equal(typeof functionsCount, 'number');
|
||||
}
|
||||
}, GLOBAL.SERVERS.OPEN);
|
||||
});
|
56
packages/client/lib/commands/FUNCTION_STATS.ts
Normal file
56
packages/client/lib/commands/FUNCTION_STATS.ts
Normal file
@ -0,0 +1,56 @@
|
||||
import { RedisCommandArguments } from '.';
|
||||
|
||||
export function transformArguments(): RedisCommandArguments {
|
||||
return ['FUNCTION', 'STATS'];
|
||||
}
|
||||
|
||||
type FunctionStatsRawReply = [
|
||||
'running_script',
|
||||
null | [
|
||||
'name',
|
||||
string,
|
||||
'command',
|
||||
string,
|
||||
'duration_ms',
|
||||
number
|
||||
],
|
||||
'engines',
|
||||
Array<any> // "flat tuples" (there is no way to type that)
|
||||
// ...[string, [
|
||||
// 'libraries_count',
|
||||
// number,
|
||||
// 'functions_count',
|
||||
// number
|
||||
// ]]
|
||||
];
|
||||
|
||||
interface FunctionStatsReply {
|
||||
runningScript: null | {
|
||||
name: string;
|
||||
command: string;
|
||||
durationMs: number;
|
||||
};
|
||||
engines: Record<string, {
|
||||
librariesCount: number;
|
||||
functionsCount: number;
|
||||
}>;
|
||||
}
|
||||
|
||||
export function transformReply(reply: FunctionStatsRawReply): FunctionStatsReply {
|
||||
const engines = Object.create(null);
|
||||
for (let i = 0; i < reply[3].length; i++) {
|
||||
engines[reply[3][i]] = {
|
||||
librariesCount: reply[3][++i][1],
|
||||
functionsCount: reply[3][i][3]
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
runningScript: reply[1] === null ? null : {
|
||||
name: reply[1][1],
|
||||
command: reply[1][3],
|
||||
durationMs: reply[1][5]
|
||||
},
|
||||
engines
|
||||
};
|
||||
}
|
@ -348,6 +348,10 @@ export interface EvalOptions {
|
||||
arguments?: Array<string>;
|
||||
}
|
||||
|
||||
export function evalFirstKeyIndex(options?: EvalOptions): string | undefined {
|
||||
return options?.keys?.[0];
|
||||
}
|
||||
|
||||
export function pushEvalArguments(args: Array<string>, options?: EvalOptions): Array<string> {
|
||||
if (options?.keys) {
|
||||
args.push(
|
||||
@ -491,6 +495,51 @@ export function transformCommandReply(
|
||||
};
|
||||
}
|
||||
|
||||
export enum RedisFunctionFlags {
|
||||
NO_WRITES = 'no-writes',
|
||||
ALLOW_OOM = 'allow-oom',
|
||||
ALLOW_STALE = 'allow-stale',
|
||||
NO_CLUSTER = 'no-cluster'
|
||||
}
|
||||
|
||||
export type FunctionListRawItemReply = [
|
||||
'library_name',
|
||||
string,
|
||||
'engine',
|
||||
string,
|
||||
'functions',
|
||||
Array<[
|
||||
'name',
|
||||
string,
|
||||
'description',
|
||||
string | null,
|
||||
'flags',
|
||||
Array<RedisFunctionFlags>
|
||||
]>
|
||||
];
|
||||
|
||||
export interface FunctionListItemReply {
|
||||
libraryName: string;
|
||||
engine: string;
|
||||
functions: Array<{
|
||||
name: string;
|
||||
description: string | null;
|
||||
flags: Array<RedisFunctionFlags>;
|
||||
}>;
|
||||
}
|
||||
|
||||
export function transformFunctionListItemReply(reply: FunctionListRawItemReply): FunctionListItemReply {
|
||||
return {
|
||||
libraryName: reply[1],
|
||||
engine: reply[3],
|
||||
functions: reply[5].map(fn => ({
|
||||
name: fn[1],
|
||||
description: fn[3],
|
||||
flags: fn[5]
|
||||
}))
|
||||
};
|
||||
}
|
||||
|
||||
export interface SortOptions {
|
||||
BY?: string;
|
||||
LIMIT?: {
|
||||
|
@ -1,22 +1,51 @@
|
||||
import { ClientCommandOptions } from '../client';
|
||||
import { CommandOptions } from '../command-options';
|
||||
import { RedisScriptConfig, SHA1 } from '../lua-script';
|
||||
|
||||
// https://github.com/Microsoft/TypeScript/issues/3496#issuecomment-128553540
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-interface
|
||||
interface RedisCommandRawReplyArray extends Array<RedisCommandRawReply> {}
|
||||
export type RedisCommandRawReply = string | number | Buffer | null | undefined | RedisCommandRawReplyArray;
|
||||
export type RedisCommandRawReply = string | number | Buffer | null | undefined | Array<RedisCommandRawReply>;
|
||||
|
||||
export type RedisCommandArgument = string | Buffer;
|
||||
|
||||
export type RedisCommandArguments = Array<RedisCommandArgument> & { preserve?: unknown };
|
||||
|
||||
export interface RedisCommand {
|
||||
FIRST_KEY_INDEX?: number | ((...args: Array<any>) => RedisCommandArgument);
|
||||
FIRST_KEY_INDEX?: number | ((...args: Array<any>) => RedisCommandArgument | undefined);
|
||||
IS_READ_ONLY?: boolean;
|
||||
transformArguments(this: void, ...args: Array<any>): RedisCommandArguments;
|
||||
transformReply?(this: void, reply: any, preserved?: any): any;
|
||||
}
|
||||
|
||||
export type RedisCommandReply<C extends RedisCommand> = C['transformReply'] extends (...args: any) => infer T ? T : RedisCommandRawReply;
|
||||
export type RedisCommandReply<C extends RedisCommand> =
|
||||
C['transformReply'] extends (...args: any) => infer T ? T : RedisCommandRawReply;
|
||||
|
||||
export type ConvertArgumentType<Type, ToType> =
|
||||
Type extends RedisCommandArgument ? (
|
||||
Type extends (string & ToType) ? Type : ToType
|
||||
) : (
|
||||
Type extends Set<infer Member> ? Set<ConvertArgumentType<Member, ToType>> : (
|
||||
Type extends Map<infer Key, infer Value> ? Map<Key, ConvertArgumentType<Value, ToType>> : (
|
||||
Type extends Array<infer Member> ? Array<ConvertArgumentType<Member, ToType>> : (
|
||||
Type extends Date ? Type : (
|
||||
Type extends Record<PropertyKey, any> ? {
|
||||
[Property in keyof Type]: ConvertArgumentType<Type[Property], ToType>
|
||||
} : Type
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
export type RedisCommandSignature<
|
||||
Command extends RedisCommand,
|
||||
Params extends Array<unknown> = Parameters<Command['transformArguments']>
|
||||
> = <Options extends CommandOptions<ClientCommandOptions>>(
|
||||
...args: Params | [options: Options, ...rest: Params]
|
||||
) => Promise<
|
||||
ConvertArgumentType<
|
||||
RedisCommandReply<Command>,
|
||||
Options['returnBuffers'] extends true ? Buffer : string
|
||||
>
|
||||
>;
|
||||
|
||||
export interface RedisCommands {
|
||||
[command: string]: RedisCommand;
|
||||
@ -30,13 +59,33 @@ export interface RedisModules {
|
||||
[module: string]: RedisModule;
|
||||
}
|
||||
|
||||
export interface RedisFunction extends RedisCommand {
|
||||
NAME: string;
|
||||
NUMBER_OF_KEYS?: number;
|
||||
}
|
||||
|
||||
export interface RedisFunctionLibrary {
|
||||
[fn: string]: RedisFunction;
|
||||
}
|
||||
|
||||
export interface RedisFunctions {
|
||||
[library: string]: RedisFunctionLibrary;
|
||||
}
|
||||
|
||||
export type RedisScript = RedisScriptConfig & SHA1;
|
||||
|
||||
export interface RedisScripts {
|
||||
[script: string]: RedisScript;
|
||||
}
|
||||
|
||||
export interface RedisPlugins<M extends RedisModules, S extends RedisScripts> {
|
||||
export interface RedisExtensions<
|
||||
M extends RedisModules = RedisModules,
|
||||
F extends RedisFunctions = RedisFunctions,
|
||||
S extends RedisScripts = RedisScripts
|
||||
> {
|
||||
modules?: M;
|
||||
functions?: F;
|
||||
scripts?: S;
|
||||
}
|
||||
|
||||
export type ExcludeMappedString<S> = string extends S ? never : S;
|
||||
|
Reference in New Issue
Block a user