You've already forked node-redis
mirror of
https://github.com/redis/node-redis.git
synced 2025-08-06 02:15:48 +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:
@@ -2,7 +2,7 @@ import { strict as assert } from 'assert';
|
||||
import testUtils, { GLOBAL, waitTillBeenCalled } from '../test-utils';
|
||||
import RedisClient, { RedisClientType } from '.';
|
||||
import { RedisClientMultiCommandType } from './multi-command';
|
||||
import { RedisCommandArguments, RedisCommandRawReply, RedisModules, RedisScripts } from '../commands';
|
||||
import { RedisCommandArguments, RedisCommandRawReply, RedisModules, RedisFunctions, RedisScripts } from '../commands';
|
||||
import { AbortError, ClientClosedError, ConnectionTimeoutError, DisconnectsClientError, SocketClosedUnexpectedlyError, WatchError } from '../errors';
|
||||
import { defineScript } from '../lua-script';
|
||||
import { spy } from 'sinon';
|
||||
@@ -10,16 +10,42 @@ import { once } from 'events';
|
||||
import { ClientKillFilters } from '../commands/CLIENT_KILL';
|
||||
|
||||
export const SQUARE_SCRIPT = defineScript({
|
||||
NUMBER_OF_KEYS: 0,
|
||||
SCRIPT: 'return ARGV[1] * ARGV[1];',
|
||||
NUMBER_OF_KEYS: 0,
|
||||
transformArguments(number: number): Array<string> {
|
||||
return [number.toString()];
|
||||
},
|
||||
transformReply(reply: number): number {
|
||||
return reply;
|
||||
}
|
||||
});
|
||||
|
||||
export const MATH_FUNCTION = {
|
||||
name: 'math',
|
||||
engine: 'LUA',
|
||||
code: `#!LUA name=math
|
||||
redis.register_function{
|
||||
function_name = "square",
|
||||
callback = function(keys, args) return args[1] * args[1] end,
|
||||
flags = { "no-writes" }
|
||||
}`,
|
||||
library: {
|
||||
square: {
|
||||
NAME: 'square',
|
||||
NUMBER_OF_KEYS: 0,
|
||||
transformArguments(number: number): Array<string> {
|
||||
return [number.toString()];
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export async function loadMathFunction(
|
||||
client: RedisClientType<RedisModules, RedisFunctions, RedisScripts>
|
||||
): Promise<void> {
|
||||
await client.functionLoad(
|
||||
MATH_FUNCTION.code,
|
||||
{ REPLACE: true }
|
||||
);
|
||||
}
|
||||
|
||||
describe('Client', () => {
|
||||
describe('parseURL', () => {
|
||||
it('redis://user:secret@localhost:6379/0', () => {
|
||||
@@ -115,7 +141,14 @@ describe('Client', () => {
|
||||
});
|
||||
|
||||
describe('legacyMode', () => {
|
||||
function sendCommandAsync<M extends RedisModules, S extends RedisScripts>(client: RedisClientType<M, S>, args: RedisCommandArguments): Promise<RedisCommandRawReply> {
|
||||
function sendCommandAsync<
|
||||
M extends RedisModules,
|
||||
F extends RedisFunctions,
|
||||
S extends RedisScripts
|
||||
>(
|
||||
client: RedisClientType<M, F, S>,
|
||||
args: RedisCommandArguments
|
||||
): Promise<RedisCommandRawReply> {
|
||||
return new Promise((resolve, reject) => {
|
||||
(client as any).sendCommand(args, (err: Error | undefined, reply: RedisCommandRawReply) => {
|
||||
if (err) return reject(err);
|
||||
@@ -159,7 +192,14 @@ describe('Client', () => {
|
||||
}
|
||||
});
|
||||
|
||||
function setAsync<M extends RedisModules, S extends RedisScripts>(client: RedisClientType<M, S>, ...args: Array<any>): Promise<RedisCommandRawReply> {
|
||||
function setAsync<
|
||||
M extends RedisModules,
|
||||
F extends RedisFunctions,
|
||||
S extends RedisScripts
|
||||
>(
|
||||
client: RedisClientType<M, F, S>,
|
||||
...args: Array<any>
|
||||
): Promise<RedisCommandRawReply> {
|
||||
return new Promise((resolve, reject) => {
|
||||
(client as any).set(...args, (err: Error | undefined, reply: RedisCommandRawReply) => {
|
||||
if (err) return reject(err);
|
||||
@@ -205,7 +245,11 @@ describe('Client', () => {
|
||||
}
|
||||
});
|
||||
|
||||
function multiExecAsync<M extends RedisModules, S extends RedisScripts>(multi: RedisClientMultiCommandType<M, S>): Promise<Array<RedisCommandRawReply>> {
|
||||
function multiExecAsync<
|
||||
M extends RedisModules,
|
||||
F extends RedisFunctions,
|
||||
S extends RedisScripts
|
||||
>(multi: RedisClientMultiCommandType<M, F, S>): Promise<Array<RedisCommandRawReply>> {
|
||||
return new Promise((resolve, reject) => {
|
||||
(multi as any).exec((err: Error | undefined, replies: Array<RedisCommandRawReply>) => {
|
||||
if (err) return reject(err);
|
||||
@@ -439,25 +483,44 @@ describe('Client', () => {
|
||||
}
|
||||
});
|
||||
|
||||
const module = {
|
||||
echo: {
|
||||
transformArguments(message: string): Array<string> {
|
||||
return ['ECHO', message];
|
||||
},
|
||||
transformReply(reply: string): string {
|
||||
return reply;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
testUtils.testWithClient('modules', async client => {
|
||||
// assert.equal(
|
||||
// await client.module.echo('message'),
|
||||
// 'message'
|
||||
// );
|
||||
assert.equal(
|
||||
await client.module.echo('message'),
|
||||
'message'
|
||||
);
|
||||
}, {
|
||||
...GLOBAL.SERVERS.OPEN,
|
||||
clientOptions: {
|
||||
modules: {
|
||||
module: {
|
||||
echo: {
|
||||
transformArguments(message: string): Array<string> {
|
||||
return ['ECHO', message];
|
||||
},
|
||||
transformReply(reply: string): string {
|
||||
return reply;
|
||||
}
|
||||
}
|
||||
}
|
||||
module
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
testUtils.testWithClient('functions', async client => {
|
||||
await loadMathFunction(client);
|
||||
|
||||
assert.equal(
|
||||
await client.math.square(2),
|
||||
4
|
||||
);
|
||||
}, {
|
||||
...GLOBAL.SERVERS.OPEN,
|
||||
minimumDockerVersion: [7, 0],
|
||||
clientOptions: {
|
||||
functions: {
|
||||
math: MATH_FUNCTION.library
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -468,9 +531,13 @@ describe('Client', () => {
|
||||
assert.ok(id !== isolatedId);
|
||||
}, GLOBAL.SERVERS.OPEN);
|
||||
|
||||
async function killClient<M extends RedisModules, S extends RedisScripts>(
|
||||
client: RedisClientType<M, S>,
|
||||
errorClient: RedisClientType<M, S> = client
|
||||
async function killClient<
|
||||
M extends RedisModules,
|
||||
F extends RedisFunctions,
|
||||
S extends RedisScripts
|
||||
>(
|
||||
client: RedisClientType<M, F, S>,
|
||||
errorClient: RedisClientType<M, F, S> = client
|
||||
): Promise<void> {
|
||||
const onceErrorPromise = once(errorClient, 'error');
|
||||
await client.sendCommand(['QUIT']);
|
||||
@@ -684,7 +751,9 @@ describe('Client', () => {
|
||||
|
||||
try {
|
||||
await assert.doesNotReject(Promise.all([
|
||||
subscriber.subscribe('channel', () => {}),
|
||||
subscriber.subscribe('channel', () => {
|
||||
// noop
|
||||
}),
|
||||
publisher.publish('channel', 'message')
|
||||
]));
|
||||
} finally {
|
||||
|
Reference in New Issue
Block a user