diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 15ef0193c..0310319cb 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -70,6 +70,7 @@ import ConnectionProvider from './connection-provider' import Connection from './connection' import Transaction from './transaction' import TransactionPromise from './transaction-promise' +import ManagedTransaction from './transaction-managed' import Session, { TransactionConfig } from './session' import Driver, * as driver from './driver' import auth from './auth' @@ -136,6 +137,7 @@ const forExport = { Result, Transaction, TransactionPromise, + ManagedTransaction, Session, Driver, Connection, @@ -194,6 +196,7 @@ export { Connection, Transaction, TransactionPromise, + ManagedTransaction, Session, Driver, types, diff --git a/packages/core/src/session.ts b/packages/core/src/session.ts index bd7ad4424..6e32682ea 100644 --- a/packages/core/src/session.ts +++ b/packages/core/src/session.ts @@ -32,9 +32,10 @@ import { Query, SessionMode } from './types' import Connection from './connection' import { NumberOrInteger } from './graph-types' import TransactionPromise from './transaction-promise' +import ManagedTransaction from './transaction-managed' type ConnectionConsumer = (connection: Connection | void) => any | undefined -type TransactionWork = (tx: Transaction) => Promise | T +type TransactionWork = (tx: ManagedTransaction) => Promise | T interface TransactionConfig { timeout?: NumberOrInteger @@ -336,7 +337,7 @@ class Session { * delay of 1 second and maximum retry time of 30 seconds. Maximum retry time is configurable via driver config's * `maxTransactionRetryTime` property in milliseconds. * - * @param {function(tx: Transaction): Promise} transactionWork - Callback that executes operations against + * @param {function(tx: ManagedTransaction): Promise} transactionWork - Callback that executes operations against * a given {@link Transaction}. * @param {TransactionConfig} [transactionConfig] - Configuration for all transactions started to execute the unit of work. * @return {Promise} Resolved promise as returned by the given function or rejected promise when given @@ -358,7 +359,7 @@ class Session { * delay of 1 second and maximum retry time of 30 seconds. Maximum retry time is configurable via driver config's * `maxTransactionRetryTime` property in milliseconds. * - * @param {function(tx: Transaction): Promise} transactionWork - Callback that executes operations against + * @param {function(tx: ManagedTransaction): Promise} transactionWork - Callback that executes operations against * a given {@link Transaction}. * @param {TransactionConfig} [transactionConfig] - Configuration for all transactions started to execute the unit of work. * @return {Promise} Resolved promise as returned by the given function or rejected promise when given diff --git a/packages/core/src/transaction-managed.ts b/packages/core/src/transaction-managed.ts new file mode 100644 index 000000000..d61fe9bbf --- /dev/null +++ b/packages/core/src/transaction-managed.ts @@ -0,0 +1,70 @@ +/** + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import Transaction from './transaction' + +/** + * Represents a transaction that is managed by the transaction executor. + * + * @public + */ +class ManagedTransaction extends Transaction { + + /** + * Commits the transaction and returns the result. + * + * After committing the transaction can no longer be used. + * + * @deprecated Commit should be done by returning from the transaction work. + * + * @returns {Promise} An empty promise if committed successfully or error if any error happened during commit. + */ + commit(): Promise { + return super.commit() + } + + /** + * Rollbacks the transaction. + * + * After rolling back, the transaction can no longer be used. + * + * @deprecated Rollback should be done by throwing or returning a rejected promise from the transaction work. + * + * @returns {Promise} An empty promise if rolled back successfully or error if any error happened during + * rollback. + */ + rollback(): Promise { + return super.rollback() + } + + /** + * Closes the transaction + * + * This method will roll back the transaction if it is not already committed or rolled back. + * + * @deprecated Closure should not be done in transaction work. See {@link ManagedTransaction#commit} and {@link ManagedTransaction#rollback} + * + * @returns {Promise} An empty promise if closed successfully or error if any error happened during + */ + close(): Promise { + return super.close() + } +} + +export default ManagedTransaction diff --git a/packages/core/src/transaction-promise.ts b/packages/core/src/transaction-promise.ts index 1d40fdab4..5a695d2ea 100644 --- a/packages/core/src/transaction-promise.ts +++ b/packages/core/src/transaction-promise.ts @@ -170,6 +170,7 @@ class TransactionPromise extends Transaction implements Promise{ /** * @access private + * @returns {void} */ private _onBeginError(error: Error): void { this._beginError = error; @@ -180,6 +181,7 @@ class TransactionPromise extends Transaction implements Promise{ /** * @access private + * @returns {void} */ private _onBeginMetadata(metadata: any): void { this._beginMetadata = metadata || {}; diff --git a/packages/core/test/transaction.test.ts b/packages/core/test/transaction.test.ts index c47aa7a27..9dfef90e1 100644 --- a/packages/core/test/transaction.test.ts +++ b/packages/core/test/transaction.test.ts @@ -21,10 +21,12 @@ import { ConnectionProvider, newError, Transaction, TransactionPromise } from ". import { Bookmarks } from "../src/internal/bookmarks"; import { ConnectionHolder } from "../src/internal/connection-holder"; import { TxConfig } from "../src/internal/tx-config"; +import ManagedTransaction from "../src/transaction-managed"; import FakeConnection from "./utils/connection.fake"; testTx('Transaction', newRegularTransaction) +testTx('ManagedTransaction', newManagedTransaction) testTx('TransactionPromise', newTransactionPromise, () => { describe('Promise', () => { @@ -498,6 +500,39 @@ function newRegularTransaction({ return transaction } +function newManagedTransaction({ + connection, + fetchSize = 1000, + highRecordWatermark = 700, + lowRecordWatermark = 300 +}: { + connection: FakeConnection + fetchSize?: number + highRecordWatermark?: number, + lowRecordWatermark?: number +}): ManagedTransaction { + const connectionProvider = new ConnectionProvider() + connectionProvider.acquireConnection = () => Promise.resolve(connection) + connectionProvider.close = () => Promise.resolve() + + const connectionHolder = new ConnectionHolder({ connectionProvider }) + connectionHolder.initializeConnection() + + const transaction = new ManagedTransaction({ + connectionHolder, + onClose: () => { }, + onBookmarks: (_: Bookmarks) => { }, + onConnection: () => { }, + reactive: false, + fetchSize, + impersonatedUser: "", + highRecordWatermark, + lowRecordWatermark + }) + + return transaction +} + function newFakeConnection(): FakeConnection { return new FakeConnection() } diff --git a/packages/neo4j-driver-lite/src/index.ts b/packages/neo4j-driver-lite/src/index.ts index 112aac33a..1614a0875 100644 --- a/packages/neo4j-driver-lite/src/index.ts +++ b/packages/neo4j-driver-lite/src/index.ts @@ -63,6 +63,7 @@ import { Session, Transaction, TransactionPromise, + ManagedTransaction, ServerInfo, Connection, driver as coreDriver, @@ -427,6 +428,7 @@ const forExport = { Session, Transaction, TransactionPromise, + ManagedTransaction, Point, Duration, LocalTime, @@ -476,6 +478,7 @@ export { Session, Transaction, TransactionPromise, + ManagedTransaction, Point, Duration, LocalTime, diff --git a/packages/neo4j-driver/src/session-rx.js b/packages/neo4j-driver/src/session-rx.js index 863d451d3..07f284b62 100644 --- a/packages/neo4j-driver/src/session-rx.js +++ b/packages/neo4j-driver/src/session-rx.js @@ -21,6 +21,7 @@ import { flatMap, catchError, concat } from 'rxjs/operators' import RxResult from './result-rx' import { Session, internal } from 'neo4j-driver-core' import RxTransaction from './transaction-rx' +import RxManagedTransaction from './transaction-managed-rx' import RxRetryLogic from './internal/retry-logic-rx' const { @@ -84,7 +85,7 @@ export default class RxSession { * Executes the provided unit of work in a {@link READ} reactive transaction which is created with the provided * transaction configuration. * @public - * @param {function(txc: RxTransaction): Observable} work - A unit of work to be executed. + * @param {function(txc: RxManagedTransaction): Observable} work - A unit of work to be executed. * @param {TransactionConfig} transactionConfig - Configuration for the enclosing transaction created by the driver. * @returns {Observable} - A reactive stream returned by the unit of work. */ @@ -96,7 +97,7 @@ export default class RxSession { * Executes the provided unit of work in a {@link WRITE} reactive transaction which is created with the provided * transaction configuration. * @public - * @param {function(txc: RxTransaction): Observable} work - A unit of work to be executed. + * @param {function(txc: RxManagedTransaction): Observable} work - A unit of work to be executed. * @param {TransactionConfig} transactionConfig - Configuration for the enclosing transaction created by the driver. * @returns {Observable} - A reactive stream returned by the unit of work. */ diff --git a/packages/neo4j-driver/src/transaction-managed-rx.js b/packages/neo4j-driver/src/transaction-managed-rx.js new file mode 100644 index 000000000..fe753ee30 --- /dev/null +++ b/packages/neo4j-driver/src/transaction-managed-rx.js @@ -0,0 +1,62 @@ +/** + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import RxTransaction from './transaction-rx' + +/** + * Represents a RX transaction that is managed by the transaction executor. + * + * @public + */ +class RxManagedTransaction extends RxTransaction { + /** + * Commits the transaction. + * + * @public + * @deprecated Commit should be done by returning from the transaction work. + * @returns {Observable} - An empty observable + */ + commit () { + return super.commit() + } + + /** + * Rolls back the transaction. + * + * @public + * @deprecated Rollback should be done by throwing or returning a rejected promise from the transaction work. + * @returns {Observable} - An empty observable + */ + rollback () { + return super.rollback() + } + + /** + * Closes the transaction + * + * This method will roll back the transaction if it is not already committed or rolled back. + * @deprecated Closure should not be done in transaction work. See {@link RxManagedTransaction#commit} and {@link RxManagedTransaction#rollback} + * @returns {Observable} - An empty observable + */ + close () { + return super.close() + } +} + +export default RxManagedTransaction diff --git a/packages/neo4j-driver/test/rx/transaction-managed.test.js b/packages/neo4j-driver/test/rx/transaction-managed.test.js new file mode 100644 index 000000000..1d5a77f8a --- /dev/null +++ b/packages/neo4j-driver/test/rx/transaction-managed.test.js @@ -0,0 +1,119 @@ +/** + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import RxManagedTransaction from '../../src/transaction-managed-rx' + +describe('#unit', () => { + describe('.commit()', () => { + it('should delegate to the original Transaction', async () => { + const txc = { + commit: jasmine.createSpy('commit').and.returnValue(Promise.resolve()) + } + + const transaction = new RxManagedTransaction(txc) + + await transaction.commit().toPromise() + + expect(txc.commit).toHaveBeenCalled() + }) + + it('should fail if to the original Transaction.close call fails', async () => { + const expectedError = new Error('expected') + const txc = { + commit: jasmine + .createSpy('commit') + .and.returnValue(Promise.reject(expectedError)) + } + + const transaction = new RxManagedTransaction(txc) + + try { + await transaction.commit().toPromise() + fail('should have thrown') + } catch (error) { + expect(error).toBe(expectedError) + } + }) + }) + + describe('.rollback()', () => { + it('should delegate to the original Transaction', async () => { + const txc = { + rollback: jasmine + .createSpy('rollback') + .and.returnValue(Promise.resolve()) + } + + const transaction = new RxManagedTransaction(txc) + + await transaction.rollback().toPromise() + + expect(txc.rollback).toHaveBeenCalled() + }) + + it('should fail if to the original Transaction.close call fails', async () => { + const expectedError = new Error('expected') + const txc = { + rollback: jasmine + .createSpy('rollback') + .and.returnValue(Promise.reject(expectedError)) + } + + const transaction = new RxManagedTransaction(txc) + + try { + await transaction.rollback().toPromise() + fail('should have thrown') + } catch (error) { + expect(error).toBe(expectedError) + } + }) + }) + + describe('.close()', () => { + it('should delegate to the original Transaction', async () => { + const txc = { + close: jasmine.createSpy('close').and.returnValue(Promise.resolve()) + } + + const transaction = new RxManagedTransaction(txc) + + await transaction.close().toPromise() + + expect(txc.close).toHaveBeenCalled() + }) + + it('should fail if to the original Transaction.close call fails', async () => { + const expectedError = new Error('expected') + const txc = { + close: jasmine + .createSpy('close') + .and.returnValue(Promise.reject(expectedError)) + } + + const transaction = new RxManagedTransaction(txc) + + try { + await transaction.close().toPromise() + fail('should have thrown') + } catch (error) { + expect(error).toBe(expectedError) + } + }) + }) +}) diff --git a/packages/neo4j-driver/test/types/session-rx.test.ts b/packages/neo4j-driver/test/types/session-rx.test.ts index 4c768cac9..5c373ff4c 100644 --- a/packages/neo4j-driver/test/types/session-rx.test.ts +++ b/packages/neo4j-driver/test/types/session-rx.test.ts @@ -28,6 +28,7 @@ import { } from 'neo4j-driver-core' import { Observable, of, Observer, throwError } from 'rxjs' import { concat, finalize, catchError } from 'rxjs/operators' +import RxManagedTransaction from '../../types/transaction-managed-rx' const dummy: any = null const intValue: Integer = Integer.fromInt(42) @@ -152,3 +153,15 @@ const observable6: Observable = rxSession.writeTransaction( (tx: RxTransaction) => of(42), txConfig4 ) + +const observable7: Observable = rxSession.readTransaction( + (tx: RxManagedTransaction) => { + return of('42') + } +) + +const observable8: Observable = rxSession.writeTransaction( + (tx: RxManagedTransaction) => { + return of('42') + } +) diff --git a/packages/neo4j-driver/test/types/session.test.ts b/packages/neo4j-driver/test/types/session.test.ts index c427f23b3..c61bf7253 100644 --- a/packages/neo4j-driver/test/types/session.test.ts +++ b/packages/neo4j-driver/test/types/session.test.ts @@ -27,6 +27,7 @@ import { Session, TransactionConfig } from 'neo4j-driver-core' +import ManagedTransaction from 'neo4j-driver-core/types/transaction-managed' const dummy: any = null const intValue: Integer = Integer.fromInt(42) diff --git a/packages/neo4j-driver/types/index.d.ts b/packages/neo4j-driver/types/index.d.ts index 2ec47266e..265ae9686 100644 --- a/packages/neo4j-driver/types/index.d.ts +++ b/packages/neo4j-driver/types/index.d.ts @@ -57,6 +57,8 @@ import { ResultObserver, QueryResult, Transaction, + TransactionPromise, + ManagedTransaction, Session, ConnectionProvider } from 'neo4j-driver-core' @@ -72,6 +74,7 @@ import { } from './driver' import RxSession from './session-rx' import RxTransaction from './transaction-rx' +import RxManagedTransaction from './transaction-managed-rx' import RxResult from './result-rx' import { Parameters } from './query-runner' @@ -116,6 +119,7 @@ declare const types: { Integer: typeof Integer RxSession: RxSession RxTransaction: RxTransaction + RxManagedTransaction: RxManagedTransaction RxResult: RxResult } @@ -189,6 +193,8 @@ declare const forExport: { NotificationPosition: NotificationPosition Session: Session Transaction: Transaction + TransactionPromise: TransactionPromise + ManagedTransaction: ManagedTransaction Point: Point isPoint: typeof isPoint Duration: Duration @@ -199,6 +205,7 @@ declare const forExport: { DateTime: DateTime RxSession: RxSession RxTransaction: RxTransaction + RxManagedTransaction: RxManagedTransaction RxResult: RxResult ConnectionProvider: ConnectionProvider isDuration: typeof isDuration @@ -246,6 +253,8 @@ export { NotificationPosition, Session, Transaction, + TransactionPromise, + ManagedTransaction, Point, isPoint, Duration, @@ -256,6 +265,7 @@ export { DateTime, RxSession, RxTransaction, + RxManagedTransaction, RxResult, ConnectionProvider, isDuration, diff --git a/packages/neo4j-driver/types/session-rx.d.ts b/packages/neo4j-driver/types/session-rx.d.ts index 396e137f2..5f1e38cc0 100644 --- a/packages/neo4j-driver/types/session-rx.d.ts +++ b/packages/neo4j-driver/types/session-rx.d.ts @@ -18,11 +18,12 @@ */ import RxResult from './result-rx' import RxTransaction from './transaction-rx' +import RxManagedTransaction from './transaction-managed-rx' import { TransactionConfig } from 'neo4j-driver-core' import { Parameters } from './query-runner' import { Observable } from 'rxjs' -declare type RxTransactionWork = (tx: RxTransaction) => Observable +declare type RxTransactionWork = (tx: RxManagedTransaction) => Observable declare interface RxSession { run( diff --git a/packages/neo4j-driver/types/transaction-managed-rx.d.ts b/packages/neo4j-driver/types/transaction-managed-rx.d.ts new file mode 100644 index 000000000..4e5303094 --- /dev/null +++ b/packages/neo4j-driver/types/transaction-managed-rx.d.ts @@ -0,0 +1,54 @@ +/** + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { Observable } from 'rxjs' +import { Parameters } from './query-runner' +import RxResult from './result-rx' + +declare interface RxManagedTransaction { + run(query: string, parameters?: Parameters): RxResult + + /** + * Commits the transaction. + * + * @public + * @deprecated Commit should be done by returning from the transaction work. + * @returns {Observable} - An empty observable + */ + commit(): Observable + + /** + * Rolls back the transaction. + * + * @public + * @deprecated Rollback should be done by throwing or returning a rejected promise from the transaction work. + * @returns {Observable} - An empty observable + */ + rollback(): Observable + + /** + * Closes the transaction + * + * This method will roll back the transaction if it is not already committed or rolled back. + * @deprecated Closure should not be done in transaction work. See {@link RxManagedTransaction#commit} and {@link RxManagedTransaction#rollback} + * @returns {Observable} - An empty observable + */ + close(): Observable +} + +export default RxManagedTransaction