import COMMANDS from '../commands'; import RedisMultiCommand, { MULTI_REPLY, MultiReply, MultiReplyType } from '../multi-command'; import { ReplyWithTypeMapping, CommandReply, Command, CommandArguments, CommanderConfig, RedisFunctions, RedisModules, RedisScripts, RespVersions, TransformReply, RedisScript, RedisFunction, TypeMapping } from '../RESP/types'; import { attachConfig, functionArgumentsPrefix, getTransformReply } from '../commander'; import { RedisSentinelType } from './types'; type CommandSignature< REPLIES extends Array, C extends Command, M extends RedisModules, F extends RedisFunctions, S extends RedisScripts, RESP extends RespVersions, TYPE_MAPPING extends TypeMapping > = (...args: Parameters) => RedisSentinelMultiCommandType< [...REPLIES, ReplyWithTypeMapping, TYPE_MAPPING>], M, F, S, RESP, TYPE_MAPPING >; type WithCommands< REPLIES extends Array, M extends RedisModules, F extends RedisFunctions, S extends RedisScripts, RESP extends RespVersions, TYPE_MAPPING extends TypeMapping > = { [P in keyof typeof COMMANDS]: CommandSignature; }; type WithModules< REPLIES extends Array, M extends RedisModules, F extends RedisFunctions, S extends RedisScripts, RESP extends RespVersions, TYPE_MAPPING extends TypeMapping > = { [P in keyof M]: { [C in keyof M[P]]: CommandSignature; }; }; type WithFunctions< REPLIES extends Array, M extends RedisModules, F extends RedisFunctions, S extends RedisScripts, RESP extends RespVersions, TYPE_MAPPING extends TypeMapping > = { [L in keyof F]: { [C in keyof F[L]]: CommandSignature; }; }; type WithScripts< REPLIES extends Array, M extends RedisModules, F extends RedisFunctions, S extends RedisScripts, RESP extends RespVersions, TYPE_MAPPING extends TypeMapping > = { [P in keyof S]: CommandSignature; }; export type RedisSentinelMultiCommandType< REPLIES extends Array, M extends RedisModules, F extends RedisFunctions, S extends RedisScripts, RESP extends RespVersions, TYPE_MAPPING extends TypeMapping > = ( RedisSentinelMultiCommand & WithCommands & WithModules & WithFunctions & WithScripts ); export default class RedisSentinelMultiCommand { private static _createCommand(command: Command, resp: RespVersions) { const transformReply = getTransformReply(command, resp); return function (this: RedisSentinelMultiCommand, ...args: Array) { const redisArgs = command.transformArguments(...args); return this.addCommand( command.IS_READ_ONLY, redisArgs, transformReply ); }; } private static _createModuleCommand(command: Command, resp: RespVersions) { const transformReply = getTransformReply(command, resp); return function (this: { _self: RedisSentinelMultiCommand }, ...args: Array) { const redisArgs = command.transformArguments(...args); return this._self.addCommand( command.IS_READ_ONLY, redisArgs, transformReply ); }; } private static _createFunctionCommand(name: string, fn: RedisFunction, resp: RespVersions) { const prefix = functionArgumentsPrefix(name, fn), transformReply = getTransformReply(fn, resp); return function (this: { _self: RedisSentinelMultiCommand }, ...args: Array) { const fnArgs = fn.transformArguments(...args); const redisArgs: CommandArguments = prefix.concat(fnArgs); redisArgs.preserve = fnArgs.preserve; return this._self.addCommand( fn.IS_READ_ONLY, redisArgs, transformReply ); }; } private static _createScriptCommand(script: RedisScript, resp: RespVersions) { const transformReply = getTransformReply(script, resp); return function (this: RedisSentinelMultiCommand, ...args: Array) { const scriptArgs = script.transformArguments(...args); this._setState( script.IS_READ_ONLY ); this._multi.addScript( script, scriptArgs, transformReply ); return this; }; } static extend< M extends RedisModules = Record, F extends RedisFunctions = Record, S extends RedisScripts = Record, RESP extends RespVersions = 2 >(config?: CommanderConfig) { return attachConfig({ BaseClass: RedisSentinelMultiCommand, commands: COMMANDS, createCommand: RedisSentinelMultiCommand._createCommand, createModuleCommand: RedisSentinelMultiCommand._createModuleCommand, createFunctionCommand: RedisSentinelMultiCommand._createFunctionCommand, createScriptCommand: RedisSentinelMultiCommand._createScriptCommand, config }); } private readonly _multi = new RedisMultiCommand(); private readonly _sentinel: RedisSentinelType private _isReadonly: boolean | undefined = true; constructor(sentinel: RedisSentinelType) { this._sentinel = sentinel; } private _setState( isReadonly: boolean | undefined, ) { this._isReadonly &&= isReadonly; } addCommand( isReadonly: boolean | undefined, args: CommandArguments, transformReply?: TransformReply ) { this._setState(isReadonly); this._multi.addCommand(args, transformReply); return this; } async exec(execAsPipeline = false) { if (execAsPipeline) return this.execAsPipeline(); return this._multi.transformReplies( await this._sentinel._executeMulti( this._isReadonly, this._multi.queue ) ) as MultiReplyType; } EXEC = this.exec; execTyped(execAsPipeline = false) { return this.exec(execAsPipeline); } async execAsPipeline() { if (this._multi.queue.length === 0) return [] as MultiReplyType; return this._multi.transformReplies( await this._sentinel._executePipeline( this._isReadonly, this._multi.queue ) ) as MultiReplyType; } execAsPipelineTyped() { return this.execAsPipeline(); } }