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

This commit is contained in:
Timothy Warren 2016-02-12 11:40:21 -05:00
parent 383d632bb0
commit 0ccd692267
22 changed files with 187 additions and 101 deletions

View File

@ -3,8 +3,15 @@ sudo: false
node_js:
- "node"
- "5.6"
- "5.5"
- "5.4"
- "5.3"
- "5.2"
- "5.1"
- "5.0"
- "4.3"
- "4.2"
- "4.1"
- "4.0"

8
CHANGELOG.md Normal file
View File

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

9
LICENSE.md Normal file
View File

@ -0,0 +1,9 @@
The MIT License (MIT)
Copyright (c) 2015 Timothy J. Warren
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:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
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.

View File

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

View File

@ -269,9 +269,9 @@ class QueryBuilder {
// Pass the sql and values to the adapter to run on the database
if (callback) {
return this.adapter.execute(sql, vals, callback);
return this.query(sql, vals, callback);
} else {
return this.adapter.execute(sql, vals);
return this.query(sql, vals);
}
}
@ -295,6 +295,19 @@ class QueryBuilder {
// ! Miscellaneous Methods
// ----------------------------------------------------------------------------
/**
* Manually make an sql query
* Returns a promise if no callback is provided
*
* @param {string} sql - The sql to execute
* @param {array} [params] - The query parameters
* @param {function} [callback] - Optional callback
* @return {void|Promise} - Returns a promise if no callback is supplied
*/
query(/*sql:string, [params]:array, [callback]:function*/) {
return this.adapter.execute.apply(this.adapter, arguments);
}
/**
* Reset the object state for a new query
*

View File

@ -2,7 +2,7 @@
let Adapter = require('../Adapter'),
getArgs = require('getargs'),
Promise = require('bluebird');
promisify = require('../promisify');
module.exports = class dblite extends Adapter {
/**
@ -15,10 +15,9 @@ module.exports = class dblite extends Adapter {
*/
execute(/*sql, params, callback*/) {
let args = getArgs('sql:string, [params]:array, [callback]:function', arguments);
let instance = Promise.promisifyAll(this.instance);
if (! args.callback) {
return instance.queryAsync(args.sql, args.params);
return promisify(this.instance.query)(args.sql, args.params);
}
return this.instance.query(args.sql, args.params, args.callback);

View File

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

View File

@ -2,7 +2,7 @@
let Adapter = require('../Adapter'),
getArgs = require('getargs'),
Promise = require('bluebird');
promisify = require('../promisify');
module.exports = class mysql2 extends Adapter {
/**
@ -14,11 +14,10 @@ module.exports = class mysql2 extends Adapter {
* @return {void|Promise} - Returns a promise if no callback is provided
*/
execute(/*sql, params, callback*/) {
let args = getArgs('sql:string, [params], [callback]:function', arguments);
let instance = Promise.promisifyAll(this.instance);
let args = getArgs('sql:string, [params]:array, [callback]:function', arguments);
if (! args.callback) {
return instance.executeAsync(args.sql, args.params);
return promisify(this.instance.execute)(args.sql, args.params);
}
return this.instance.execute(args.sql, args.params, args.callback);

View File

@ -1,8 +1,7 @@
'use strict';
let Adapter = require('../Adapter'),
getArgs = require('getargs'),
Promise = require('bluebird');
getArgs = require('getargs');
module.exports = class nodefirebird extends Adapter {
/**
@ -15,10 +14,20 @@ module.exports = class nodefirebird extends Adapter {
*/
execute(/*sql, params, callback*/) {
let args = getArgs('sql:string, [params], [callback]:function', arguments);
let instance = Promise.promisifyAll(this.instance);
if (! args.callback) {
return instance.queryAsync(args.sql, args.params);
//return instance.queryAsync(args.sql, args.params);
if (! args.callback) {
return new Promise((resolve, reject) => {
this.instance.query(args.sql, args.params, (err, result) => {
if (err) {
return reject(err);
}
return resolve(result);
});
});
}
}
return this.instance.query(args.sql, args.params, args.callback);

View File

@ -1,8 +1,7 @@
'use strict';
let Adapter = require('../Adapter'),
getArgs = require('getargs'),
Promise = require('bluebird');
getArgs = require('getargs');
module.exports = class pg extends Adapter {
/**
@ -15,7 +14,6 @@ module.exports = class pg extends Adapter {
*/
execute(/*sql, params, callback*/) {
let args = getArgs('sql:string, [params]:array, [callback]:function', arguments);
let instance = Promise.promisifyAll(this.instance);
// Replace question marks with numbered placeholders, because this adapter is different...
let count = 0;
@ -25,7 +23,15 @@ module.exports = class pg extends Adapter {
});
if (! args.callback) {
return instance.queryAsync(args.sql, args.params);
return new Promise((resolve, reject) => {
this.instance.query(args.sql, args.params, (err, result) => {
if (err) {
return reject(err);
}
return resolve(result);
});
});
}
return this.instance.query(args.sql, args.params, args.callback);

View File

@ -7,7 +7,7 @@ let helpers = require('../helpers');
*
* @module drivers/firebird
*/
module.exports = (function() {
module.exports = (() => {
delete require.cache[require.resolve('../Driver')];
let driver = require('../Driver');
@ -21,7 +21,7 @@ module.exports = (function() {
* @param {Number|null} offset - Number of rows to skip
* @return {String} - Modified SQL statement
*/
driver.limit = function(origSql, limit, offset) {
driver.limit = (origSql, limit, offset) => {
let sql = `FIRST ${limit}`;
if (helpers.isNumber(offset))
@ -38,9 +38,9 @@ module.exports = (function() {
* @return {void}
* @throws {Error}
*/
driver.insertBatch = function() {
driver.insertBatch = () => {
throw new Error('Not Implemented');
};
return driver;
}());
})();

View File

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

View File

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

26
lib/promisify.js Normal file
View File

@ -0,0 +1,26 @@
'use strict';
/*eslint-disable prefer-arrow-callback*/
/**
* Function to convert a callback function into a promise
*
* @see http://eddmann.com/posts/promisifying-error-first-asynchronous-callbacks-in-javascript/
* @example promisify(fs.readFile)('hello.txt', 'utf8')
* .then(console.log)
* .catch(console.error)
* @param {Function} fn - the callback function to convert
* @return {Promise} - the new promise
*/
function promisify(fn) {
return function () {
let args = [].slice.call(arguments);
return new Promise(function(resolve, reject) {
fn.apply(undefined, args.concat((error, value) => {
return error ? reject(error) : resolve(value);
}));
});
};
}
module.exports = promisify;
/*eslint-enable prefer-arrow-callback*/

View File

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

View File

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

View File

@ -23,30 +23,32 @@ let connection = mysql2.createConnection(config.conn);
// Set up the query builder object
let nodeQuery = reload('../../lib/NodeQuery');
let qb = nodeQuery.init('mysql', connection, adapterName);
qb.query(qb.driver.truncate('create_test')).then(() => {
suite('Mysql2 adapter tests -', () => {
suite('Mysql2 adapter tests -', () => {
require('./mysql-base')(qb, nodeQuery, expect, testRunner, promiseTestRunner);
require('./mysql-base')(qb, nodeQuery, expect, testRunner, promiseTestRunner);
test('Test Insert Batch', done => {
let data = [
{
id: 5441,
key: 3,
val: new Buffer('7'),
}, {
id: 891,
key: 34,
val: new Buffer('10 o\'clock'),
}, {
id: 481,
key: 403,
val: new Buffer('97'),
},
];
test('Test Insert Batch', done => {
let data = [
{
id: 5441,
key: 3,
val: new Buffer('7'),
}, {
id: 891,
key: 34,
val: new Buffer('10 o\'clock'),
}, {
id: 481,
key: 403,
val: new Buffer('97'),
},
];
qb.insertBatch('create_test', data, (err, rows) => {
expect(err).is.not.ok;
return done();
qb.insertBatch('create_test', data, (err, rows) => {
expect(err).is.not.ok;
return done();
});
});
});
});

View File

@ -24,29 +24,32 @@ let connection = mysql.createConnection(config.conn);
let nodeQuery = reload('../../lib/NodeQuery');
let qb = nodeQuery.init('mysql', connection);
suite('Mysql adapter tests -', () => {
require('./mysql-base')(qb, nodeQuery, expect, testRunner, promiseTestRunner);
qb.query(qb.driver.truncate('create_test')).then(() => {
suite('Mysql adapter tests -', () => {
test('Test Insert Batch', done => {
let data = [
{
id: 544,
key: 3,
val: new Buffer('7'),
}, {
id: 89,
key: 34,
val: new Buffer('10 o\'clock'),
}, {
id: 48,
key: 403,
val: new Buffer('97'),
},
];
require('./mysql-base')(qb, nodeQuery, expect, testRunner, promiseTestRunner);
qb.insertBatch('create_test', data, (err, rows) => {
expect(err).is.not.ok;
return done();
test('Test Insert Batch', done => {
let data = [
{
id: 544,
key: 3,
val: new Buffer('7'),
}, {
id: 89,
key: 34,
val: new Buffer('10 o\'clock'),
}, {
id: 48,
key: 403,
val: new Buffer('97'),
},
];
qb.insertBatch('create_test', data, (err, rows) => {
expect(err).is.not.ok;
return done();
});
});
});
});

View File

@ -4,6 +4,7 @@
const path = require('path');
const reload = require('require-reload')(require);
const testBase = reload('../base');
const promisify = require('../../lib/promisify');
const expect = reload('chai').expect;
const testRunner = testBase.testRunner;
const promiseTestRunner = testBase.promiseTestRunner;
@ -22,19 +23,11 @@ if (process.env.CI || process.env.JENKINS_HOME) {
return;
}
suite('Firebird adapter tests -', () => {
// Set up the query builder object
suiteSetup('Database connection', connected => {
Firebird.attach(config.conn, (err, db) => {
qb = nodeQuery.init('firebird', db, adapterName);
return connected(err);
});
});
testRunner(qb, (err, done) => {
expect(err).is.not.ok;
done();
});
suite('Adapter-specific tests', () => {
// Promisifying the connection seems to be the only way to get
// this test suite to run consistently.
promisify(Firebird.attach)(config.conn).then(db => {
qb = nodeQuery.init('firebird', db, adapterName);
suite('Firebird adapter tests -', () => {
test('nodeQuery.getQuery = nodeQuery.init', () => {
expect(nodeQuery.getQuery())
.to.be.deep.equal(qb);
@ -44,8 +37,23 @@ suite('Firebird adapter tests -', () => {
qb.driver.insertBatch('create_test', []);
}).to.throw(Error, "Not Implemented");
});
/*---------------------------------------------------------------------------
Callback Tests
---------------------------------------------------------------------------*/
testRunner(qb, (err, done) => {
expect(err).is.not.ok;
done();
});
/*---------------------------------------------------------------------------
Promise Tests
---------------------------------------------------------------------------*/
/*qb.adapter.execute(qb.driver.truncate('create_test')).then(() => {
promiseTestRunner(qb);
});*/
suiteTeardown(() => {
qb.end();
});
});
suiteTeardown(() => {
qb.end();
});
});
}).catch(err => {
throw new Error(err);
});

View File

@ -17,17 +17,15 @@ let config = reload(configFile)[adapterName];
// Set up the connection
let pg = reload(adapterName);
let connection = new pg.Client(config.conn);
connection.connect(err => {
if (err) {
throw new Error(err);
}
});
// Set up the query builder object
let nodeQuery = reload('../../lib/NodeQuery');
let qb = nodeQuery.init('pg', connection);
suite('Pg adapter tests -', () => {
suiteSetup(done => {
return connection.connect(done);
});
test('nodeQuery.getQuery = nodeQuery.init', () => {
expect(nodeQuery.getQuery())
.to.be.deep.equal(qb);

View File

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

View File

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