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 CLIENT LIST command and fix CLIENT INFO (#2368)
* fix client info * add client list * fix key validation in transformClientInfoReply * fix issue with field in CLIENT LIST reply * clean code * fix multimem * fix qbufFree argvMem totMem multiMem Co-authored-by: Leibale <me@leibale.com>
This commit is contained in:
@@ -1,7 +1,10 @@
|
||||
import { strict as assert } from 'assert';
|
||||
import { transformArguments, transformReply } from './CLIENT_INFO';
|
||||
import testUtils, { GLOBAL } from '../test-utils';
|
||||
|
||||
describe('CLIENT INFO', () => {
|
||||
testUtils.isVersionGreaterThanHook([6, 2]);
|
||||
|
||||
it('transformArguments', () => {
|
||||
assert.deepEqual(
|
||||
transformArguments(),
|
||||
@@ -9,34 +12,39 @@ describe('CLIENT INFO', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('transformReply', () => {
|
||||
assert.deepEqual(
|
||||
transformReply('id=526512 addr=127.0.0.1:36244 laddr=127.0.0.1:6379 fd=8 name= age=11213 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=26 qbuf-free=40928 argv-mem=10 obl=0 oll=0 omem=0 tot-mem=61466 events=r cmd=client user=default redir=-1\n'),
|
||||
{
|
||||
id: 526512,
|
||||
addr: '127.0.0.1:36244',
|
||||
laddr: '127.0.0.1:6379',
|
||||
fd: 8,
|
||||
name: '',
|
||||
age: 11213,
|
||||
idle: 0,
|
||||
flags: 'N',
|
||||
db: 0,
|
||||
sub: 0,
|
||||
psub: 0,
|
||||
multi: -1,
|
||||
qbuf: 26,
|
||||
qbufFree: 40928,
|
||||
argvMem: 10,
|
||||
obl: 0,
|
||||
oll: 0,
|
||||
omem: 0,
|
||||
totMem: 61466,
|
||||
events: 'r',
|
||||
cmd: 'client',
|
||||
user: 'default',
|
||||
redir: -1
|
||||
}
|
||||
);
|
||||
});
|
||||
testUtils.testWithClient('client.clientInfo', async client => {
|
||||
const reply = await client.clientInfo();
|
||||
assert.equal(typeof reply.id, 'number');
|
||||
assert.equal(typeof reply.addr, 'string');
|
||||
assert.equal(typeof reply.laddr, 'string');
|
||||
assert.equal(typeof reply.fd, 'number');
|
||||
assert.equal(typeof reply.name, 'string');
|
||||
assert.equal(typeof reply.age, 'number');
|
||||
assert.equal(typeof reply.idle, 'number');
|
||||
assert.equal(typeof reply.flags, 'string');
|
||||
assert.equal(typeof reply.db, 'number');
|
||||
assert.equal(typeof reply.sub, 'number');
|
||||
assert.equal(typeof reply.psub, 'number');
|
||||
assert.equal(typeof reply.multi, 'number');
|
||||
assert.equal(typeof reply.qbuf, 'number');
|
||||
assert.equal(typeof reply.qbufFree, 'number');
|
||||
assert.equal(typeof reply.argvMem, 'number');
|
||||
assert.equal(typeof reply.obl, 'number');
|
||||
assert.equal(typeof reply.oll, 'number');
|
||||
assert.equal(typeof reply.omem, 'number');
|
||||
assert.equal(typeof reply.totMem, 'number');
|
||||
assert.equal(typeof reply.events, 'string');
|
||||
assert.equal(typeof reply.cmd, 'string');
|
||||
assert.equal(typeof reply.user, 'string');
|
||||
assert.equal(typeof reply.redir, 'number');
|
||||
|
||||
if (testUtils.isVersionGreaterThan([7, 0])) {
|
||||
assert.equal(typeof reply.multiMem, 'number');
|
||||
assert.equal(typeof reply.resp, 'number');
|
||||
}
|
||||
|
||||
if (testUtils.isVersionGreaterThan([7, 0, 3])) {
|
||||
assert.equal(typeof reply.ssub, 'number');
|
||||
}
|
||||
}, GLOBAL.SERVERS.OPEN);
|
||||
});
|
||||
|
@@ -1,11 +1,13 @@
|
||||
export const IS_READ_ONLY = true;
|
||||
|
||||
export function transformArguments(): Array<string> {
|
||||
return ['CLIENT', 'INFO'];
|
||||
}
|
||||
|
||||
interface ClientInfoReply {
|
||||
export interface ClientInfoReply {
|
||||
id: number;
|
||||
addr: string;
|
||||
laddr: string;
|
||||
laddr?: string; // 6.2
|
||||
fd: number;
|
||||
name: string;
|
||||
age: number;
|
||||
@@ -14,72 +16,74 @@ interface ClientInfoReply {
|
||||
db: number;
|
||||
sub: number;
|
||||
psub: number;
|
||||
ssub?: number; // 7.0.3
|
||||
multi: number;
|
||||
qbuf: number;
|
||||
qbufFree: number;
|
||||
argvMem: number;
|
||||
argvMem?: number; // 6.0
|
||||
multiMem?: number; // 7.0
|
||||
obl: number;
|
||||
oll: number;
|
||||
omem: number;
|
||||
totMem: number;
|
||||
totMem?: number; // 6.0
|
||||
events: string;
|
||||
cmd: string;
|
||||
user: string;
|
||||
redir: number;
|
||||
user?: string; // 6.0
|
||||
redir?: number; // 6.2
|
||||
resp?: number; // 7.0
|
||||
}
|
||||
|
||||
const REGEX = /=([^\s]*)/g;
|
||||
const CLIENT_INFO_REGEX = /([^\s=]+)=([^\s]*)/g;
|
||||
|
||||
export function transformReply(reply: string): ClientInfoReply {
|
||||
const [
|
||||
[, id],
|
||||
[, addr],
|
||||
[, laddr],
|
||||
[, fd],
|
||||
[, name],
|
||||
[, age],
|
||||
[, idle],
|
||||
[, flags],
|
||||
[, db],
|
||||
[, sub],
|
||||
[, psub],
|
||||
[, multi],
|
||||
[, qbuf],
|
||||
[, qbufFree],
|
||||
[, argvMem],
|
||||
[, obl],
|
||||
[, oll],
|
||||
[, omem],
|
||||
[, totMem],
|
||||
[, events],
|
||||
[, cmd],
|
||||
[, user],
|
||||
[, redir]
|
||||
] = [...reply.matchAll(REGEX)];
|
||||
export function transformReply(rawReply: string): ClientInfoReply {
|
||||
const map: Record<string, string> = {};
|
||||
for (const item of rawReply.matchAll(CLIENT_INFO_REGEX)) {
|
||||
map[item[1]] = item[2];
|
||||
}
|
||||
|
||||
return {
|
||||
id: Number(id),
|
||||
addr,
|
||||
laddr,
|
||||
fd: Number(fd),
|
||||
name,
|
||||
age: Number(age),
|
||||
idle: Number(idle),
|
||||
flags,
|
||||
db: Number(db),
|
||||
sub: Number(sub),
|
||||
psub: Number(psub),
|
||||
multi: Number(multi),
|
||||
qbuf: Number(qbuf),
|
||||
qbufFree: Number(qbufFree),
|
||||
argvMem: Number(argvMem),
|
||||
obl: Number(obl),
|
||||
oll: Number(oll),
|
||||
omem: Number(omem),
|
||||
totMem: Number(totMem),
|
||||
events,
|
||||
cmd,
|
||||
user,
|
||||
redir: Number(redir)
|
||||
const reply: ClientInfoReply = {
|
||||
id: Number(map.id),
|
||||
addr: map.addr,
|
||||
fd: Number(map.fd),
|
||||
name: map.name,
|
||||
age: Number(map.age),
|
||||
idle: Number(map.idle),
|
||||
flags: map.flags,
|
||||
db: Number(map.db),
|
||||
sub: Number(map.sub),
|
||||
psub: Number(map.psub),
|
||||
multi: Number(map.multi),
|
||||
qbuf: Number(map.qbuf),
|
||||
qbufFree: Number(map['qbuf-free']),
|
||||
argvMem: Number(map['argv-mem']),
|
||||
obl: Number(map.obl),
|
||||
oll: Number(map.oll),
|
||||
omem: Number(map.omem),
|
||||
totMem: Number(map['tot-mem']),
|
||||
events: map.events,
|
||||
cmd: map.cmd,
|
||||
user: map.user
|
||||
};
|
||||
|
||||
if (map.laddr !== undefined) {
|
||||
reply.laddr = map.laddr;
|
||||
}
|
||||
|
||||
if (map.redir !== undefined) {
|
||||
reply.redir = Number(map.redir);
|
||||
}
|
||||
|
||||
if (map.ssub !== undefined) {
|
||||
reply.ssub = Number(map.ssub);
|
||||
}
|
||||
|
||||
if (map['multi-mem'] !== undefined) {
|
||||
reply.multiMem = Number(map['multi-mem']);
|
||||
}
|
||||
|
||||
if (map.resp !== undefined) {
|
||||
reply.resp = Number(map.resp);
|
||||
}
|
||||
|
||||
return reply;
|
||||
}
|
||||
|
78
packages/client/lib/commands/CLIENT_LIST.spec.ts
Normal file
78
packages/client/lib/commands/CLIENT_LIST.spec.ts
Normal file
@@ -0,0 +1,78 @@
|
||||
import { strict as assert } from 'assert';
|
||||
import { transformArguments, transformReply } from './CLIENT_LIST';
|
||||
import testUtils, { GLOBAL } from '../test-utils';
|
||||
|
||||
describe('CLIENT LIST', () => {
|
||||
describe('transformArguments', () => {
|
||||
it('simple', () => {
|
||||
assert.deepEqual(
|
||||
transformArguments(),
|
||||
['CLIENT', 'LIST']
|
||||
);
|
||||
});
|
||||
|
||||
it('with TYPE', () => {
|
||||
assert.deepEqual(
|
||||
transformArguments({
|
||||
TYPE: 'NORMAL'
|
||||
}),
|
||||
['CLIENT', 'LIST', 'TYPE', 'NORMAL']
|
||||
);
|
||||
});
|
||||
|
||||
it('with ID', () => {
|
||||
assert.deepEqual(
|
||||
transformArguments({
|
||||
ID: ['1', '2']
|
||||
}),
|
||||
['CLIENT', 'LIST', 'ID', '1', '2']
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
testUtils.testWithClient('client.clientList', async client => {
|
||||
const reply = await client.clientList();
|
||||
assert.ok(Array.isArray(reply));
|
||||
|
||||
for (const item of reply) {
|
||||
assert.equal(typeof item.id, 'number');
|
||||
assert.equal(typeof item.addr, 'string');
|
||||
assert.equal(typeof item.fd, 'number');
|
||||
assert.equal(typeof item.name, 'string');
|
||||
assert.equal(typeof item.age, 'number');
|
||||
assert.equal(typeof item.idle, 'number');
|
||||
assert.equal(typeof item.flags, 'string');
|
||||
assert.equal(typeof item.db, 'number');
|
||||
assert.equal(typeof item.sub, 'number');
|
||||
assert.equal(typeof item.psub, 'number');
|
||||
assert.equal(typeof item.multi, 'number');
|
||||
assert.equal(typeof item.qbuf, 'number');
|
||||
assert.equal(typeof item.qbufFree, 'number');
|
||||
assert.equal(typeof item.obl, 'number');
|
||||
assert.equal(typeof item.oll, 'number');
|
||||
assert.equal(typeof item.omem, 'number');
|
||||
assert.equal(typeof item.events, 'string');
|
||||
assert.equal(typeof item.cmd, 'string');
|
||||
|
||||
if (testUtils.isVersionGreaterThan([6, 0])) {
|
||||
assert.equal(typeof item.argvMem, 'number');
|
||||
assert.equal(typeof item.totMem, 'number');
|
||||
assert.equal(typeof item.user, 'string');
|
||||
}
|
||||
|
||||
if (testUtils.isVersionGreaterThan([6, 2])) {
|
||||
assert.equal(typeof item.redir, 'number');
|
||||
assert.equal(typeof item.laddr, 'string');
|
||||
}
|
||||
|
||||
if (testUtils.isVersionGreaterThan([7, 0])) {
|
||||
assert.equal(typeof item.multiMem, 'number');
|
||||
assert.equal(typeof item.resp, 'number');
|
||||
}
|
||||
|
||||
if (testUtils.isVersionGreaterThan([7, 0, 3])) {
|
||||
assert.equal(typeof item.ssub, 'number');
|
||||
}
|
||||
}
|
||||
}, GLOBAL.SERVERS.OPEN);
|
||||
});
|
43
packages/client/lib/commands/CLIENT_LIST.ts
Normal file
43
packages/client/lib/commands/CLIENT_LIST.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
import { RedisCommandArguments, RedisCommandArgument } from '.';
|
||||
import { pushVerdictArguments } from './generic-transformers';
|
||||
import { transformReply as transformClientInfoReply, ClientInfoReply } from './CLIENT_INFO';
|
||||
|
||||
interface ListFilterType {
|
||||
TYPE: 'NORMAL' | 'MASTER' | 'REPLICA' | 'PUBSUB';
|
||||
ID?: never;
|
||||
}
|
||||
|
||||
interface ListFilterId {
|
||||
ID: Array<RedisCommandArgument>;
|
||||
TYPE?: never;
|
||||
}
|
||||
|
||||
export type ListFilter = ListFilterType | ListFilterId;
|
||||
|
||||
export const IS_READ_ONLY = true;
|
||||
|
||||
export function transformArguments(filter?: ListFilter): RedisCommandArguments {
|
||||
let args: RedisCommandArguments = ['CLIENT', 'LIST'];
|
||||
|
||||
if (filter) {
|
||||
if (filter.TYPE !== undefined) {
|
||||
args.push('TYPE', filter.TYPE);
|
||||
} else {
|
||||
args.push('ID');
|
||||
args = pushVerdictArguments(args, filter.ID);
|
||||
}
|
||||
}
|
||||
|
||||
return args;
|
||||
}
|
||||
|
||||
export function transformReply(rawReply: string): Array<ClientInfoReply> {
|
||||
const split = rawReply.split('\n'),
|
||||
length = split.length - 1,
|
||||
reply: Array<ClientInfoReply> = [];
|
||||
for (let i = 0; i < length; i++) {
|
||||
reply.push(transformClientInfoReply(split[i]));
|
||||
}
|
||||
|
||||
return reply;
|
||||
}
|
Reference in New Issue
Block a user