From b1ec3de26c1640caeef361dd529d8b9c06afe623 Mon Sep 17 00:00:00 2001 From: Mike Eldridge Date: Thu, 22 Oct 2015 15:20:01 -0500 Subject: [PATCH 1/2] #10 - transaction support via knex --- package.json | 1 + src/index.js | 19 +++++--- test/create_trx.spec.js | 66 ++++++++++++++++++++++++++++ test/destroy_trx.spec.js | 48 +++++++++++++++++++++ test/update_trx.spec.js | 93 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 220 insertions(+), 7 deletions(-) create mode 100644 test/create_trx.spec.js create mode 100644 test/destroy_trx.spec.js create mode 100644 test/update_trx.spec.js diff --git a/package.json b/package.json index f21779d..843fea6 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,7 @@ "babel-loader": "5.3.2", "bluebird": "2.10.2", "chai": "3.3.0", + "co": "^4.6.0", "co-mocha": "1.1.2", "mocha": "2.3.3", "standard": "5.3.1", diff --git a/src/index.js b/src/index.js index 8131117..e639d7b 100644 --- a/src/index.js +++ b/src/index.js @@ -19,9 +19,10 @@ function getTable (resourceConfig) { return resourceConfig.table || underscore(resourceConfig.name) } -function filterQuery (resourceConfig, params) { +function filterQuery (resourceConfig, params, options) { let table = getTable(resourceConfig) - let query = this.query.select(`${table}.*`).from(table) + let query = options && options.transaction || this.query + query = query.select(`${table}.*`).from(table) params = params || {} params.where = params.where || {} params.orderBy = params.orderBy || params.sort @@ -297,7 +298,8 @@ class DSSqlAdapter { let instance options = options || {} options.with = options.with || [] - return this.query + let query = options && options.transaction || this.query + return query .select('*') .from(getTable(resourceConfig)) .where(resourceConfig.idAttribute, toString(id)) @@ -324,7 +326,8 @@ class DSSqlAdapter { create (resourceConfig, attrs, options) { attrs = DSUtils.removeCircular(DSUtils.omit(attrs, resourceConfig.relationFields || [])) - return this.query(getTable(resourceConfig)) + let query = options && options.transaction || this.query + return query(getTable(resourceConfig)) .insert(attrs, resourceConfig.idAttribute) .then(ids => { if (attrs[resourceConfig.idAttribute]) { @@ -339,7 +342,8 @@ class DSSqlAdapter { update (resourceConfig, id, attrs, options) { attrs = DSUtils.removeCircular(DSUtils.omit(attrs, resourceConfig.relationFields || [])) - return this.query(getTable(resourceConfig)) + let query = options && options.transaction || this.query + return query(getTable(resourceConfig)) .where(resourceConfig.idAttribute, toString(id)) .update(attrs) .then(() => this.find(resourceConfig, id, options)) @@ -360,8 +364,9 @@ class DSSqlAdapter { }) } - destroy (resourceConfig, id) { - return this.query(getTable(resourceConfig)) + destroy (resourceConfig, id, options) { + let query = options && options.transaction || this.query + return query(getTable(resourceConfig)) .where(resourceConfig.idAttribute, toString(id)) .del().then(() => undefined) } diff --git a/test/create_trx.spec.js b/test/create_trx.spec.js new file mode 100644 index 0000000..bd94701 --- /dev/null +++ b/test/create_trx.spec.js @@ -0,0 +1,66 @@ +describe('DSSqlAdapter#create + transaction', function () { + it('commit should persist created user in a sql db', function* () { + var id; + var co = require('co'); + + yield adapter.query.transaction(co.wrap(function * (trx) { + var createUser = yield adapter.create(User, {name: 'Jane'}, {transaction: trx}); + id = createUser.id; + assert.equal(createUser.name, 'Jane'); + assert.isDefined(createUser.id); + + var findUser = yield adapter.find(User, createUser.id, {transaction: trx}); + assert.equal(findUser.name, 'Jane'); + assert.isDefined(findUser.id); + assert.equalObjects(findUser, {id: id, name: 'Jane', age: null, profileId: null}); + + return findUser; + })).then( + function (user) { + assert.isObject(user, 'transaction returned user object'); + assert.equal(user.name, 'Jane'); + assert.isDefined(user.id); + assert.equalObjects(user, {id: id, name: 'Jane', age: null, profileId: null}); + }, + function (err) { + throw new Error('transaction threw exception!'); + } + ); + + try { + var findUser2 = yield adapter.find(User, id); + assert.isObject(findUser2, 'user committed to database'); + } catch(err) { + throw new Error('transaction failed to commit!'); + } + }); + + it('rollback should not persist created user in a sql db', function* () { + var id; + var co = require('co'); + + yield adapter.query.transaction(co.wrap(function * (trx) { + var createUser = yield adapter.create(User, {name: 'John'}, {transaction: trx}); + id = createUser.id; + assert.equal(createUser.name, 'John'); + assert.isDefined(createUser.id); + + var findUser = yield adapter.find(User, createUser.id, {transaction: trx}); + assert.equal(findUser.name, 'John'); + assert.isDefined(findUser.id); + assert.equalObjects(findUser, {id: id, name: 'John', age: null, profileId: null}); + + throw new Error('rollback'); + })).then( + function () { throw new Error('transaction did not throw exception!') }, + function (err) { assert.equal(err.message, 'rollback') } + ); + + try { + var findUser2 = yield adapter.find(User, id); + throw new Error('transaction failed to roll back!'); + } catch(err) { + assert.equal(err.message, 'Not Found!'); + } + }); +}); diff --git a/test/destroy_trx.spec.js b/test/destroy_trx.spec.js new file mode 100644 index 0000000..fd98a0d --- /dev/null +++ b/test/destroy_trx.spec.js @@ -0,0 +1,48 @@ +describe('DSSqlAdapter#destroy + transaction', function () { + it('commit should destroy a user from a Sql db', function* () { + var co = require('co'); + + var createUser = yield adapter.create(User, {name: 'John'}) + var id = createUser.id; + + yield adapter.query.transaction(co.wrap(function * (trx) { + var destroyUser = yield adapter.destroy(User, createUser.id, {transaction: trx}); + assert.isFalse(!!destroyUser); + })); + + try { + var findUser = yield adapter.find(User, id); + throw new Error('Should not have reached here!'); + } catch (err) { + assert.equal(err.message, 'Not Found!'); + } + }); + + it('rollback should not destroy a user from a Sql db', function* () { + var co = require('co'); + + var createUser = yield adapter.create(User, {name: 'John'}) + var id = createUser.id; + + yield adapter.query.transaction(co.wrap(function * (trx) { + var destroyUser = yield adapter.destroy(User, createUser.id, {transaction: trx}); + assert.isFalse(!!destroyUser); + + throw new Error('rollback'); + })).then( + function () { throw new Error('transaction did not throw exception!') }, + function (err) { assert.equal(err.message, 'rollback') } + ); + + try { + var findUser = yield adapter.find(User, id); + assert.isObject(findUser, 'user still exists'); + } catch (err) { + if (err.message == 'Not Found!') { + throw new Error('transaction did not roll back'); + } else { + throw new Error('caught exception trying to locate user'); + } + } + }); +}); diff --git a/test/update_trx.spec.js b/test/update_trx.spec.js new file mode 100644 index 0000000..d63614d --- /dev/null +++ b/test/update_trx.spec.js @@ -0,0 +1,93 @@ +describe('DSSqlAdapter#update + transaction', function () { + it('commit should update a user in a Sql db', function* () { + var co = require('co'); + + var user = yield adapter.create(User, {name: 'John'}) + var id = user.id; + assert.equal(user.name, 'John'); + assert.isDefined(user.id); + + var foundUser = yield adapter.find(User, user.id); + assert.equal(foundUser.name, 'John'); + assert.isDefined(foundUser.id); + assert.equalObjects(foundUser, {id: id, name: 'John', age: null, profileId: null}); + + yield adapter.query.transaction(co.wrap(function * (trx) { + var updatedUser = yield adapter.update(User, foundUser.id, {name: 'Johnny'}, {transaction: trx}); + assert.equal(updatedUser.name, 'Johnny'); + assert.isDefined(updatedUser.id); + assert.equalObjects(updatedUser, {id: id, name: 'Johnny', age: null, profileId: null}); + + var foundUser2 = yield adapter.find(User, updatedUser.id, {transaction: trx}); + assert.equal(foundUser2.name, 'Johnny'); + assert.isDefined(foundUser2.id); + assert.equalObjects(foundUser2, {id: id, name: 'Johnny', age: null, profileId: null}); + + return foundUser2; + })).then( + function (user) { assert.isObject(user, 'transaction returned user object') }, + function (err) { throw new Error('transaction threw exception!') } + ); + + var foundUser3 = yield adapter.find(User, user.id); + assert.equal(foundUser3.name, 'Johnny'); + assert.isDefined(foundUser3.id); + assert.equalObjects(foundUser3, {id: id, name: 'Johnny', age: null, profileId: null}); + + var destroyUser = yield adapter.destroy(User, foundUser3.id); + assert.isFalse(!!destroyUser); + + try { + yield adapter.find(User, id); + throw new Error('Should not have reached here!'); + } catch (err) { + assert.equal(err.message, 'Not Found!'); + } + }); + + it('rollback should not update a user in a Sql db', function* () { + var co = require('co'); + + var user = yield adapter.create(User, {name: 'John'}) + var id = user.id; + assert.equal(user.name, 'John'); + assert.isDefined(user.id); + + var foundUser = yield adapter.find(User, user.id); + assert.equal(foundUser.name, 'John'); + assert.isDefined(foundUser.id); + assert.equalObjects(foundUser, {id: id, name: 'John', age: null, profileId: null}); + + yield adapter.query.transaction(co.wrap(function * (trx) { + var updatedUser = yield adapter.update(User, foundUser.id, {name: 'Johnny'}, {transaction: trx}); + assert.equal(updatedUser.name, 'Johnny'); + assert.isDefined(updatedUser.id); + assert.equalObjects(updatedUser, {id: id, name: 'Johnny', age: null, profileId: null}); + + var foundUser2 = yield adapter.find(User, updatedUser.id, {transaction: trx}); + assert.equal(foundUser2.name, 'Johnny'); + assert.isDefined(foundUser2.id); + assert.equalObjects(foundUser2, {id: id, name: 'Johnny', age: null, profileId: null}); + + throw new Error('rollback'); + })).then( + function () { throw new Error('transaction did not throw exception!') }, + function (err) { assert.equal(err.message, 'rollback') } + ); + + var foundUser3 = yield adapter.find(User, user.id); + assert.equal(foundUser3.name, 'John'); + assert.isDefined(foundUser3.id); + assert.equalObjects(foundUser3, {id: id, name: 'John', age: null, profileId: null}); + + var destroyUser = yield adapter.destroy(User, foundUser3.id); + assert.isFalse(!!destroyUser); + + try { + yield adapter.find(User, id); + throw new Error('Should not have reached here!'); + } catch (err) { + assert.equal(err.message, 'Not Found!'); + } + }); +}); From 49bb7cc17d2e31ba8eb5b310d320c611ff800dd6 Mon Sep 17 00:00:00 2001 From: Mike Eldridge Date: Thu, 22 Oct 2015 22:59:26 -0500 Subject: [PATCH 2/2] #10 - move co into mocha.start.js and tidy up the transaction tests --- mocha.start.js | 3 +- test/create_trx.spec.js | 64 +++++++++-------------------- test/destroy_trx.spec.js | 33 +++++---------- test/update_trx.spec.js | 88 +++++++++------------------------------- 4 files changed, 52 insertions(+), 136 deletions(-) diff --git a/mocha.start.js b/mocha.start.js index 35ad722..af0bffa 100644 --- a/mocha.start.js +++ b/mocha.start.js @@ -38,7 +38,8 @@ var globals = module.exports = { }], TYPES_EXCEPT_FUNCTION: ['string', 123, 123.123, null, undefined, {}, [], true, false], assert: assert, - adapter: undefined + adapter: undefined, + co: require('co') }; var test = new mocha(); diff --git a/test/create_trx.spec.js b/test/create_trx.spec.js index bd94701..7808b79 100644 --- a/test/create_trx.spec.js +++ b/test/create_trx.spec.js @@ -1,64 +1,40 @@ describe('DSSqlAdapter#create + transaction', function () { it('commit should persist created user in a sql db', function* () { var id; - var co = require('co'); yield adapter.query.transaction(co.wrap(function * (trx) { var createUser = yield adapter.create(User, {name: 'Jane'}, {transaction: trx}); id = createUser.id; assert.equal(createUser.name, 'Jane'); assert.isDefined(createUser.id); + })); - var findUser = yield adapter.find(User, createUser.id, {transaction: trx}); - assert.equal(findUser.name, 'Jane'); - assert.isDefined(findUser.id); - assert.equalObjects(findUser, {id: id, name: 'Jane', age: null, profileId: null}); - - return findUser; - })).then( - function (user) { - assert.isObject(user, 'transaction returned user object'); - assert.equal(user.name, 'Jane'); - assert.isDefined(user.id); - assert.equalObjects(user, {id: id, name: 'Jane', age: null, profileId: null}); - }, - function (err) { - throw new Error('transaction threw exception!'); - } - ); - - try { - var findUser2 = yield adapter.find(User, id); - assert.isObject(findUser2, 'user committed to database'); - } catch(err) { - throw new Error('transaction failed to commit!'); - } + var findUser = yield adapter.find(User, id); + assert.isObject(findUser, 'user committed to database'); + assert.equal(findUser.name, 'Jane'); + assert.isDefined(findUser.id); + assert.equalObjects(findUser, {id: id, name: 'Jane', age: null, profileId: null}); }); it('rollback should not persist created user in a sql db', function* () { var id; - var co = require('co'); - - yield adapter.query.transaction(co.wrap(function * (trx) { - var createUser = yield adapter.create(User, {name: 'John'}, {transaction: trx}); - id = createUser.id; - assert.equal(createUser.name, 'John'); - assert.isDefined(createUser.id); - var findUser = yield adapter.find(User, createUser.id, {transaction: trx}); - assert.equal(findUser.name, 'John'); - assert.isDefined(findUser.id); - assert.equalObjects(findUser, {id: id, name: 'John', age: null, profileId: null}); - - throw new Error('rollback'); - })).then( - function () { throw new Error('transaction did not throw exception!') }, - function (err) { assert.equal(err.message, 'rollback') } - ); + try { + yield adapter.query.transaction(co.wrap(function * (trx) { + var createUser = yield adapter.create(User, {name: 'John'}, {transaction: trx}); + id = createUser.id; + assert.equal(createUser.name, 'John'); + assert.isDefined(createUser.id); + + throw new Error('rollback'); + })); + } catch (err) { + assert.equal(err.message, 'rollback'); + } try { - var findUser2 = yield adapter.find(User, id); - throw new Error('transaction failed to roll back!'); + var findUser = yield adapter.find(User, id); + throw new Error('user committed to database'); } catch(err) { assert.equal(err.message, 'Not Found!'); } diff --git a/test/destroy_trx.spec.js b/test/destroy_trx.spec.js index fd98a0d..2d7b47c 100644 --- a/test/destroy_trx.spec.js +++ b/test/destroy_trx.spec.js @@ -1,13 +1,10 @@ describe('DSSqlAdapter#destroy + transaction', function () { it('commit should destroy a user from a Sql db', function* () { - var co = require('co'); - var createUser = yield adapter.create(User, {name: 'John'}) var id = createUser.id; yield adapter.query.transaction(co.wrap(function * (trx) { - var destroyUser = yield adapter.destroy(User, createUser.id, {transaction: trx}); - assert.isFalse(!!destroyUser); + return adapter.destroy(User, id, {transaction: trx}); })); try { @@ -19,30 +16,20 @@ describe('DSSqlAdapter#destroy + transaction', function () { }); it('rollback should not destroy a user from a Sql db', function* () { - var co = require('co'); - var createUser = yield adapter.create(User, {name: 'John'}) var id = createUser.id; - yield adapter.query.transaction(co.wrap(function * (trx) { - var destroyUser = yield adapter.destroy(User, createUser.id, {transaction: trx}); - assert.isFalse(!!destroyUser); - - throw new Error('rollback'); - })).then( - function () { throw new Error('transaction did not throw exception!') }, - function (err) { assert.equal(err.message, 'rollback') } - ); - try { - var findUser = yield adapter.find(User, id); - assert.isObject(findUser, 'user still exists'); + yield adapter.query.transaction(co.wrap(function * (trx) { + yield adapter.destroy(User, createUser.id, {transaction: trx}); + + throw new Error('rollback'); + })); } catch (err) { - if (err.message == 'Not Found!') { - throw new Error('transaction did not roll back'); - } else { - throw new Error('caught exception trying to locate user'); - } + assert.equal(err.message, 'rollback'); } + + var findUser = yield adapter.find(User, id); + assert.isObject(findUser, 'user still exists'); }); }); diff --git a/test/update_trx.spec.js b/test/update_trx.spec.js index d63614d..fe16b0c 100644 --- a/test/update_trx.spec.js +++ b/test/update_trx.spec.js @@ -1,93 +1,45 @@ describe('DSSqlAdapter#update + transaction', function () { it('commit should update a user in a Sql db', function* () { - var co = require('co'); - var user = yield adapter.create(User, {name: 'John'}) var id = user.id; assert.equal(user.name, 'John'); assert.isDefined(user.id); - var foundUser = yield adapter.find(User, user.id); - assert.equal(foundUser.name, 'John'); - assert.isDefined(foundUser.id); - assert.equalObjects(foundUser, {id: id, name: 'John', age: null, profileId: null}); - yield adapter.query.transaction(co.wrap(function * (trx) { - var updatedUser = yield adapter.update(User, foundUser.id, {name: 'Johnny'}, {transaction: trx}); + var updatedUser = yield adapter.update(User, id, {name: 'Johnny'}, {transaction: trx}); assert.equal(updatedUser.name, 'Johnny'); assert.isDefined(updatedUser.id); assert.equalObjects(updatedUser, {id: id, name: 'Johnny', age: null, profileId: null}); + })); - var foundUser2 = yield adapter.find(User, updatedUser.id, {transaction: trx}); - assert.equal(foundUser2.name, 'Johnny'); - assert.isDefined(foundUser2.id); - assert.equalObjects(foundUser2, {id: id, name: 'Johnny', age: null, profileId: null}); - - return foundUser2; - })).then( - function (user) { assert.isObject(user, 'transaction returned user object') }, - function (err) { throw new Error('transaction threw exception!') } - ); - - var foundUser3 = yield adapter.find(User, user.id); - assert.equal(foundUser3.name, 'Johnny'); - assert.isDefined(foundUser3.id); - assert.equalObjects(foundUser3, {id: id, name: 'Johnny', age: null, profileId: null}); - - var destroyUser = yield adapter.destroy(User, foundUser3.id); - assert.isFalse(!!destroyUser); - - try { - yield adapter.find(User, id); - throw new Error('Should not have reached here!'); - } catch (err) { - assert.equal(err.message, 'Not Found!'); - } + var foundUser = yield adapter.find(User, id); + assert.equal(foundUser.name, 'Johnny'); + assert.isDefined(foundUser.id); + assert.equalObjects(foundUser, {id: id, name: 'Johnny', age: null, profileId: null}); }); it('rollback should not update a user in a Sql db', function* () { - var co = require('co'); - var user = yield adapter.create(User, {name: 'John'}) var id = user.id; assert.equal(user.name, 'John'); assert.isDefined(user.id); - var foundUser = yield adapter.find(User, user.id); - assert.equal(foundUser.name, 'John'); - assert.isDefined(foundUser.id); - assert.equalObjects(foundUser, {id: id, name: 'John', age: null, profileId: null}); - - yield adapter.query.transaction(co.wrap(function * (trx) { - var updatedUser = yield adapter.update(User, foundUser.id, {name: 'Johnny'}, {transaction: trx}); - assert.equal(updatedUser.name, 'Johnny'); - assert.isDefined(updatedUser.id); - assert.equalObjects(updatedUser, {id: id, name: 'Johnny', age: null, profileId: null}); - - var foundUser2 = yield adapter.find(User, updatedUser.id, {transaction: trx}); - assert.equal(foundUser2.name, 'Johnny'); - assert.isDefined(foundUser2.id); - assert.equalObjects(foundUser2, {id: id, name: 'Johnny', age: null, profileId: null}); - - throw new Error('rollback'); - })).then( - function () { throw new Error('transaction did not throw exception!') }, - function (err) { assert.equal(err.message, 'rollback') } - ); - - var foundUser3 = yield adapter.find(User, user.id); - assert.equal(foundUser3.name, 'John'); - assert.isDefined(foundUser3.id); - assert.equalObjects(foundUser3, {id: id, name: 'John', age: null, profileId: null}); - - var destroyUser = yield adapter.destroy(User, foundUser3.id); - assert.isFalse(!!destroyUser); - try { - yield adapter.find(User, id); - throw new Error('Should not have reached here!'); + yield adapter.query.transaction(co.wrap(function * (trx) { + var updatedUser = yield adapter.update(User, id, {name: 'Johnny'}, {transaction: trx}); + assert.equal(updatedUser.name, 'Johnny'); + assert.isDefined(updatedUser.id); + assert.equalObjects(updatedUser, {id: id, name: 'Johnny', age: null, profileId: null}); + + throw new Error('rollback'); + })); } catch (err) { - assert.equal(err.message, 'Not Found!'); + assert.equal(err.message, 'rollback'); } + + var foundUser = yield adapter.find(User, id); + assert.equal(foundUser.name, 'John'); + assert.isDefined(foundUser.id); + assert.equalObjects(foundUser, {id: id, name: 'John', age: null, profileId: null}); }); });