Browse Source

Remove bluebird dependency, add public query method, and re-add tests for node-firebird

Timothy J. Warren 2 years ago
parent
commit
0ccd692267

+ 7
- 0
.travis.yml View File

@@ -3,8 +3,15 @@ sudo: false
3 3
 
4 4
 node_js:
5 5
   - "node"
6
+  - "5.6"
7
+  - "5.5"
8
+  - "5.4"
9
+  - "5.3"
10
+  - "5.2"
6 11
   - "5.1"
7 12
   - "5.0"
13
+  - "4.3"
14
+  - "4.2"
8 15
   - "4.1"
9 16
   - "4.0"
10 17
 

+ 8
- 0
CHANGELOG.md View File

@@ -0,0 +1,8 @@
1
+# Changelog
2
+
3
+## 3.2.0
4
+* Added public `query` method for making arbitrary sql calls
5
+* Added back tests for `node-query` adapter. Using this adapter with promises is not currently supported.
6
+
7
+## 3.1.0
8
+* Added support for promises on query execution methods

+ 9
- 0
LICENSE.md View File

@@ -0,0 +1,9 @@
1
+The MIT License (MIT)
2
+
3
+Copyright (c) 2015 Timothy J. Warren
4
+
5
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6
+
7
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8
+
9
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

+ 3
- 1
README.md View File

@@ -7,6 +7,9 @@ A node query builder for various SQL databases, based on [CodeIgniter](http://ww
7 7
 [![Code Climate](https://codeclimate.com/github/timw4mail/node-query/badges/gpa.svg)](https://codeclimate.com/github/timw4mail/node-query)
8 8
 [![Test Coverage](https://codeclimate.com/github/timw4mail/node-query/badges/coverage.svg)](https://codeclimate.com/github/timw4mail/node-query/coverage)
9 9
 
10
+### Features
11
+* Callback and Promise API for making database calls.
12
+
10 13
 ### Supported adapters
11 14
 
12 15
 * mysql
@@ -72,7 +75,6 @@ As of version 2, `where` and `having` type methods parse the values passed to lo
72 75
 
73 76
 * Generated documentation is in the docs/ folder
74 77
 * The API is documented in [API.md](./API.md)
75
-* `tests/query-builder-base.js`	contains a lot of usage examples
76 78
 * The `tests/adapters` folder contains examples of how to set up a connection for the appropriate database library
77 79
 * The documentation generated for the latest dev build is also [Available](https://github.timshomepage.net/node-query/docs/)
78 80
 

+ 15
- 2
lib/QueryBuilder.js View File

@@ -269,9 +269,9 @@ class QueryBuilder {
269 269
 
270 270
 		// Pass the sql and values to the adapter to run on the database
271 271
 		if (callback) {
272
-			return this.adapter.execute(sql, vals, callback);
272
+			return this.query(sql, vals, callback);
273 273
 		} else {
274
-			return this.adapter.execute(sql, vals);
274
+			return this.query(sql, vals);
275 275
 		}
276 276
 	}
277 277
 
@@ -295,6 +295,19 @@ class QueryBuilder {
295 295
 	// ! Miscellaneous Methods
296 296
 	// ----------------------------------------------------------------------------
297 297
 
298
+	/**
299
+	 * Manually make an sql query
300
+	 * Returns a promise if no callback is provided
301
+	 *
302
+	 * @param {string} sql - The sql to execute
303
+	 * @param {array} [params] - The query parameters
304
+	 * @param {function} [callback] - Optional callback
305
+	 * @return {void|Promise} - Returns a promise if no callback is supplied
306
+	 */
307
+	query(/*sql:string, [params]:array, [callback]:function*/) {
308
+		return this.adapter.execute.apply(this.adapter, arguments);
309
+	}
310
+
298 311
 	/**
299 312
 	 * Reset the object state for a new query
300 313
 	 *

+ 2
- 3
lib/adapters/dblite.js View File

@@ -2,7 +2,7 @@
2 2
 
3 3
 let Adapter = require('../Adapter'),
4 4
 	getArgs = require('getargs'),
5
-	Promise = require('bluebird');
5
+	promisify = require('../promisify');
6 6
 
7 7
 module.exports = class dblite extends Adapter {
8 8
 	/**
@@ -15,10 +15,9 @@ module.exports = class dblite extends Adapter {
15 15
 	 */
16 16
 	execute(/*sql, params, callback*/) {
17 17
 		let args = getArgs('sql:string, [params]:array, [callback]:function', arguments);
18
-		let instance = Promise.promisifyAll(this.instance);
19 18
 
20 19
 		if (! args.callback) {
21
-			return instance.queryAsync(args.sql, args.params);
20
+			return promisify(this.instance.query)(args.sql, args.params);
22 21
 		}
23 22
 
24 23
 		return this.instance.query(args.sql, args.params, args.callback);

+ 3
- 4
lib/adapters/mysql.js View File

@@ -2,7 +2,7 @@
2 2
 
3 3
 let Adapter = require('../Adapter'),
4 4
 	getArgs = require('getargs'),
5
-	Promise = require('bluebird');
5
+	promisify = require('../promisify');
6 6
 
7 7
 module.exports = class mysql extends Adapter {
8 8
 	/**
@@ -14,11 +14,10 @@ module.exports = class mysql extends Adapter {
14 14
 	 * @return {void|Promise} - Returns a promise if no callback is provided
15 15
 	 */
16 16
 	execute(sql, params, callback) {
17
-		let args = getArgs('sql:string, [params], [callback]:function', arguments);
18
-		let instance = Promise.promisifyAll(this.instance);
17
+		let args = getArgs('sql:string, [params]:array, [callback]:function', arguments);
19 18
 
20 19
 		if (! args.callback) {
21
-			return instance.queryAsync(args.sql, args.params);
20
+			return promisify(this.instance.query)(args.sql, args.params);
22 21
 		}
23 22
 
24 23
 		return this.instance.query(args.sql, args.params, args.callback);

+ 3
- 4
lib/adapters/mysql2.js View File

@@ -2,7 +2,7 @@
2 2
 
3 3
 let Adapter = require('../Adapter'),
4 4
 	getArgs = require('getargs'),
5
-	Promise = require('bluebird');
5
+	promisify = require('../promisify');
6 6
 
7 7
 module.exports = class mysql2 extends Adapter {
8 8
 	/**
@@ -14,11 +14,10 @@ module.exports = class mysql2 extends Adapter {
14 14
 	 * @return {void|Promise} - Returns a promise if no callback is provided
15 15
 	 */
16 16
 	execute(/*sql, params, callback*/) {
17
-		let args = getArgs('sql:string, [params], [callback]:function', arguments);
18
-		let instance = Promise.promisifyAll(this.instance);
17
+		let args = getArgs('sql:string, [params]:array, [callback]:function', arguments);
19 18
 
20 19
 		if (! args.callback) {
21
-			return instance.executeAsync(args.sql, args.params);
20
+			return promisify(this.instance.execute)(args.sql, args.params);
22 21
 		}
23 22
 
24 23
 		return this.instance.execute(args.sql, args.params, args.callback);

+ 13
- 4
lib/adapters/node-firebird.js View File

@@ -1,8 +1,7 @@
1 1
 'use strict';
2 2
 
3 3
 let Adapter = require('../Adapter'),
4
-	getArgs = require('getargs'),
5
-	Promise = require('bluebird');
4
+	getArgs = require('getargs');
6 5
 
7 6
 module.exports = class nodefirebird extends Adapter {
8 7
 	/**
@@ -15,10 +14,20 @@ module.exports = class nodefirebird extends Adapter {
15 14
 	 */
16 15
 	execute(/*sql, params, callback*/) {
17 16
 		let args = getArgs('sql:string, [params], [callback]:function', arguments);
18
-		let instance = Promise.promisifyAll(this.instance);
19 17
 
20 18
 		if (! args.callback) {
21
-			return instance.queryAsync(args.sql, args.params);
19
+			//return instance.queryAsync(args.sql, args.params);
20
+			if (! args.callback) {
21
+				return new Promise((resolve, reject) => {
22
+					this.instance.query(args.sql, args.params, (err, result) => {
23
+						if (err) {
24
+							return reject(err);
25
+						}
26
+
27
+						return resolve(result);
28
+					});
29
+				});
30
+			}
22 31
 		}
23 32
 
24 33
 		return this.instance.query(args.sql, args.params, args.callback);

+ 10
- 4
lib/adapters/pg.js View File

@@ -1,8 +1,7 @@
1 1
 'use strict';
2 2
 
3 3
 let Adapter = require('../Adapter'),
4
-	getArgs = require('getargs'),
5
-	Promise = require('bluebird');
4
+	getArgs = require('getargs');
6 5
 
7 6
 module.exports = class pg extends Adapter {
8 7
 	/**
@@ -15,7 +14,6 @@ module.exports = class pg extends Adapter {
15 14
 	 */
16 15
 	execute(/*sql, params, callback*/) {
17 16
 		let args = getArgs('sql:string, [params]:array, [callback]:function', arguments);
18
-		let instance = Promise.promisifyAll(this.instance);
19 17
 
20 18
 		// Replace question marks with numbered placeholders, because this adapter is different...
21 19
 		let count = 0;
@@ -25,7 +23,15 @@ module.exports = class pg extends Adapter {
25 23
 		});
26 24
 
27 25
 		if (! args.callback) {
28
-			return instance.queryAsync(args.sql, args.params);
26
+			return new Promise((resolve, reject) => {
27
+				this.instance.query(args.sql, args.params, (err, result) => {
28
+					if (err) {
29
+						return reject(err);
30
+					}
31
+
32
+					return resolve(result);
33
+				});
34
+			});
29 35
 		}
30 36
 
31 37
 		return this.instance.query(args.sql, args.params, args.callback);

+ 4
- 4
lib/drivers/Firebird.js View File

@@ -7,7 +7,7 @@ let helpers = require('../helpers');
7 7
  *
8 8
  * @module drivers/firebird
9 9
  */
10
-module.exports = (function() {
10
+module.exports = (() => {
11 11
 	delete require.cache[require.resolve('../Driver')];
12 12
 	let driver = require('../Driver');
13 13
 
@@ -21,7 +21,7 @@ module.exports = (function() {
21 21
 	 * @param {Number|null} offset - Number of rows to skip
22 22
 	 * @return {String} - Modified SQL statement
23 23
 	 */
24
-	driver.limit = function(origSql, limit, offset) {
24
+	driver.limit = (origSql, limit, offset) => {
25 25
 		let sql = `FIRST ${limit}`;
26 26
 
27 27
 		if (helpers.isNumber(offset))
@@ -38,9 +38,9 @@ module.exports = (function() {
38 38
 	 * @return {void}
39 39
 	 * @throws {Error}
40 40
 	 */
41
-	driver.insertBatch = function() {
41
+	driver.insertBatch = () => {
42 42
 		throw new Error('Not Implemented');
43 43
 	};
44 44
 
45 45
 	return driver;
46
-}());
46
+})();

+ 3
- 3
lib/drivers/Mysql.js View File

@@ -5,7 +5,7 @@
5 5
  *
6 6
  * @module drivers/mysql
7 7
  */
8
-module.exports = (function() {
8
+module.exports = (() => {
9 9
 	delete require.cache[require.resolve('../Driver')];
10 10
 	let driver = require('../Driver'),
11 11
 		helpers = require('../helpers');
@@ -21,7 +21,7 @@ module.exports = (function() {
21 21
 	 * @param {Number|null} offset - Number of rows to skip
22 22
 	 * @return {String} - Modified SQL statement
23 23
 	 */
24
-	driver.limit = function(sql, limit, offset) {
24
+	driver.limit = (sql, limit, offset) => {
25 25
 		if (! helpers.isNumber(offset)) {
26 26
 			return sql += ` LIMIT ${limit}`;
27 27
 		}
@@ -31,4 +31,4 @@ module.exports = (function() {
31 31
 
32 32
 	return driver;
33 33
 
34
-}());
34
+})();

+ 2
- 2
lib/drivers/Pg.js View File

@@ -5,9 +5,9 @@
5 5
  *
6 6
  * @module drivers/pg
7 7
  */
8
-module.exports = (function() {
8
+module.exports = (() => {
9 9
 	delete require.cache[require.resolve('../Driver')];
10 10
 	let driver = require('../Driver');
11 11
 
12 12
 	return driver;
13
-}());
13
+})();

+ 26
- 0
lib/promisify.js View File

@@ -0,0 +1,26 @@
1
+'use strict';
2
+
3
+/*eslint-disable prefer-arrow-callback*/
4
+/**
5
+ * Function to convert a callback function into a promise
6
+ *
7
+ * @see http://eddmann.com/posts/promisifying-error-first-asynchronous-callbacks-in-javascript/
8
+ * @example promisify(fs.readFile)('hello.txt', 'utf8')
9
+ *	.then(console.log)
10
+ *	.catch(console.error)
11
+ * @param	{Function} fn - the callback function to convert
12
+ * @return {Promise} - the new promise
13
+ */
14
+function promisify(fn) {
15
+	return function () {
16
+		let args = [].slice.call(arguments);
17
+		return new Promise(function(resolve, reject) {
18
+			fn.apply(undefined, args.concat((error, value) => {
19
+				return error ? reject(error) : resolve(value);
20
+			}));
21
+		});
22
+	};
23
+}
24
+
25
+module.exports = promisify;
26
+/*eslint-enable prefer-arrow-callback*/

+ 1
- 2
package.json View File

@@ -1,6 +1,6 @@
1 1
 {
2 2
   "name": "ci-node-query",
3
-  "version": "3.1.0",
3
+  "version": "3.2.0",
4 4
   "description": "A query builder for node based on the one in CodeIgniter",
5 5
   "author": "Timothy J Warren <tim@timshomepage.net>",
6 6
   "engines": {
@@ -36,7 +36,6 @@
36 36
   },
37 37
   "main": "lib/NodeQuery.js",
38 38
   "dependencies": {
39
-    "bluebird": "^3.1.4",
40 39
     "dblite": "~0.7.6",
41 40
     "getargs": "~0.0.8",
42 41
     "mysql": "~2.10.2",

+ 1
- 1
test/adapters/dblite_test.js View File

@@ -93,6 +93,6 @@ if (connection) {
93 93
 				.get();
94 94
 
95 95
 			expect(promise).to.be.fulfilled;
96
-		});
96
+		});
97 97
 	});
98 98
 }

+ 26
- 24
test/adapters/mysql2_test.js View File

@@ -23,30 +23,32 @@ let connection = mysql2.createConnection(config.conn);
23 23
 // Set up the query builder object
24 24
 let nodeQuery = reload('../../lib/NodeQuery');
25 25
 let qb = nodeQuery.init('mysql', connection, adapterName);
26
-
27
-suite('Mysql2 adapter tests -', () => {
28
-	require('./mysql-base')(qb, nodeQuery, expect, testRunner, promiseTestRunner);
29
-
30
-	test('Test Insert Batch', done => {
31
-		let data = [
32
-			{
33
-				id: 5441,
34
-				key: 3,
35
-				val: new Buffer('7'),
36
-			}, {
37
-				id: 891,
38
-				key: 34,
39
-				val: new Buffer('10 o\'clock'),
40
-			}, {
41
-				id: 481,
42
-				key: 403,
43
-				val: new Buffer('97'),
44
-			},
45
-		];
46
-
47
-		qb.insertBatch('create_test', data, (err, rows) => {
48
-			expect(err).is.not.ok;
49
-			return done();
26
+qb.query(qb.driver.truncate('create_test')).then(() => {
27
+	suite('Mysql2 adapter tests -', () => {
28
+
29
+		require('./mysql-base')(qb, nodeQuery, expect, testRunner, promiseTestRunner);
30
+
31
+		test('Test Insert Batch', done => {
32
+			let data = [
33
+				{
34
+					id: 5441,
35
+					key: 3,
36
+					val: new Buffer('7'),
37
+				}, {
38
+					id: 891,
39
+					key: 34,
40
+					val: new Buffer('10 o\'clock'),
41
+				}, {
42
+					id: 481,
43
+					key: 403,
44
+					val: new Buffer('97'),
45
+				},
46
+			];
47
+
48
+			qb.insertBatch('create_test', data, (err, rows) => {
49
+				expect(err).is.not.ok;
50
+				return done();
51
+			});
50 52
 		});
51 53
 	});
52 54
 });

+ 26
- 23
test/adapters/mysql_test.js View File

@@ -24,29 +24,32 @@ let connection = mysql.createConnection(config.conn);
24 24
 let nodeQuery = reload('../../lib/NodeQuery');
25 25
 let qb = nodeQuery.init('mysql', connection);
26 26
 
27
-suite('Mysql adapter tests -', () => {
28
-	require('./mysql-base')(qb, nodeQuery, expect, testRunner, promiseTestRunner);
29
-
30
-	test('Test Insert Batch', done => {
31
-		let data = [
32
-			{
33
-				id: 544,
34
-				key: 3,
35
-				val: new Buffer('7'),
36
-			}, {
37
-				id: 89,
38
-				key: 34,
39
-				val: new Buffer('10 o\'clock'),
40
-			}, {
41
-				id: 48,
42
-				key: 403,
43
-				val: new Buffer('97'),
44
-			},
45
-		];
46
-
47
-		qb.insertBatch('create_test', data, (err, rows) => {
48
-			expect(err).is.not.ok;
49
-			return done();
27
+qb.query(qb.driver.truncate('create_test')).then(() => {
28
+	suite('Mysql adapter tests -', () => {
29
+
30
+		require('./mysql-base')(qb, nodeQuery, expect, testRunner, promiseTestRunner);
31
+
32
+		test('Test Insert Batch', done => {
33
+			let data = [
34
+				{
35
+					id: 544,
36
+					key: 3,
37
+					val: new Buffer('7'),
38
+				}, {
39
+					id: 89,
40
+					key: 34,
41
+					val: new Buffer('10 o\'clock'),
42
+				}, {
43
+					id: 48,
44
+					key: 403,
45
+					val: new Buffer('97'),
46
+				},
47
+			];
48
+
49
+			qb.insertBatch('create_test', data, (err, rows) => {
50
+				expect(err).is.not.ok;
51
+				return done();
52
+			});
50 53
 		});
51 54
 	});
52 55
 });

test/adapters/node-firebird.js → test/adapters/node-firebird_test.js View File

@@ -4,6 +4,7 @@
4 4
 const path = require('path');
5 5
 const reload = require('require-reload')(require);
6 6
 const testBase = reload('../base');
7
+const promisify = require('../../lib/promisify');
7 8
 const expect = reload('chai').expect;
8 9
 const testRunner = testBase.testRunner;
9 10
 const promiseTestRunner = testBase.promiseTestRunner;
@@ -22,19 +23,11 @@ if (process.env.CI || process.env.JENKINS_HOME) {
22 23
 	return;
23 24
 }
24 25
 
25
-suite('Firebird adapter tests -', () => {
26
-	// Set up the query builder object
27
-	suiteSetup('Database connection', connected => {
28
-		Firebird.attach(config.conn, (err, db) => {
29
-			qb = nodeQuery.init('firebird', db, adapterName);
30
-			return connected(err);
31
-		});
32
-	});
33
-	testRunner(qb, (err, done) => {
34
-		expect(err).is.not.ok;
35
-		done();
36
-	});
37
-	suite('Adapter-specific tests', () => {
26
+// Promisifying the connection seems to be the only way to get
27
+// this test suite to run consistently.
28
+promisify(Firebird.attach)(config.conn).then(db => {
29
+	qb = nodeQuery.init('firebird', db, adapterName);
30
+	suite('Firebird adapter tests -', () => {
38 31
 		test('nodeQuery.getQuery = nodeQuery.init', () => {
39 32
 			expect(nodeQuery.getQuery())
40 33
 				.to.be.deep.equal(qb);
@@ -44,8 +37,23 @@ suite('Firebird adapter tests -', () => {
44 37
 				qb.driver.insertBatch('create_test', []);
45 38
 			}).to.throw(Error, "Not Implemented");
46 39
 		});
40
+		/*---------------------------------------------------------------------------
41
+		Callback Tests
42
+		---------------------------------------------------------------------------*/
43
+		testRunner(qb, (err, done) => {
44
+			expect(err).is.not.ok;
45
+			done();
46
+		});
47
+		/*---------------------------------------------------------------------------
48
+		Promise Tests
49
+		---------------------------------------------------------------------------*/
50
+		/*qb.adapter.execute(qb.driver.truncate('create_test')).then(() => {
51
+			promiseTestRunner(qb);
52
+		});*/
53
+		suiteTeardown(() => {
54
+			qb.end();
55
+		});
47 56
 	});
48
-	suiteTeardown(() => {
49
-		qb.end();
50
-	});
51
-});
57
+}).catch(err => {
58
+	throw new Error(err);
59
+});

+ 3
- 5
test/adapters/pg_test.js View File

@@ -17,17 +17,15 @@ let config = reload(configFile)[adapterName];
17 17
 // Set up the connection
18 18
 let pg = reload(adapterName);
19 19
 let connection = new pg.Client(config.conn);
20
-connection.connect(err => {
21
-	if (err) {
22
-		throw new Error(err);
23
-	}
24
-});
25 20
 
26 21
 // Set up the query builder object
27 22
 let nodeQuery = reload('../../lib/NodeQuery');
28 23
 let qb = nodeQuery.init('pg', connection);
29 24
 
30 25
 suite('Pg adapter tests -', () => {
26
+	suiteSetup(done => {
27
+		return connection.connect(done);
28
+	});
31 29
 	test('nodeQuery.getQuery = nodeQuery.init', () => {
32 30
 		expect(nodeQuery.getQuery())
33 31
 			.to.be.deep.equal(qb);

+ 1
- 2
test/base.js View File

@@ -3,10 +3,9 @@
3 3
 const chai = require('chai');
4 4
 const chaiAsPromised = require('chai-as-promised');
5 5
 chai.use(chaiAsPromised);
6
-const expect = chai.expect;
7 6
 
8 7
 module.exports = {
9
-	expect: expect,
8
+	expect: chai.expect,
10 9
 	tests: require('./base/tests'),
11 10
 	testRunner: require('./base/adapterCallbackTestRunner'),
12 11
 	promiseTestRunner: require('./base/adapterPromiseTestRunner'),

+ 1
- 1
test/query-parser_test.js View File

@@ -42,7 +42,7 @@ let mixedSet = function mixedSet(/* $letName, $valType, $key, [$val] */) {
42 42
 	return state[args.$letName];
43 43
 };
44 44
 
45
-let whereMock = function() {
45
+let whereMock = function () {
46 46
 	let args = getArgs('key:string|object, [val]', arguments);
47 47
 
48 48
 	state.whereMap = [];