transaction.js 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  1. const Promise = require('bluebird');
  2. const Model = require('./model/Model');
  3. const { isSubclassOf } = require('./utils/classUtils');
  4. function transaction() {
  5. // There must be at least one model class and the callback.
  6. if (arguments.length < 2) {
  7. return Promise.reject(
  8. new Error(
  9. 'objection.transaction: provide at least one Model class to bind to the transaction or a knex instance'
  10. )
  11. );
  12. }
  13. let args = new Array(arguments.length);
  14. for (let i = 0, l = args.length; i < l; ++i) {
  15. args[i] = arguments[i];
  16. }
  17. if (!isSubclassOf(args[0], Model) && typeof args[0].transaction === 'function') {
  18. let knex = args[0];
  19. args = args.slice(1);
  20. // If the function is a generator, wrap it using Promise.coroutine.
  21. if (isGenerator(args[0])) {
  22. args[0] = Promise.coroutine(args[0]);
  23. }
  24. return knex.transaction.apply(knex, args);
  25. } else {
  26. // The last argument should be the callback and all other Model subclasses.
  27. let callback = args[args.length - 1];
  28. let modelClasses = args.slice(0, args.length - 1);
  29. let i;
  30. for (i = 0; i < modelClasses.length; ++i) {
  31. if (!isSubclassOf(modelClasses[i], Model)) {
  32. return Promise.reject(
  33. new Error('objection.transaction: all but the last argument should be Model subclasses')
  34. );
  35. }
  36. }
  37. let knex = modelClasses[0].knex();
  38. for (i = 0; i < modelClasses.length; ++i) {
  39. if (modelClasses[i].knex() !== knex) {
  40. return Promise.reject(
  41. new Error(
  42. 'objection.transaction: all Model subclasses must be bound to the same database'
  43. )
  44. );
  45. }
  46. }
  47. // If the function is a generator, wrap it using Promise.coroutine.
  48. if (isGenerator(callback)) {
  49. callback = Promise.coroutine(callback);
  50. }
  51. return knex.transaction(trx => {
  52. let args = new Array(modelClasses.length + 1);
  53. for (let i = 0; i < modelClasses.length; ++i) {
  54. args[i] = modelClasses[i].bindTransaction(trx);
  55. }
  56. args[args.length - 1] = trx;
  57. return Promise.try(() => {
  58. return callback.apply(trx, args);
  59. });
  60. });
  61. }
  62. }
  63. transaction.start = function(modelClassOrKnex) {
  64. let knex = modelClassOrKnex;
  65. if (isSubclassOf(modelClassOrKnex, Model)) {
  66. knex = modelClassOrKnex.knex();
  67. }
  68. if (!knex || typeof knex.transaction !== 'function') {
  69. return Promise.reject(
  70. new Error(
  71. 'objection.transaction.start: first argument must be a model class or a knex instance'
  72. )
  73. );
  74. }
  75. return new Promise((resolve, reject) => {
  76. knex
  77. .transaction(trx => {
  78. resolve(trx);
  79. })
  80. .catch(err => {
  81. reject(err);
  82. });
  83. });
  84. };
  85. function isGenerator(fn) {
  86. return fn && fn.constructor && fn.constructor.name === 'GeneratorFunction';
  87. }
  88. module.exports = transaction;