UNPKG

react-relay

Version:

A framework for building data-driven React applications.

357 lines (297 loc) • 13.4 kB
/** * Copyright 2013-2015, Facebook, Inc. * All rights reserved. * * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * * @providesModule RelayMutationTransaction * @typechecks * */ 'use strict'; var _classCallCheck = require('babel-runtime/helpers/class-call-check')['default']; var _extends = require('babel-runtime/helpers/extends')['default']; var _defineProperty = require('babel-runtime/helpers/define-property')['default']; Object.defineProperty(exports, '__esModule', { value: true }); var ErrorUtils = require('fbjs/lib/ErrorUtils'); var RelayConnectionInterface = require('./RelayConnectionInterface'); var RelayMutationQuery = require('./RelayMutationQuery'); var RelayMutationRequest = require('./RelayMutationRequest'); var RelayMutationTransactionStatus = require('./RelayMutationTransactionStatus'); var RelayNetworkLayer = require('./RelayNetworkLayer'); var RelayStoreData = require('./RelayStoreData'); var fromGraphQL = require('./fromGraphQL'); var invariant = require('fbjs/lib/invariant'); var nullthrows = require('fbjs/lib/nullthrows'); var resolveImmediate = require('fbjs/lib/resolveImmediate'); var CLIENT_MUTATION_ID = RelayConnectionInterface.CLIENT_MUTATION_ID; var collisionQueueMap = {}; var pendingTransactionMap = {}; var queue = []; var transactionIDCounter = 0; /** * @internal */ var RelayMutationTransaction = (function () { function RelayMutationTransaction(mutation) { _classCallCheck(this, RelayMutationTransaction); this._id = (transactionIDCounter++).toString(36); this._mutation = mutation; this._status = RelayMutationTransactionStatus.UNCOMMITTED; pendingTransactionMap[this._id] = this; queue.push(this); this._handleOptimisticUpdate(); } RelayMutationTransaction.get = function get(id) { var transaction = pendingTransactionMap[id]; !transaction ? process.env.NODE_ENV !== 'production' ? invariant(false, 'RelayMutationTransaction: `%s` is not a valid pending transaction ID.', id) : invariant(false) : undefined; return transaction; }; RelayMutationTransaction.prototype._assertIsPending = function _assertIsPending() { !pendingTransactionMap[this._id] ? process.env.NODE_ENV !== 'production' ? invariant(false, 'RelayMutationTransaction: Only pending transactions can be interacted ' + 'with.') : invariant(false) : undefined; }; RelayMutationTransaction.prototype.commit = function commit(callbacks) { this._assertIsPending(); !(this._status === RelayMutationTransactionStatus.UNCOMMITTED) ? process.env.NODE_ENV !== 'production' ? invariant(false, 'RelayMutationTransaction: Only transactions with status `UNCOMMITTED` ' + 'can be comitted.') : invariant(false) : undefined; if (callbacks) { this._onCommitFailureCallback = callbacks.onFailure; this._onCommitSuccessCallback = callbacks.onSuccess; } this._queueForCommit(); }; RelayMutationTransaction.prototype.getError = function getError() { this._assertIsPending(); return this._error; }; RelayMutationTransaction.prototype.getStatus = function getStatus() { this._assertIsPending(); return this._status; }; RelayMutationTransaction.prototype.recommit = function recommit() { this._assertIsPending(); !(this._status === RelayMutationTransactionStatus.COMMIT_FAILED || this._status === RelayMutationTransactionStatus.COLLISION_COMMIT_FAILED) ? process.env.NODE_ENV !== 'production' ? invariant(false, 'RelayMutationTransaction: Only transaction with status ' + '`COMMIT_FAILED` or `COLLISION_COMMIT_FAILED` can be comitted.') : invariant(false) : undefined; this._queueForCommit(); }; RelayMutationTransaction.prototype.rollback = function rollback() { this._assertIsPending(); !(this._status === RelayMutationTransactionStatus.UNCOMMITTED || this._status === RelayMutationTransactionStatus.COMMIT_FAILED || this._status === RelayMutationTransactionStatus.COLLISION_COMMIT_FAILED) ? process.env.NODE_ENV !== 'production' ? invariant(false, 'RelayMutationTransaction: Only transactions with status `UNCOMMITTED` ' + '`COMMIT_FAILED` or `COLLISION_COMMIT_FAILED` can be rolledback.') : invariant(false) : undefined; this._handleRollback(); }; RelayMutationTransaction.prototype._getCallName = function _getCallName() { if (!this._callName) { this._callName = this._getMutationNode().calls[0].name; } return this._callName; }; RelayMutationTransaction.prototype._getConfigs = function _getConfigs() { if (!this._configs) { this._configs = this._mutation.getConfigs(); } return this._configs; }; RelayMutationTransaction.prototype._getCollisionKey = function _getCollisionKey() { if (this._collisionKey === undefined) { this._collisionKey = this._mutation.getCollisionKey() || null; } return this._collisionKey; }; RelayMutationTransaction.prototype._getFatQuery = function _getFatQuery() { if (!this._fatQuery) { this._fatQuery = fromGraphQL.Fragment(this._mutation.getFatQuery()); } return this._fatQuery; }; RelayMutationTransaction.prototype._getMutationNode = function _getMutationNode() { if (!this._mutationNode) { this._mutationNode = this._mutation.getMutation(); } return this._mutationNode; }; RelayMutationTransaction.prototype._getQuery = function _getQuery() { if (!this._query) { this._query = RelayMutationQuery.buildQuery({ configs: this._getConfigs(), fatQuery: this._getFatQuery(), mutationName: this._mutation.constructor.name, mutation: this._getMutationNode() }); } return this._query; }; RelayMutationTransaction.prototype._getVariables = function _getVariables() { if (!this._variables) { var input = _extends({}, this._mutation.getVariables(), _defineProperty({}, CLIENT_MUTATION_ID, this._id)); this._variables = { input: input }; } return this._variables; }; RelayMutationTransaction.prototype._getFiles = function _getFiles() { if (this._files === undefined) { this._files = this._mutation.getFiles() || null; } return this._files; }; RelayMutationTransaction.prototype._getOptimisticConfigs = function _getOptimisticConfigs() { if (this._optimisticConfigs === undefined) { this._optimisticConfigs = this._mutation.getOptimisticConfigs() || null; } return this._optimisticConfigs; }; RelayMutationTransaction.prototype._getOptimisticQuery = function _getOptimisticQuery() { if (this._optimisticQuery === undefined) { var optimisticResponse = this._getOptimisticResponse(); if (optimisticResponse) { var optimisticConfigs = this._getOptimisticConfigs(); if (optimisticConfigs) { this._optimisticQuery = RelayMutationQuery.buildQuery({ configs: optimisticConfigs, fatQuery: this._getFatQuery(), mutationName: this._mutation.constructor.name, mutation: this._getMutationNode() }); } else { this._optimisticQuery = RelayMutationQuery.buildQueryForOptimisticUpdate({ response: optimisticResponse, fatQuery: this._getFatQuery(), mutation: this._getMutationNode() }); } } else { this._optimisticQuery = null; } } return this._optimisticQuery; }; RelayMutationTransaction.prototype._getOptimisticResponse = function _getOptimisticResponse() { if (this._optimisticResponse === undefined) { var optimisticResponse = this._mutation.getOptimisticResponse() || null; if (optimisticResponse) { optimisticResponse[CLIENT_MUTATION_ID] = this._id; } this._optimisticResponse = optimisticResponse; } return this._optimisticResponse; }; RelayMutationTransaction.prototype._queueForCommit = function _queueForCommit() { var collisionKey = this._getCollisionKey(); if (collisionKey) { if (!collisionQueueMap.hasOwnProperty(collisionKey)) { collisionQueueMap[collisionKey] = [this]; this._handleCommit(); } else { collisionQueueMap[collisionKey].push(this); this._status = RelayMutationTransactionStatus.COMMIT_QUEUED; } } else { this._handleCommit(); } }; RelayMutationTransaction.prototype._markAsNotPending = function _markAsNotPending() { var _this = this; delete pendingTransactionMap[this._id]; queue = queue.filter(function (transaction) { return transaction !== _this; }); }; RelayMutationTransaction.prototype._handleOptimisticUpdate = function _handleOptimisticUpdate() { var optimisticResponse = this._getOptimisticResponse(); var optimisticQuery = this._getOptimisticQuery(); if (optimisticResponse && optimisticQuery) { var configs = this._getOptimisticConfigs() || this._getConfigs(); optimisticResponse[CLIENT_MUTATION_ID] = this._id; // Repeating for Flow RelayStoreData.getDefaultInstance().handleUpdatePayload(optimisticQuery, optimisticResponse, { configs: configs, isOptimisticUpdate: true }); } }; RelayMutationTransaction.prototype._handleCommit = function _handleCommit() { var _this2 = this; this._status = RelayMutationTransactionStatus.COMMITTING; var request = new RelayMutationRequest(this._getQuery(), this._getVariables(), this._getFiles()); RelayNetworkLayer.sendMutation(request); request.getPromise().done(function (result) { return _this2._handleCommitSuccess(result.response); }, function (error) { _this2._error = error; _this2._handleCommitFailure(true); }); }; RelayMutationTransaction.prototype._handleCommitFailure = function _handleCommitFailure(isServerError) { this._status = isServerError ? RelayMutationTransactionStatus.COMMIT_FAILED : RelayMutationTransactionStatus.COLLISION_COMMIT_FAILED; var shouldRollback = true; var commitFailureCallback = this._onCommitFailureCallback; if (commitFailureCallback) { var preventAutoRollback = function preventAutoRollback() { shouldRollback = false; }; ErrorUtils.applyWithGuard(commitFailureCallback, null, [this, preventAutoRollback], null, 'RelayMutationTransaction:onCommitFailure'); } if (isServerError) { RelayMutationTransaction._failCollisionQueue(this._getCollisionKey()); } // Might have already been rolled back via `commitFailureCallback`. var wasRolledback = !pendingTransactionMap[this._id]; if (shouldRollback && !wasRolledback) { this._handleRollback(); } else { RelayMutationTransaction._batchRefreshQueuedData(); } }; RelayMutationTransaction.prototype._handleRollback = function _handleRollback() { this._markAsNotPending(); RelayMutationTransaction._batchRefreshQueuedData(); }; RelayMutationTransaction.prototype._handleCommitSuccess = function _handleCommitSuccess(response) { RelayMutationTransaction._advanceCollisionQueue(this._getCollisionKey()); this._markAsNotPending(); RelayMutationTransaction._refreshQueuedData(); RelayStoreData.getDefaultInstance().handleUpdatePayload(this._getQuery(), response[this._getCallName()], { configs: this._getConfigs(), isOptimisticUpdate: false }); if (this._onCommitSuccessCallback) { ErrorUtils.applyWithGuard(this._onCommitSuccessCallback, null, [response], null, 'RelayMutationTransaction:onCommitSuccess'); } }; RelayMutationTransaction._advanceCollisionQueue = function _advanceCollisionQueue(collisionKey) { if (collisionKey) { var collisionQueue = nullthrows(collisionQueueMap[collisionKey]); // Remove the transaction that called this function. collisionQueue.shift(); if (collisionQueue.length) { collisionQueue[0]._handleCommit(); } else { delete collisionQueueMap[collisionKey]; } } }; RelayMutationTransaction._failCollisionQueue = function _failCollisionQueue(collisionKey) { if (collisionKey) { var collisionQueue = nullthrows(collisionQueueMap[collisionKey]); // Remove the transaction that called this function. collisionQueue.shift(); collisionQueue.forEach(function (transaction) { return transaction._handleCommitFailure(false); }); delete collisionQueueMap[collisionKey]; } }; RelayMutationTransaction._refreshQueuedData = function _refreshQueuedData() { RelayStoreData.getDefaultInstance().clearQueuedData(); queue.forEach(function (transaction) { return transaction._handleOptimisticUpdate(); }); }; RelayMutationTransaction._batchRefreshQueuedData = function _batchRefreshQueuedData() { if (!RelayMutationTransaction._willBatchRefreshQueuedData) { RelayMutationTransaction._willBatchRefreshQueuedData = true; resolveImmediate(function () { RelayMutationTransaction._willBatchRefreshQueuedData = false; RelayMutationTransaction._refreshQueuedData(); }); } }; return RelayMutationTransaction; })(); module.exports = RelayMutationTransaction; // These are lazily computed and memoized. /* $FlowIssue #7728187 - Computed Property */