diff --git a/packages/client/lib/client/index.spec.ts b/packages/client/lib/client/index.spec.ts index 60e776b0dd..1ae449ccbb 100644 --- a/packages/client/lib/client/index.spec.ts +++ b/packages/client/lib/client/index.spec.ts @@ -14,6 +14,8 @@ import { RESP_TYPES } from '../RESP/decoder'; import { NumberReply } from '../RESP/types'; import { SortedSetMember } from '../commands/generic-transformers'; +import {version} from '../../package.json'; + export const SQUARE_SCRIPT = defineScript({ SCRIPT: `local number = redis.call('GET', KEYS[1]) @@ -120,6 +122,44 @@ describe('Client', () => { } }); + testUtils.testWithClient('should set default lib name and version', async client => { + const clientInfo = await client.clientInfo(); + + assert.equal(clientInfo.libName, 'node-redis'); + assert.equal(clientInfo.libVer, version); + }, { + ...GLOBAL.SERVERS.PASSWORD, + minimumDockerVersion: [7, 2] + }); + + testUtils.testWithClient('disable sending lib name and version', async client => { + const clientInfo = await client.clientInfo(); + + assert.equal(clientInfo.libName, ''); + assert.equal(clientInfo.libVer, ''); + }, { + ...GLOBAL.SERVERS.PASSWORD, + clientOptions: { + ...GLOBAL.SERVERS.PASSWORD.clientOptions, + disableClientInfo: true + }, + minimumDockerVersion: [7, 2] + }); + + testUtils.testWithClient('send client name tag', async client => { + const clientInfo = await client.clientInfo(); + + assert.equal(clientInfo.libName, 'node-redis(test)'); + assert.equal(clientInfo.libVer, version); + }, { + ...GLOBAL.SERVERS.PASSWORD, + clientOptions: { + ...GLOBAL.SERVERS.PASSWORD.clientOptions, + clientInfoTag: "test" + }, + minimumDockerVersion: [7, 2] + }); + testUtils.testWithClient('connect, ready and end events', async client => { await Promise.all([ once(client, 'connect'), diff --git a/packages/client/lib/client/index.ts b/packages/client/lib/client/index.ts index 4979c905b2..5b5cccaa8b 100644 --- a/packages/client/lib/client/index.ts +++ b/packages/client/lib/client/index.ts @@ -3,7 +3,7 @@ import RedisSocket, { RedisSocketOptions, RedisTlsSocketOptions } from './socket import RedisCommandsQueue, { CommandOptions } from './commands-queue'; import { EventEmitter } from 'node:events'; import { attachConfig, functionArgumentsPrefix, getTransformReply, scriptArgumentsPrefix } from '../commander'; -import { ClientClosedError, ClientOfflineError, DisconnectsClientError, WatchError } from '../errors'; +import { ClientClosedError, ClientOfflineError, DisconnectsClientError, WatchError, ErrorReply } from '../errors'; import { URL } from 'node:url'; import { TcpSocketConnectOpts } from 'node:net'; import { PubSubType, PubSubListener, PubSubTypeListeners, ChannelListeners } from './pub-sub'; @@ -15,6 +15,8 @@ import { ScanOptions, ScanCommonOptions } from '../commands/SCAN'; import { RedisLegacyClient, RedisLegacyClientType } from './legacy-mode'; import { RedisPoolOptions, RedisClientPool } from './pool'; +import {version} from '../../package.json'; + export interface RedisClientOptions< M extends RedisModules = RedisModules, F extends RedisFunctions = RedisFunctions, @@ -69,6 +71,14 @@ export interface RedisClientOptions< * TODO */ commandOptions?: CommandOptions; + /** + * Don't send client info to Redis server + */ + disableClientInfo?: boolean; + /** + * Tag to append to library name that is sent to the Redis server + */ + clientInfoTag?: string; } type WithCommands< @@ -352,6 +362,33 @@ export default class RedisClient< ); } + if (!this._options?.disableClientInfo) { + promises.push( + this._queue.addCommand( + [ 'CLIENT', 'SETINFO', 'LIB-VER', version], + { asap: true } + ).catch(err => { + if (!(err instanceof ErrorReply)) { + throw err; + } + }) + ); + + promises.push( + this._queue.addCommand( + [ + 'CLIENT', 'SETINFO', 'LIB-NAME', + this._options?.clientInfoTag ? `node-redis(${this._options.clientInfoTag})` : 'node-redis' + ], + { asap: true } + ).catch(err => { + if (!(err instanceof ErrorReply)) { + throw err; + } + }) + ); + } + if (this._options?.RESP) { const hello: HelloOptions = {}; diff --git a/packages/client/lib/commands/CLIENT_INFO.ts b/packages/client/lib/commands/CLIENT_INFO.ts index 88721e2f8b..0a1b4ddce9 100644 --- a/packages/client/lib/commands/CLIENT_INFO.ts +++ b/packages/client/lib/commands/CLIENT_INFO.ts @@ -51,6 +51,12 @@ export interface ClientInfoReply { * available since 7.0 */ resp?: number; + /** + * available since 7.2 + */ + libName?: string; + libVer?: string; + } const CLIENT_INFO_REGEX = /([^\s=]+)=([^\s]*)/g; @@ -88,7 +94,9 @@ export default { totMem: Number(map['tot-mem']), events: map.events, cmd: map.cmd, - user: map.user + user: map.user, + libName: map['lib-name'], + libVer: map['lib-ver'], }; if (map.laddr !== undefined) { diff --git a/packages/client/lib/test-utils.ts b/packages/client/lib/test-utils.ts index ebdec826f6..81aac6f9b0 100644 --- a/packages/client/lib/test-utils.ts +++ b/packages/client/lib/test-utils.ts @@ -5,7 +5,7 @@ import { setTimeout } from 'node:timers/promises'; const utils = new TestUtils({ dockerImageName: 'redis', dockerImageVersionArgument: 'redis-version', - defaultDockerVersion: '7.2-rc' + defaultDockerVersion: '7.2' }); export default utils; diff --git a/packages/client/tsconfig.json b/packages/client/tsconfig.json index 5e044cbaa1..d460f1613a 100644 --- a/packages/client/tsconfig.json +++ b/packages/client/tsconfig.json @@ -5,7 +5,8 @@ }, "include": [ "./index.ts", - "./lib/**/*.ts" + "./lib/**/*.ts", + "./package.json" ], "exclude": [ "./lib/test-utils.ts", diff --git a/tsconfig.base.json b/tsconfig.base.json index 3bec460a0d..24b80987d6 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -13,6 +13,7 @@ "composite": true, "declaration": true, - "allowJs": true + "allowJs": true, + "resolveJsonModule": true } }