diff --git a/packages/client/lib/client/commands.ts b/packages/client/lib/client/commands.ts index e2b0a8cd27..0ec33583bd 100644 --- a/packages/client/lib/client/commands.ts +++ b/packages/client/lib/client/commands.ts @@ -24,6 +24,7 @@ import * as CLIENT_KILL from '../commands/CLIENT_KILL'; import * as CLIENT_NO_EVICT from '../commands/CLIENT_NO-EVICT'; import * as CLIENT_PAUSE from '../commands/CLIENT_PAUSE'; import * as CLIENT_SETNAME from '../commands/CLIENT_SETNAME'; +import * as CLIENT_TRACKING from '../commands/CLIENT_TRACKING'; import * as CLIENT_UNPAUSE from '../commands/CLIENT_UNPAUSE'; import * as CLIENT_INFO from '../commands/CLIENT_INFO'; import * as CLUSTER_ADDSLOTS from '../commands/CLUSTER_ADDSLOTS'; @@ -165,6 +166,8 @@ export default { clientPause: CLIENT_PAUSE, CLIENT_SETNAME, clientSetName: CLIENT_SETNAME, + CLIENT_TRACKING, + clientTracking: CLIENT_TRACKING, CLIENT_UNPAUSE, clientUnpause: CLIENT_UNPAUSE, CLIENT_INFO, diff --git a/packages/client/lib/commands/CLIENT_TRACKING.spec.ts b/packages/client/lib/commands/CLIENT_TRACKING.spec.ts new file mode 100644 index 0000000000..bbd0b13e77 --- /dev/null +++ b/packages/client/lib/commands/CLIENT_TRACKING.spec.ts @@ -0,0 +1,101 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments } from './CLIENT_TRACKING'; + +describe('CLIENT TRACKING', () => { + testUtils.isVersionGreaterThanHook([6]); + + describe('transformArguments', () => { + describe('true', () => { + it('simple', () => { + assert.deepEqual( + transformArguments(true), + ['CLIENT', 'TRACKING', 'ON'] + ); + }); + + it('with REDIRECT', () => { + assert.deepEqual( + transformArguments(true, { + REDIRECT: 1 + }), + ['CLIENT', 'TRACKING', 'ON', 'REDIRECT', '1'] + ); + }); + + describe('with BCAST', () => { + it('simple', () => { + assert.deepEqual( + transformArguments(true, { + BCAST: true + }), + ['CLIENT', 'TRACKING', 'ON', 'BCAST'] + ); + }); + + describe('with PREFIX', () => { + it('string', () => { + assert.deepEqual( + transformArguments(true, { + BCAST: true, + PREFIX: 'prefix' + }), + ['CLIENT', 'TRACKING', 'ON', 'BCAST', 'PREFIX', 'prefix'] + ); + }); + + it('array', () => { + assert.deepEqual( + transformArguments(true, { + BCAST: true, + PREFIX: ['1', '2'] + }), + ['CLIENT', 'TRACKING', 'ON', 'BCAST', 'PREFIX', '1', 'PREFIX', '2'] + ); + }); + }); + }); + + it('with OPTIN', () => { + assert.deepEqual( + transformArguments(true, { + OPTIN: true + }), + ['CLIENT', 'TRACKING', 'ON', 'OPTIN'] + ); + }); + + it('with OPTOUT', () => { + assert.deepEqual( + transformArguments(true, { + OPTOUT: true + }), + ['CLIENT', 'TRACKING', 'ON', 'OPTOUT'] + ); + }); + + it('with NOLOOP', () => { + assert.deepEqual( + transformArguments(true, { + NOLOOP: true + }), + ['CLIENT', 'TRACKING', 'ON', 'NOLOOP'] + ); + }); + }); + + it('false', () => { + assert.deepEqual( + transformArguments(false), + ['CLIENT', 'TRACKING', 'OFF'] + ); + }); + }); + + testUtils.testWithClient('client.clientTracking', async client => { + assert.equal( + await client.clientTracking(false), + 'OK' + ); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/client/lib/commands/CLIENT_TRACKING.ts b/packages/client/lib/commands/CLIENT_TRACKING.ts new file mode 100644 index 0000000000..c70702706e --- /dev/null +++ b/packages/client/lib/commands/CLIENT_TRACKING.ts @@ -0,0 +1,83 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; + +interface CommonOptions { + REDIRECT?: number; + NOLOOP?: boolean; +} + +interface BroadcastOptions { + BCAST?: boolean; + PREFIX?: RedisCommandArgument | Array; +} + +interface OptInOptions { + OPTIN?: boolean; +} + +interface OptOutOptions { + OPTOUT?: boolean; +} + +type ClientTrackingOptions = CommonOptions & ( + BroadcastOptions | + OptInOptions | + OptOutOptions +); + +export function transformArguments( + mode: M, + options?: M extends true ? ClientTrackingOptions : undefined +): RedisCommandArguments { + const args: RedisCommandArguments = [ + 'CLIENT', + 'TRACKING', + mode ? 'ON' : 'OFF' + ]; + + if (mode) { + if (options?.REDIRECT) { + args.push( + 'REDIRECT', + options.REDIRECT.toString() + ); + } + + if (isBroadcast(options)) { + args.push('BCAST'); + + if (options?.PREFIX) { + if (Array.isArray(options.PREFIX)) { + for (const prefix of options.PREFIX) { + args.push('PREFIX', prefix); + } + } else { + args.push('PREFIX', options.PREFIX); + } + } + } else if (isOptIn(options)) { + args.push('OPTIN'); + } else if (isOptOut(options)) { + args.push('OPTOUT'); + } + + if (options?.NOLOOP) { + args.push('NOLOOP'); + } + } + + return args; +} + +function isBroadcast(options?: ClientTrackingOptions): options is BroadcastOptions { + return (options as BroadcastOptions)?.BCAST === true; +} + +function isOptIn(options?: ClientTrackingOptions): options is OptInOptions { + return (options as OptInOptions)?.OPTIN === true; +} + +function isOptOut(options?: ClientTrackingOptions): options is OptOutOptions { + return (options as OptOutOptions)?.OPTOUT === true; +} + +export declare function transformReply(): 'OK' | Buffer;