2017-03-24 22:53:56 +00:00
|
|
|
/**
|
|
|
|
* @flow
|
2017-10-29 00:27:45 +00:00
|
|
|
* Database Transaction representation wrapper
|
2017-03-24 22:53:56 +00:00
|
|
|
*/
|
2017-12-22 15:24:31 +00:00
|
|
|
import { getAppEventName, SharedEventEmitter } from '../../utils/events';
|
|
|
|
import { getLogger } from '../../utils/log';
|
2018-01-05 17:20:02 +00:00
|
|
|
import { getNativeModule } from '../../utils/native';
|
2017-12-04 12:07:41 +00:00
|
|
|
import type Database from './';
|
2017-03-24 22:53:56 +00:00
|
|
|
|
2017-07-30 06:34:41 +00:00
|
|
|
let transactionId = 0;
|
2017-03-24 22:53:56 +00:00
|
|
|
|
2018-01-05 17:20:02 +00:00
|
|
|
/**
|
|
|
|
* Uses the push id generator to create a transaction id
|
|
|
|
* @returns {number}
|
|
|
|
* @private
|
|
|
|
*/
|
2018-01-25 18:25:39 +00:00
|
|
|
const generateTransactionId = (): number => transactionId++;
|
2018-01-05 17:20:02 +00:00
|
|
|
|
2017-03-24 22:53:56 +00:00
|
|
|
/**
|
2017-10-29 00:27:45 +00:00
|
|
|
* @class TransactionHandler
|
2017-03-24 22:53:56 +00:00
|
|
|
*/
|
2017-06-30 16:23:32 +00:00
|
|
|
export default class TransactionHandler {
|
2017-12-04 12:07:41 +00:00
|
|
|
_database: Database;
|
2018-01-25 18:25:39 +00:00
|
|
|
_transactions: { [number]: Object };
|
2017-12-04 12:07:41 +00:00
|
|
|
|
|
|
|
constructor(database: Database) {
|
2017-06-30 16:23:32 +00:00
|
|
|
this._transactions = {};
|
|
|
|
this._database = database;
|
2017-07-30 06:34:41 +00:00
|
|
|
|
2018-02-13 13:58:52 +00:00
|
|
|
SharedEventEmitter.addListener(
|
2017-12-22 15:24:31 +00:00
|
|
|
getAppEventName(this._database, 'database_transaction_event'),
|
2018-01-25 18:25:39 +00:00
|
|
|
this._handleTransactionEvent.bind(this)
|
2017-03-24 22:53:56 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2017-08-02 09:38:30 +00:00
|
|
|
* Add a new transaction and start it natively.
|
2017-03-24 22:53:56 +00:00
|
|
|
* @param reference
|
|
|
|
* @param transactionUpdater
|
|
|
|
* @param onComplete
|
|
|
|
* @param applyLocally
|
|
|
|
*/
|
2018-01-25 18:25:39 +00:00
|
|
|
add(
|
|
|
|
reference: Object,
|
|
|
|
transactionUpdater: Function,
|
|
|
|
onComplete?: Function,
|
|
|
|
applyLocally?: boolean = false
|
|
|
|
) {
|
2018-01-05 17:20:02 +00:00
|
|
|
const id = generateTransactionId();
|
2017-03-24 22:53:56 +00:00
|
|
|
|
2017-06-30 16:23:32 +00:00
|
|
|
this._transactions[id] = {
|
2017-03-24 22:53:56 +00:00
|
|
|
id,
|
|
|
|
reference,
|
|
|
|
transactionUpdater,
|
|
|
|
onComplete,
|
|
|
|
applyLocally,
|
|
|
|
completed: false,
|
|
|
|
started: true,
|
|
|
|
};
|
|
|
|
|
2018-01-25 18:25:39 +00:00
|
|
|
getNativeModule(this._database).transactionStart(
|
|
|
|
reference.path,
|
|
|
|
id,
|
|
|
|
applyLocally
|
|
|
|
);
|
2017-03-24 22:53:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* INTERNALS
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
*
|
|
|
|
* @param event
|
|
|
|
* @returns {*}
|
|
|
|
* @private
|
|
|
|
*/
|
|
|
|
_handleTransactionEvent(event: Object = {}) {
|
|
|
|
switch (event.type) {
|
|
|
|
case 'update':
|
|
|
|
return this._handleUpdate(event);
|
|
|
|
case 'error':
|
2017-04-04 16:58:47 +00:00
|
|
|
return this._handleError(event);
|
2017-03-24 22:53:56 +00:00
|
|
|
case 'complete':
|
|
|
|
return this._handleComplete(event);
|
|
|
|
default:
|
2018-01-25 18:25:39 +00:00
|
|
|
getLogger(this._database).warn(
|
|
|
|
`Unknown transaction event type: '${event.type}'`,
|
|
|
|
event
|
|
|
|
);
|
2017-03-24 22:53:56 +00:00
|
|
|
return undefined;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
*
|
|
|
|
* @param event
|
|
|
|
* @private
|
|
|
|
*/
|
|
|
|
_handleUpdate(event: Object = {}) {
|
|
|
|
let newValue;
|
|
|
|
const { id, value } = event;
|
|
|
|
|
|
|
|
try {
|
2017-06-30 16:23:32 +00:00
|
|
|
const transaction = this._transactions[id];
|
2017-07-30 06:34:41 +00:00
|
|
|
if (!transaction) return;
|
|
|
|
|
2017-03-24 22:53:56 +00:00
|
|
|
newValue = transaction.transactionUpdater(value);
|
|
|
|
} finally {
|
|
|
|
let abort = false;
|
|
|
|
|
|
|
|
if (newValue === undefined) {
|
|
|
|
abort = true;
|
|
|
|
}
|
|
|
|
|
2018-01-25 18:25:39 +00:00
|
|
|
getNativeModule(this._database).transactionTryCommit(id, {
|
|
|
|
value: newValue,
|
|
|
|
abort,
|
|
|
|
});
|
2017-03-24 22:53:56 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
*
|
|
|
|
* @param event
|
|
|
|
* @private
|
|
|
|
*/
|
|
|
|
_handleError(event: Object = {}) {
|
2017-06-30 16:23:32 +00:00
|
|
|
const transaction = this._transactions[event.id];
|
2017-03-24 22:53:56 +00:00
|
|
|
if (transaction && !transaction.completed) {
|
|
|
|
transaction.completed = true;
|
|
|
|
try {
|
2018-01-05 17:20:02 +00:00
|
|
|
transaction.onComplete(event.error, false, null);
|
2017-03-24 22:53:56 +00:00
|
|
|
} finally {
|
|
|
|
setImmediate(() => {
|
2017-06-30 16:23:32 +00:00
|
|
|
delete this._transactions[event.id];
|
2017-03-24 22:53:56 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
*
|
|
|
|
* @param event
|
|
|
|
* @private
|
|
|
|
*/
|
|
|
|
_handleComplete(event: Object = {}) {
|
2017-06-30 16:23:32 +00:00
|
|
|
const transaction = this._transactions[event.id];
|
2017-03-24 22:53:56 +00:00
|
|
|
if (transaction && !transaction.completed) {
|
|
|
|
transaction.completed = true;
|
|
|
|
try {
|
2018-01-25 18:25:39 +00:00
|
|
|
transaction.onComplete(
|
|
|
|
null,
|
|
|
|
event.committed,
|
|
|
|
Object.assign({}, event.snapshot)
|
|
|
|
);
|
2017-03-24 22:53:56 +00:00
|
|
|
} finally {
|
|
|
|
setImmediate(() => {
|
2017-06-30 16:23:32 +00:00
|
|
|
delete this._transactions[event.id];
|
2017-03-24 22:53:56 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|