Skip to content

Commit 3fd865b

Browse files
author
Ruben Bridgewater
committed
Move the exposed and documented api into a separate file
1 parent 861749f commit 3fd865b

File tree

3 files changed

+228
-60
lines changed

3 files changed

+228
-60
lines changed

index.js

+10-47
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,14 @@ util.inherits(RedisClient, EventEmitter);
180180

181181
RedisClient.connection_id = 0;
182182

183+
/******************************************************************************
184+
185+
All functions in here are internal besides the RedisClient constructor
186+
and the exported functions. Don't rely on them as they will be private
187+
functions in node_redis v.3
188+
189+
******************************************************************************/
190+
183191
// Attention: the function name "create_stream" should not be changed, as other libraries need this to mock the stream (e.g. fakeredis)
184192
RedisClient.prototype.create_stream = function () {
185193
var self = this;
@@ -269,17 +277,6 @@ RedisClient.prototype.handle_reply = function (reply, command) {
269277
RedisClient.prototype.cork = noop;
270278
RedisClient.prototype.uncork = noop;
271279

272-
RedisClient.prototype.duplicate = function (options) {
273-
var existing_options = utils.clone(this.options);
274-
options = utils.clone(options);
275-
for (var elem in options) { // jshint ignore: line
276-
existing_options[elem] = options[elem];
277-
}
278-
var client = new RedisClient(existing_options);
279-
client.selected_db = this.selected_db;
280-
return client;
281-
};
282-
283280
RedisClient.prototype.initialize_retry_vars = function () {
284281
this.retry_timer = null;
285282
this.retry_totaltime = 0;
@@ -288,18 +285,6 @@ RedisClient.prototype.initialize_retry_vars = function () {
288285
this.attempts = 1;
289286
};
290287

291-
RedisClient.prototype.unref = function () {
292-
if (this.connected) {
293-
debug("Unref'ing the socket connection");
294-
this.stream.unref();
295-
} else {
296-
debug('Not connected yet, will unref later');
297-
this.once('connect', function () {
298-
this.unref();
299-
});
300-
}
301-
};
302-
303288
RedisClient.prototype.warn = function (msg) {
304289
var self = this;
305290
// Warn on the next tick. Otherwise no event listener can be added
@@ -918,36 +903,14 @@ RedisClient.prototype.write = function (data) {
918903
return;
919904
};
920905

921-
RedisClient.prototype.end = function (flush) {
922-
// Flush queue if wanted
923-
if (flush) {
924-
this.flush_and_error(new Error("The command can't be processed. The connection has already been closed."));
925-
} else if (arguments.length === 0) {
926-
this.warn(
927-
'Using .end() without the flush parameter is deprecated and throws from v.3.0.0 on.\n' +
928-
'Please check the doku (https://github.com/NodeRedis/node_redis) and explictly use flush.'
929-
);
930-
}
931-
// Clear retry_timer
932-
if (this.retry_timer) {
933-
clearTimeout(this.retry_timer);
934-
this.retry_timer = null;
935-
}
936-
this.stream.removeAllListeners();
937-
this.stream.on('error', noop);
938-
this.connected = false;
939-
this.ready = false;
940-
this.closing = true;
941-
return this.stream.destroySoon();
942-
};
943-
944906
exports.createClient = function () {
945907
return new RedisClient(unifyOptions.apply(null, arguments));
946908
};
947909
exports.RedisClient = RedisClient;
948910
exports.print = utils.print;
949911
exports.Multi = require('./lib/multi');
950912

951-
// Add all redis commands to the client
913+
// Add all redis commands / node_redis api to the client
952914
require('./lib/individualCommands');
915+
require('./lib/extendedApi');
953916
require('./lib/commands');

lib/extendedApi.js

+91
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
'use strict';
2+
3+
var utils = require('./utils');
4+
var debug = require('./debug');
5+
var RedisClient = require('../').RedisClient;
6+
var noop = function () {};
7+
8+
/**********************************************
9+
All documented and exposed API belongs in here
10+
**********************************************/
11+
12+
// Redirect calls to the appropriate function and use to send arbitrary / not supported commands
13+
RedisClient.prototype.send_command = function (command, args, callback) {
14+
// Throw to fail early instead of relying in order in this case
15+
if (typeof command !== 'string') {
16+
throw new Error('Wrong input type "' + (command !== null && command !== undefined ? command.constructor.name : command) + '" for command name');
17+
}
18+
if (!Array.isArray(args)) {
19+
if (args === undefined || args === null) {
20+
args = [];
21+
} else if (typeof args === 'function' && callback === undefined) {
22+
callback = args;
23+
args = [];
24+
} else {
25+
throw new Error('Wrong input type "' + args.constructor.name + '" for args');
26+
}
27+
}
28+
if (typeof callback !== 'function' && callback !== undefined) {
29+
throw new Error('Wrong input type "' + (callback !== null ? callback.constructor.name : 'null') + '" for callback function');
30+
}
31+
32+
// Using the raw multi command is only possible with this function
33+
// If the command is not yet added to the client, the internal function should be called right away
34+
// Otherwise we need to redirect the calls to make sure the interal functions don't get skipped
35+
// The internal functions could actually be used for any non hooked function
36+
// but this might change from time to time and at the moment there's no good way to distinguishe them
37+
// from each other, so let's just do it do it this way for the time being
38+
if (command === 'multi' || typeof this[command] !== 'function') {
39+
return this.internal_send_command(command, args, callback);
40+
}
41+
if (typeof callback === 'function') {
42+
args = args.concat([callback]);
43+
}
44+
return this[command].apply(this, args);
45+
};
46+
47+
RedisClient.prototype.end = function (flush) {
48+
// Flush queue if wanted
49+
if (flush) {
50+
this.flush_and_error(new Error("The command can't be processed. The connection has already been closed."));
51+
} else if (arguments.length === 0) {
52+
this.warn(
53+
'Using .end() without the flush parameter is deprecated and throws from v.3.0.0 on.\n' +
54+
'Please check the doku (https://github.com/NodeRedis/node_redis) and explictly use flush.'
55+
);
56+
}
57+
// Clear retry_timer
58+
if (this.retry_timer) {
59+
clearTimeout(this.retry_timer);
60+
this.retry_timer = null;
61+
}
62+
this.stream.removeAllListeners();
63+
this.stream.on('error', noop);
64+
this.connected = false;
65+
this.ready = false;
66+
this.closing = true;
67+
return this.stream.destroySoon();
68+
};
69+
70+
RedisClient.prototype.unref = function () {
71+
if (this.connected) {
72+
debug("Unref'ing the socket connection");
73+
this.stream.unref();
74+
} else {
75+
debug('Not connected yet, will unref later');
76+
this.once('connect', function () {
77+
this.unref();
78+
});
79+
}
80+
};
81+
82+
RedisClient.prototype.duplicate = function (options) {
83+
var existing_options = utils.clone(this.options);
84+
options = utils.clone(options);
85+
for (var elem in options) { // jshint ignore: line
86+
existing_options[elem] = options[elem];
87+
}
88+
var client = new RedisClient(existing_options);
89+
client.selected_db = this.selected_db;
90+
return client;
91+
};

test/node_redis.spec.js

+127-13
Original file line numberDiff line numberDiff line change
@@ -109,33 +109,147 @@ describe('The node_redis client', function () {
109109

110110
describe('send_command', function () {
111111

112-
it('omitting args should be fine in some cases', function (done) {
112+
it('omitting args should be fine', function (done) {
113+
client.server_info = {};
114+
client.send_command('info');
115+
client.send_command('ping', function (err, res) {
116+
assert.strictEqual(res, 'PONG');
117+
// Check if the previous info command used the internal individual info command
118+
assert.notDeepEqual(client.server_info, {});
119+
client.server_info = {};
120+
});
121+
client.send_command('info', null, undefined);
122+
client.send_command('ping', null, function (err, res) {
123+
assert.strictEqual(res, 'PONG');
124+
// Check if the previous info command used the internal individual info command
125+
assert.notDeepEqual(client.server_info, {});
126+
client.server_info = {};
127+
});
128+
client.send_command('info', undefined, undefined);
129+
client.send_command('ping', function (err, res) {
130+
assert.strictEqual(res, 'PONG');
131+
// Check if the previous info command used the internal individual info command
132+
assert.notDeepEqual(client.server_info, {});
133+
client.server_info = {};
134+
});
113135
client.send_command('info', undefined, function (err, res) {
114136
assert(/redis_version/.test(res));
137+
// The individual info command should also be called by using send_command
138+
// console.log(info, client.server_info);
139+
assert.notDeepEqual(client.server_info, {});
140+
done();
141+
});
142+
});
143+
144+
it('using multi with send_command should work as individual command instead of using the internal multi', function (done) {
145+
// This is necessary to keep backwards compatibility and it is the only way to handle multis as you want in node_redis
146+
client.send_command('multi');
147+
client.send_command('set', ['foo', 'bar'], helper.isString('QUEUED'));
148+
client.get('foo');
149+
client.exec(function (err, res) { // exec is not manipulated if not fired by the individual multi command
150+
// As the multi command is handled individually by the user he also has to handle the return value
151+
assert.strictEqual(res[0].toString(), 'OK');
152+
assert.strictEqual(res[1].toString(), 'bar');
115153
done();
116154
});
117155
});
118156

119-
it('using another type as cb should just work as if there were no callback parameter', function (done) {
120-
client.send_command('set', ['test', 'bla'], [true]);
121-
client.get('test', function (err, res) {
122-
assert.equal(res, 'bla');
157+
it('multi should be handled special', function (done) {
158+
client.send_command('multi', undefined, helper.isString('OK'));
159+
var args = ['test', 'bla'];
160+
client.send_command('set', args, helper.isString('QUEUED'));
161+
assert.deepEqual(args, ['test', 'bla']); // Check args manipulation
162+
client.get('test', helper.isString('QUEUED'));
163+
client.exec(function (err, res) {
164+
// As the multi command is handled individually by the user he also has to handle the return value
165+
assert.strictEqual(res[0].toString(), 'OK');
166+
assert.strictEqual(res[1].toString(), 'bla');
167+
done();
168+
});
169+
});
170+
171+
it('using another type as cb should throw', function () {
172+
try {
173+
client.send_command('set', ['test', 'bla'], [true]);
174+
throw new Error('failed');
175+
} catch (err) {
176+
assert.strictEqual(err.message, 'Wrong input type "Array" for callback function');
177+
}
178+
try {
179+
client.send_command('set', ['test', 'bla'], null);
180+
throw new Error('failed');
181+
} catch (err) {
182+
assert.strictEqual(err.message, 'Wrong input type "null" for callback function');
183+
}
184+
});
185+
186+
it('command argument has to be of type string', function () {
187+
try {
188+
client.send_command(true, ['test', 'bla'], function () {});
189+
throw new Error('failed');
190+
} catch (err) {
191+
assert.strictEqual(err.message, 'Wrong input type "Boolean" for command name');
192+
}
193+
try {
194+
client.send_command(undefined, ['test', 'bla'], function () {});
195+
throw new Error('failed');
196+
} catch (err) {
197+
assert.strictEqual(err.message, 'Wrong input type "undefined" for command name');
198+
}
199+
try {
200+
client.send_command(null, ['test', 'bla'], function () {});
201+
throw new Error('failed');
202+
} catch (err) {
203+
assert.strictEqual(err.message, 'Wrong input type "null" for command name');
204+
}
205+
});
206+
207+
it('args may only be of type Array or undefined', function () {
208+
try {
209+
client.send_command('info', 123);
210+
throw new Error('failed');
211+
} catch (err) {
212+
assert.strictEqual(err.message, 'Wrong input type "Number" for args');
213+
}
214+
});
215+
216+
it('passing a callback as args and as callback should throw', function () {
217+
try {
218+
client.send_command('info', function a () {}, function b () {});
219+
throw new Error('failed');
220+
} catch (err) {
221+
assert.strictEqual(err.message, 'Wrong input type "Function" for args');
222+
}
223+
});
224+
225+
it('multi should be handled special', function (done) {
226+
client.send_command('multi', undefined, helper.isString('OK'));
227+
var args = ['test', 'bla'];
228+
client.send_command('set', args, helper.isString('QUEUED'));
229+
assert.deepEqual(args, ['test', 'bla']); // Check args manipulation
230+
client.get('test', helper.isString('QUEUED'));
231+
client.exec(function (err, res) {
232+
// As the multi command is handled individually by the user he also has to handle the return value
233+
assert.strictEqual(res[0].toString(), 'OK');
234+
assert.strictEqual(res[1].toString(), 'bla');
123235
done();
124236
});
125237
});
126238

127-
it('misusing the function should eventually throw (no command)', function (done) {
128-
client.send_command(true, 'info', function (err, res) {
129-
assert(/ERR Protocol error/.test(err.message));
130-
assert.equal(err.command, undefined);
131-
assert.equal(err.code, 'ERR');
239+
it('the args array may contain a arbitrary number of arguments', function (done) {
240+
client.send_command('mset', ['foo', 1, 'bar', 2, 'baz', 3], helper.isString('OK'));
241+
client.mget(['foo', 'bar', 'baz'], function (err, res) {
242+
// As the multi command is handled individually by the user he also has to handle the return value
243+
assert.strictEqual(res[0].toString(), '1');
244+
assert.strictEqual(res[1].toString(), '2');
245+
assert.strictEqual(res[2].toString(), '3');
132246
done();
133247
});
134248
});
135249

136-
it('misusing the function should eventually throw (wrong args)', function (done) {
137-
client.send_command('info', false, function (err, res) {
138-
assert.equal(err.message, 'ERR Protocol error: invalid multibulk length');
250+
it('send_command with callback as args', function (done) {
251+
client.send_command('abcdef', function (err, res) {
252+
assert.strictEqual(err.message, "ERR unknown command 'abcdef'");
139253
done();
140254
});
141255
});

0 commit comments

Comments
 (0)