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'; import { BasicCommandParser } from '../client/parser'; import { Tail } from '../commands/generic-transformers'; 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: Tail>) => 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 parser = new BasicCommandParser(); command.parseCommand(parser, ...args); const redisArgs: CommandArguments = parser.redisArgs; redisArgs.preserve = parser.preserve; 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 parser = new BasicCommandParser(); command.parseCommand(parser, ...args); const redisArgs: CommandArguments = parser.redisArgs; redisArgs.preserve = parser.preserve; return this._self.addCommand( command.IS_READ_ONLY, redisArgs, transformReply ); }; } private static _createFunctionCommand(name: string, fn: RedisFunction, resp: RespVersions) { const prefix = functionArgumentsPrefix(name, fn); const transformReply = getTransformReply(fn, resp); return function (this: { _self: RedisSentinelMultiCommand }, ...args: Array) { const parser = new BasicCommandParser(); parser.push(...prefix); fn.parseCommand(parser, ...args); const redisArgs: CommandArguments = parser.redisArgs; redisArgs.preserve = parser.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 parser = new BasicCommandParser(); script.parseCommand(parser, ...args); const scriptArgs: CommandArguments = parser.redisArgs; scriptArgs.preserve = parser.preserve; return this.#addScript( script.IS_READ_ONLY, script, scriptArgs, transformReply ); }; } 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 }); } readonly #multi = new RedisMultiCommand(); readonly #sentinel: RedisSentinelType #isReadonly: boolean | undefined = true; constructor(sentinel: RedisSentinelType, typeMapping: TypeMapping) { this.#multi = new RedisMultiCommand(typeMapping); this.#sentinel = sentinel; } #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; } #addScript( isReadonly: boolean | undefined, script: RedisScript, args: CommandArguments, transformReply?: TransformReply ) { this.#setState(isReadonly); this.#multi.addScript(script, 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(); } }