Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Executing services methods using transaction, implementation of issue #91 #113

Merged
50 changes: 50 additions & 0 deletions src/hooks.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
const debug = require('debug')('feathers-knex-transaction');

const start = (options) => {
const { dbServiceName } = options;
debug('started transaction system with %s service', dbServiceName);
return (hook) =>
new Promise(resolve =>
hook.app.get(dbServiceName).transaction(trx => {
const id = Date.now();
hook.params.transaction = {
trx,
id
};
debug('started a new transaction %s', id);
return resolve(hook);
})
);
};

const end = (options) => {
return (hook) => {
if (hook.params.transaction) {
const { trx, id } = hook.params.transaction;
return trx.commit()
.then(() => debug('finished transaction %s with success', id))
.then(hook);
}
return hook;
};
};

const rollback = (options) => {
return (hook) => {
if (hook.params.transaction) {
const { trx, id } = hook.params.transaction;
return trx.rollback()
.then(() => debug('rolling back transaction %s', id))
.then(hook);
}
return hook;
};
};

export default {
transaction: {
start,
end,
rollback
}
};
29 changes: 18 additions & 11 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import filter from 'feathers-query-filters';
import isPlainObject from 'is-plain-object';
import { errors } from 'feathers-errors';
import errorHandler from './error-handler';
import hooks from './hooks';

const debug = require('debug')('feathers-knex');

Expand Down Expand Up @@ -45,7 +46,12 @@ class Service {

// NOTE (EK): We need this method so that we return a new query
// instance each time, otherwise it will reuse the same query.
db () {
db (params = {}) {
if (params.transaction) {
const { trx, id } = params.transaction;
debug('ran %s with transaction %s', this.table, id);
return trx(this.table);
}
return this.knex(this.table);
}

Expand Down Expand Up @@ -99,13 +105,13 @@ class Service {
});
}

createQuery (paramsQuery = {}) {
const { filters, query } = filter(paramsQuery);
let q = this.db().select([`${this.table}.*`]);
createQuery (params = {}) {
const { filters, query } = filter(params.query || {});
let q = this.db(params).select([`${this.table}.*`]);

// $select uses a specific find syntax, so it has to come first.
if (filters.$select) {
q = this.db().select(...filters.$select.concat(`${this.table}.${this.id}`));
q = this.db(params).select(...filters.$select.concat(`${this.table}.${this.id}`));
}

// build up the knex query out of the query params
Expand All @@ -123,7 +129,7 @@ class Service {

_find (params, count, getFilter = filter) {
const { filters, query } = getFilter(params.query || {});
const q = params.knex || this.createQuery(params.query);
const q = params.knex || this.createQuery(params);

// Handle $limit
if (filters.$limit) {
Expand Down Expand Up @@ -158,7 +164,7 @@ class Service {
}

if (count) {
let countQuery = this.db().count(`${this.id} as total`);
let countQuery = this.db(params).count(`${this.id} as total`);

this.knexify(countQuery, query);

Expand Down Expand Up @@ -201,7 +207,7 @@ class Service {
}

_create (data, params) {
return this.db().insert(data, this.id).then(rows => {
return this.db(params).insert(data, this.id).then(rows => {
const id = typeof data[this.id] !== 'undefined' ? data[this.id] : rows[0];
return this._get(id, params);
}).catch(errorHandler);
Expand Down Expand Up @@ -230,7 +236,7 @@ class Service {
query[this.id] = id;
}

let q = this.db();
let q = this.db(params);

this.knexify(q, query);

Expand Down Expand Up @@ -286,7 +292,7 @@ class Service {
// NOTE (EK): Delete id field so we don't update it
delete newObject[this.id];

return this.db().where(this.id, id).update(newObject).then(() => {
return this.db(params).where(this.id, id).update(newObject).then(() => {
// NOTE (EK): Restore the id field so we can return it to the client
newObject[this.id] = id;
return newObject;
Expand All @@ -305,7 +311,7 @@ class Service {

return this._find(params).then(page => {
const items = page.data;
const query = this.db();
const query = this.db(params);

this.knexify(query, params.query);

Expand All @@ -328,4 +334,5 @@ export default function init (options) {
return new Service(options);
}

init.hooks = hooks;
init.Service = Service;