Skip to content

Commit dd23a14

Browse files
authored
Merge pull request #1857 from strongloop/feat/execute-db-command
feat: improve `dataSource.execute` to support more flavors
2 parents 7835a43 + c884c62 commit dd23a14

File tree

2 files changed

+72
-15
lines changed

2 files changed

+72
-15
lines changed

lib/datasource.js

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2690,19 +2690,10 @@ DataSource.prototype.ping = function(cb) {
26902690
* Execute an arbitrary command. The commands are connector specific,
26912691
* please refer to the documentation of your connector for more details.
26922692
*
2693-
* @param command String|Object The command to execute, e.g. an SQL query.
2694-
* @param [args] Array Parameters values to set in the command.
2695-
* @param [options] Object Additional options, e.g. the transaction to use.
2693+
* @param [...params] Array The command and its arguments, e.g. an SQL query.
26962694
* @returns Promise A promise of the result
26972695
*/
2698-
DataSource.prototype.execute = function(command, args = [], options = {}) {
2699-
assert(typeof command === 'string' || typeof command === 'object',
2700-
'"command" must be a string or an object.');
2701-
assert(typeof args === 'object',
2702-
'"args" must be an object, an array or undefined.');
2703-
assert(typeof options === 'object',
2704-
'"options" must be an object or undefined.');
2705-
2696+
DataSource.prototype.execute = function(...params) {
27062697
if (!this.connector) {
27072698
return Promise.reject(errorNotImplemented(
27082699
`DataSource "${this.name}" is missing a connector to execute the command.`,
@@ -2717,7 +2708,7 @@ DataSource.prototype.execute = function(command, args = [], options = {}) {
27172708
}
27182709

27192710
return new Promise((resolve, reject) => {
2720-
this.connector.execute(command, args, options, onExecuted);
2711+
this.connector.execute(...params, onExecuted);
27212712
function onExecuted(err, result) {
27222713
if (err) return reject(err);
27232714
if (arguments.length > 2) {

test/datasource.test.js

Lines changed: 69 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -415,6 +415,14 @@ describe('DataSource', function() {
415415
it('supports shorthand version (cmd)', async () => {
416416
let called = 'not called';
417417
ds.connector.execute = function(command, args, options, callback) {
418+
// copied from loopback-connector/lib/sql.js
419+
if (typeof args === 'function' && options === undefined && callback === undefined) {
420+
// execute(sql, callback)
421+
options = {};
422+
callback = args;
423+
args = [];
424+
}
425+
418426
called = {command, args, options};
419427
callback(null, 'a-result');
420428
};
@@ -431,6 +439,13 @@ describe('DataSource', function() {
431439
it('supports shorthand version (cmd, args)', async () => {
432440
let called = 'not called';
433441
ds.connector.execute = function(command, args, options, callback) {
442+
// copied from loopback-connector/lib/sql.js
443+
if (typeof options === 'function' && callback === undefined) {
444+
// execute(sql, params, callback)
445+
callback = options;
446+
options = {};
447+
}
448+
434449
called = {command, args, options};
435450
callback(null, 'a-result');
436451
};
@@ -444,7 +459,8 @@ describe('DataSource', function() {
444459
});
445460

446461
it('converts multiple callbacks arguments into a promise resolved with an array', async () => {
447-
ds.connector.execute = function(command, args, options, callback) {
462+
ds.connector.execute = function() {
463+
const callback = arguments[arguments.length - 1];
448464
callback(null, 'result1', 'result2');
449465
};
450466
const result = await ds.execute('command');
@@ -460,14 +476,64 @@ describe('DataSource', function() {
460476

461477
// See https://www.npmjs.com/package/loopback-connector-neo4j-graph
462478
const command = 'MATCH (u:User {email: {email}}) RETURN u';
463-
await ds.execute(command, {email: '[email protected]'});
479+
await ds.execute(command, {email: '[email protected]'}, {options: true});
464480
called.should.be.eql({
465481
command,
466482
args: {email: '[email protected]'},
467-
options: {},
483+
options: {options: true},
468484
});
469485
});
470486

487+
it('supports MongoDB version (collection, cmd, args, options)', async () => {
488+
let called = 'not called';
489+
ds.connector.execute = function(...params) {
490+
const callback = params.pop();
491+
called = params;
492+
callback(null, 'a-result');
493+
};
494+
495+
const result = await ds.execute(
496+
'collection',
497+
'command',
498+
['arg1', 'arg2'],
499+
{options: true},
500+
);
501+
502+
result.should.equal('a-result');
503+
called.should.be.eql([
504+
'collection',
505+
'command',
506+
['arg1', 'arg2'],
507+
{options: true},
508+
]);
509+
});
510+
511+
it('supports free-form version (...params)', async () => {
512+
let called = 'not called';
513+
ds.connector.execute = function(...params) {
514+
const callback = params.pop();
515+
called = params;
516+
callback(null, 'a-result');
517+
};
518+
519+
const result = await ds.execute(
520+
'arg1',
521+
'arg2',
522+
'arg3',
523+
'arg4',
524+
{options: true},
525+
);
526+
527+
result.should.equal('a-result');
528+
called.should.be.eql([
529+
'arg1',
530+
'arg2',
531+
'arg3',
532+
'arg4',
533+
{options: true},
534+
]);
535+
});
536+
471537
it('throws NOT_IMPLEMENTED when no connector is provided', () => {
472538
ds.connector = undefined;
473539
return ds.execute('command').should.be.rejectedWith({

0 commit comments

Comments
 (0)