Skip to content

Commit

Permalink
Added hooks to support mongo-db transaction
Browse files Browse the repository at this point in the history
  • Loading branch information
jaiyashree committed Nov 29, 2018
1 parent e23ecf1 commit 68e0525
Show file tree
Hide file tree
Showing 3 changed files with 160 additions and 0 deletions.
65 changes: 65 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,71 @@ students.find({ query, collation }).then( ... );
For more information on MongoDB's collation feature, visit the [collation reference page](https://docs.mongodb.com/manual/reference/collation/).


## Mongo-DB Transaction

This adapter includes support to enable database transaction to rollback the persisted records for any error occured for a api call. This requires [Mongo-DB v4.x](https://docs.mongodb.com/manual/) installed.

Start working with transactin enabled by adding the following lines in `app.hooks.js`.

```js
const TransactionManager = require('feathers-mongoose').TransactionManager;
const isTransactionEnable = process.env.TRANSACTION_ENABLE || false;
const skipPath = ['login'];

let moduleExports = {
before: {
// !<DEFAULT> code: before
all: [],
find: [],
get: [],
create: [
when(isTransactionEnable, async hook =>
TransactionManager.beginTransaction(hook, skipPath)
)
],
update: [
when(isTransactionEnable, async hook =>
TransactionManager.beginTransaction(hook, skipPath)
)
],
patch: [],
remove: []
// !end
},

after: {
// !<DEFAULT> code: after
all: [log()],
find: [],
get: [],
create: [when(isTransactionEnable, TransactionManager.commitTransaction)],
update: [when(isTransactionEnable, TransactionManager.commitTransaction)],
patch: [],
remove: [afterAudit]
// !end
},

error: {
// !<DEFAULT> code: error
all: [log()],
find: [],
get: [],
create: [when(isTransactionEnable, TransactionManager.rollbackTransaction)],
update: [when(isTransactionEnable, TransactionManager.rollbackTransaction)],
patch: [],
remove: []
// !end
}
// !code: moduleExports // !end
};

// !code: exports // !end
module.exports = moduleExports;

// !code: funcs // !end
// !code: end // !end
```

## License

[MIT](LICENSE)
Expand Down
2 changes: 2 additions & 0 deletions lib/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
const hooks = require('./hooks');
const service = require('./service');
const transactionManager = require('./transaction-manager');

Object.assign(service, { hooks, service });

module.exports = service;
module.exports.TransactionManager = transactionManager;
93 changes: 93 additions & 0 deletions lib/transaction-manager.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
const errors = require('@feathersjs/errors');
const mongoose = require('mongoose');

/**
* To start a new session and initiate transaction on the session and set
* mongoose-session in context-params to all consecutive service call if the
* boolean
*
* @param {object} context context and all params
* @param {[string]} skipPath list of paths to exclude from transaction
* - Example: ['login']
* @return {object} context context with db-session appended
*/
const beginTransaction = async (context, skipPath) => {
try {
// if the current path is not added to skipPath-list
if (skipPath.indexOf(context.path) === -1) {
// if there is no open-transaction appended already
if (context.params && !context.params.transactionOpen) {
const session = await mongoose.startSession();
session.startTransaction();
context.params.transactionOpen = true;
context.params.mongoose = { session };
}
context.enableTransaction = true; // true if transaction is enabled
} else {
context.enableTransaction = false;
}
return context;
} catch (err) {
throw new errors.MongoError(`Error while starting session`);
}
};

/**
* To commit a mongo-transaction after save methods of mongo service
*
* @param {object} context context with params, result and DB-session
* @return {object} context context with params, result and DB-session
*/
const commitTransaction = async context => {
try {
// if transaction is enabled during startSession
if (context.enableTransaction)
// if context contains the mongoose session to be committed
if (
context.params &&
context.params.mongoose &&
context.params.mongoose.session
) {
await context.params.mongoose.session.commitTransaction();
context.params.mongoose = null;
context.params.transactionOpen = false; // reset transaction-open
context.enableTransaction = false;
}
return context;
} catch (err) {
throw new errors.MongoError(`Error while commiting transaction`);
}
};

/**
* To rollback a mongo-transaction for any error thrown in service calls
*
* @param {object} context context with params and DB-session
* @return {object} context context with params and DB-session
*/
const rollbackTransaction = async context => {
try {
// if transaction is enabled during startSession
if (context.enableTransaction)
// if context contains the mongoose session to be committed
if (
context.params &&
context.params.mongoose &&
context.params.mongoose.session
) {
await context.params.mongoose.session.abortTransaction();
context.params.mongoose = null;
context.transactionOpen = false; // reset transaction-open
context.enableTransaction = false;
}
return context;
} catch (err) {
throw new errors.MongoError(`Error while rolling-back transaction`);
}
};

module.exports = {
beginTransaction,
commitTransaction,
rollbackTransaction
};

0 comments on commit 68e0525

Please sign in to comment.