Skip to content

Commit

Permalink
Expose executionPromise for transactors
Browse files Browse the repository at this point in the history
  • Loading branch information
kibertoad committed Jun 19, 2019
1 parent 141b202 commit b69875a
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 4 deletions.
19 changes: 15 additions & 4 deletions src/transaction.js
Expand Up @@ -20,7 +20,7 @@ class Transaction extends EventEmitter {

// If there is no container provided, assume user wants to get instance of transaction and use it directly
if (!container) {
this.initPromise = new Bluebird((resolve, reject) => {
this.initPromise = new Promise((resolve, reject) => {
this.initRejectFn = reject;
container = (transactor) => {
resolve(transactor);
Expand Down Expand Up @@ -57,6 +57,11 @@ class Transaction extends EventEmitter {
return makeTransactor(this, connection, trxClient);
})
.then((transactor) => {
transactor.executionPromise = new Promise((resolve, reject) => {
this.executionRejectFn = reject;
this.executionResolveFn = resolve;
});

// If we've returned a "thenable" = require(the transaction container, assume
// the rollback and commit are chained to this object's success / failure.
// Directly thrown errors are treated as automatic rollbacks.
Expand All @@ -79,14 +84,20 @@ class Transaction extends EventEmitter {
})
.catch((e) => {
if (this.initRejectFn) {
this.initRejectFn();
this.initRejectFn(e);
}
return this._rejecter(e);
});

return new Bluebird((resolver, rejecter) => {
this._resolver = resolver;
this._rejecter = rejecter;
this._resolver = (value) => {
this.executionResolveFn(value);
resolver(value);
};
this._rejecter = (err) => {
this.executionRejectFn(err);
rejecter(err);
};
});
}
).catch((err) => {
Expand Down
60 changes: 60 additions & 0 deletions test/unit/knex.js
Expand Up @@ -326,6 +326,66 @@ describe('knex', () => {
});
});

it('supports accessing execution promise from standalone transaction', async () => {
const knex = Knex(sqliteConfig);

const trx = await knex.transaction();
const executionPromise = trx.executionPromise;
expect(executionPromise).to.be.ok;

expect(trx.client.transacting).to.equal(true);
const rows = await knex.transacting(trx).select(knex.raw('1 as result'));
expect(rows[0].result).to.equal(1);
await trx.commit();

const result = await executionPromise;
expect(result).to.be.undefined;
});

it('supports accessing execution promise from transaction with a callback', async () => {
const knex = Knex(sqliteConfig);
const trxPromise = new Promise(async (resolve, reject) => {
knex.transaction((transaction) => {
resolve(transaction);
});
});
const trx = await trxPromise;
const executionPromise = trx.executionPromise;
expect(executionPromise).to.be.ok;

expect(trx.client.transacting).to.equal(true);
const rows = await knex.transacting(trx).select(knex.raw('1 as result'));
expect(rows[0].result).to.equal(1);
await trx.commit();

const result = await executionPromise;
expect(result).to.be.undefined;
});

it('rejects execution promise if there was a rollback', async () => {
const knex = Knex(sqliteConfig);

const trx = await knex.transaction();
const executionPromise = trx.executionPromise;

expect(trx.client.transacting).to.equal(true);
const rows = await knex.transacting(trx).select(knex.raw('1 as result'));
expect(rows[0].result).to.equal(1);
await trx.rollback();

let errorWasThrown;
try {
await executionPromise;
} catch (err) {
errorWasThrown = true;
expect(err.message).to.equal(
'Transaction rejected with non-error: undefined'
);
}

expect(errorWasThrown).to.be.true;
});

it('creating transaction copy with user params should throw an error', () => {
if (!sqliteConfig) {
return;
Expand Down
2 changes: 2 additions & 0 deletions types/index.d.ts
Expand Up @@ -1401,6 +1401,8 @@ declare namespace Knex {

interface Transaction<TRecord extends {} = any, TResult = any>
extends Knex<TRecord, TResult> {
executionPromise: Promise<TResult>;

query<TRecord extends {} = any, TResult = void>(
conn: any,
sql: any,
Expand Down

0 comments on commit b69875a

Please sign in to comment.