Skip to content

Commit a8a5ad8

Browse files
committed
Doesn't allow .commit(), .rollback() or .close() managed transactions
Allowing self-manage managed transactions could causing unexpected behaviours in the user code depends on how the different possible code paths in the transaction work.
1 parent 524d883 commit a8a5ad8

File tree

3 files changed

+44
-5
lines changed

3 files changed

+44
-5
lines changed

packages/core/src/internal/transaction-executor.ts

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,8 @@ export class TransactionExecutor {
168168
transactionWork: TransactionWork<T>
169169
): Promise<T> {
170170
try {
171-
const result = transactionWork(tx)
171+
const managedTx = this._toManagedTx(tx)
172+
const result = transactionWork(managedTx)
172173
// user defined callback is supposed to return a promise, but it might not; so to protect against an
173174
// incorrect API usage we wrap the returned value with a resolved promise; this is effectively a
174175
// validation step without type checks
@@ -178,6 +179,23 @@ export class TransactionExecutor {
178179
}
179180
}
180181

182+
_toManagedTx(tx: Transaction): Transaction {
183+
const _thown =
184+
(method: string) =>
185+
() => Promise.reject(newError(`Transaction.${method}() is not supported in managed transactions.`))
186+
const _isTxManagementMethod =
187+
(method: string) => ['rollback', 'commit', 'close'].includes(method)
188+
189+
return new Proxy(tx, {
190+
get: (target, name) => {
191+
if (_isTxManagementMethod(name.toString())) {
192+
return _thown(name.toString())
193+
}
194+
return (target as any)[name];
195+
}
196+
}) as Transaction;
197+
}
198+
181199
_handleTransactionWorkSuccess<T>(
182200
result: T,
183201
tx: Transaction,

packages/core/test/session.test.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
*/
1919
import { ConnectionProvider, Session, Connection, TransactionPromise, Transaction } from '../src'
2020
import { bookmarks } from '../src/internal'
21+
import { newError } from '../src/error'
2122
import { ACCESS_MODE_READ, FETCH_ALL } from '../src/internal/constants'
2223
import FakeConnection from './utils/connection.fake'
2324

@@ -204,6 +205,30 @@ describe('session', () => {
204205
})
205206
}, 70000)
206207

208+
it.each([
209+
['commit', (tx: Transaction) => tx.commit()],
210+
['rollback', (tx: Transaction) => tx.rollback()],
211+
['close', (tx: Transaction) => tx.close()],
212+
])('should not allow call Transaction.%s() in the read transaction work', async (method, work) => {
213+
const connection = mockBeginWithSuccess(newFakeConnection())
214+
const session = newSessionWithConnection(connection, false, 1000)
215+
const expectedError = newError(`Transaction.${method}() is not supported in managed transactions.`)
216+
217+
expect(session.readTransaction(work)).rejects.toThrow(expectedError)
218+
})
219+
220+
it.each([
221+
['commit', (tx: Transaction) => tx.commit()],
222+
['rollback', (tx: Transaction) => tx.rollback()],
223+
['close', (tx: Transaction) => tx.close()],
224+
])('should not allow call Transaction.%s() in the write transaction work', async (method, work) => {
225+
const connection = mockBeginWithSuccess(newFakeConnection())
226+
const session = newSessionWithConnection(connection, false, 1000)
227+
const expectedError = newError(`Transaction.${method}() is not supported in managed transactions.`)
228+
229+
expect(session.writeTransaction(work)).rejects.toThrow(expectedError)
230+
})
231+
207232
describe('.lastBookmark()', () => {
208233
it.each([
209234
[bookmarks.Bookmarks.empty()],

packages/testkit-backend/src/skipped-tests/common.js

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -138,10 +138,6 @@ const skippedTests = [
138138
'Results are always valid but don\'t return records when out of scope',
139139
ifStartsWith('stub.iteration.test_result_scope.TestResultScope.')
140140
),
141-
skip(
142-
'Driver (still) allows explicit managing of managed transaction',
143-
ifEquals('stub.tx_lifetime.test_tx_lifetime.TestTxLifetime.test_managed_tx_raises_tx_managed_exec')
144-
),
145141
skip(
146142
'Flaky tests, requires investigation',
147143
ifEndsWith('.test_should_fail_when_reading_from_unexpectedly_interrupting_readers_using_tx_function')

0 commit comments

Comments
 (0)