From c884c62b3be3ba30cc85167a8ad278e9ed0ecdf6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miroslav=20Bajto=C5=A1?= Date: Mon, 3 Aug 2020 14:23:50 +0200 Subject: [PATCH] Add more `dataSource.execute()` flavors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implement support for the following variants: - `execute(collection, command, ...params, options)` (MongoDB) - `execute(...params)` (forward-compatibility & other databases) Signed-off-by: Miroslav Bajtoš --- lib/datasource.js | 15 ++------- test/datasource.test.js | 72 +++++++++++++++++++++++++++++++++++++++-- 2 files changed, 72 insertions(+), 15 deletions(-) diff --git a/lib/datasource.js b/lib/datasource.js index 82b09cee..13c6a714 100644 --- a/lib/datasource.js +++ b/lib/datasource.js @@ -2690,19 +2690,10 @@ DataSource.prototype.ping = function(cb) { * Execute an arbitrary command. The commands are connector specific, * please refer to the documentation of your connector for more details. * - * @param command String|Object The command to execute, e.g. an SQL query. - * @param [args] Array Parameters values to set in the command. - * @param [options] Object Additional options, e.g. the transaction to use. + * @param [...params] Array The command and its arguments, e.g. an SQL query. * @returns Promise A promise of the result */ -DataSource.prototype.execute = function(command, args = [], options = {}) { - assert(typeof command === 'string' || typeof command === 'object', - '"command" must be a string or an object.'); - assert(typeof args === 'object', - '"args" must be an object, an array or undefined.'); - assert(typeof options === 'object', - '"options" must be an object or undefined.'); - +DataSource.prototype.execute = function(...params) { if (!this.connector) { return Promise.reject(errorNotImplemented( `DataSource "${this.name}" is missing a connector to execute the command.`, @@ -2717,7 +2708,7 @@ DataSource.prototype.execute = function(command, args = [], options = {}) { } return new Promise((resolve, reject) => { - this.connector.execute(command, args, options, onExecuted); + this.connector.execute(...params, onExecuted); function onExecuted(err, result) { if (err) return reject(err); if (arguments.length > 2) { diff --git a/test/datasource.test.js b/test/datasource.test.js index b3127d02..e7a50f37 100644 --- a/test/datasource.test.js +++ b/test/datasource.test.js @@ -415,6 +415,14 @@ describe('DataSource', function() { it('supports shorthand version (cmd)', async () => { let called = 'not called'; ds.connector.execute = function(command, args, options, callback) { + // copied from loopback-connector/lib/sql.js + if (typeof args === 'function' && options === undefined && callback === undefined) { + // execute(sql, callback) + options = {}; + callback = args; + args = []; + } + called = {command, args, options}; callback(null, 'a-result'); }; @@ -431,6 +439,13 @@ describe('DataSource', function() { it('supports shorthand version (cmd, args)', async () => { let called = 'not called'; ds.connector.execute = function(command, args, options, callback) { + // copied from loopback-connector/lib/sql.js + if (typeof options === 'function' && callback === undefined) { + // execute(sql, params, callback) + callback = options; + options = {}; + } + called = {command, args, options}; callback(null, 'a-result'); }; @@ -444,7 +459,8 @@ describe('DataSource', function() { }); it('converts multiple callbacks arguments into a promise resolved with an array', async () => { - ds.connector.execute = function(command, args, options, callback) { + ds.connector.execute = function() { + const callback = arguments[arguments.length - 1]; callback(null, 'result1', 'result2'); }; const result = await ds.execute('command'); @@ -460,14 +476,64 @@ describe('DataSource', function() { // See https://www.npmjs.com/package/loopback-connector-neo4j-graph const command = 'MATCH (u:User {email: {email}}) RETURN u'; - await ds.execute(command, {email: 'alice@example.com'}); + await ds.execute(command, {email: 'alice@example.com'}, {options: true}); called.should.be.eql({ command, args: {email: 'alice@example.com'}, - options: {}, + options: {options: true}, }); }); + it('supports MongoDB version (collection, cmd, args, options)', async () => { + let called = 'not called'; + ds.connector.execute = function(...params) { + const callback = params.pop(); + called = params; + callback(null, 'a-result'); + }; + + const result = await ds.execute( + 'collection', + 'command', + ['arg1', 'arg2'], + {options: true}, + ); + + result.should.equal('a-result'); + called.should.be.eql([ + 'collection', + 'command', + ['arg1', 'arg2'], + {options: true}, + ]); + }); + + it('supports free-form version (...params)', async () => { + let called = 'not called'; + ds.connector.execute = function(...params) { + const callback = params.pop(); + called = params; + callback(null, 'a-result'); + }; + + const result = await ds.execute( + 'arg1', + 'arg2', + 'arg3', + 'arg4', + {options: true}, + ); + + result.should.equal('a-result'); + called.should.be.eql([ + 'arg1', + 'arg2', + 'arg3', + 'arg4', + {options: true}, + ]); + }); + it('throws NOT_IMPLEMENTED when no connector is provided', () => { ds.connector = undefined; return ds.execute('command').should.be.rejectedWith({