Why Gemfury? Push, build, and install  RubyGems npm packages Python packages Maven artifacts PHP packages Go Modules Bower components Debian packages RPM packages NuGet packages

pfchangs / react-relay   js

Repository URL to install this package:

Version: 0.7.1-ccinternal 

/ lib / RelayStoreData.js

/**
 * 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 RelayStoreData
 * 
 * @typechecks
 */

'use strict';

var _classCallCheck = require('babel-runtime/helpers/class-call-check')['default'];

var _Object$keys = require('babel-runtime/core-js/object/keys')['default'];

var GraphQLQueryRunner = require('./GraphQLQueryRunner');
var GraphQLStoreChangeEmitter = require('./GraphQLStoreChangeEmitter');
var GraphQLStoreDataHandler = require('./GraphQLStoreDataHandler');
var GraphQLStoreRangeUtils = require('./GraphQLStoreRangeUtils');
var RelayChangeTracker = require('./RelayChangeTracker');

var RelayConnectionInterface = require('./RelayConnectionInterface');

var RelayGarbageCollector = require('./RelayGarbageCollector');
var RelayMutationQueue = require('./RelayMutationQueue');

var RelayNodeInterface = require('./RelayNodeInterface');
var RelayPendingQueryTracker = require('./RelayPendingQueryTracker');
var RelayProfiler = require('./RelayProfiler');
var RelayQuery = require('./RelayQuery');
var RelayQueryTracker = require('./RelayQueryTracker');
var RelayQueryWriter = require('./RelayQueryWriter');
var RelayRecordStore = require('./RelayRecordStore');

var forEachObject = require('fbjs/lib/forEachObject');
var invariant = require('fbjs/lib/invariant');
var generateForceIndex = require('./generateForceIndex');
var readRelayDiskCache = require('./readRelayDiskCache');
var warning = require('fbjs/lib/warning');
var writeRelayQueryPayload = require('./writeRelayQueryPayload');
var writeRelayUpdatePayload = require('./writeRelayUpdatePayload');

var CLIENT_MUTATION_ID = RelayConnectionInterface.CLIENT_MUTATION_ID;
var NODE_TYPE = RelayNodeInterface.NODE_TYPE;

// The source of truth for application data.
var _instance;

/**
 * @internal
 *
 * Wraps the data caches and associated metadata tracking objects used by
 * GraphQLStore/RelayStore.
 */

var RelayStoreData = (function () {

  /**
   * Get the data set backing actual Relay operations. Used in GraphQLStore.
   */

  RelayStoreData.getDefaultInstance = function getDefaultInstance() {
    if (!_instance) {
      _instance = new RelayStoreData();
    }
    return _instance;
  };

  function RelayStoreData() {
    _classCallCheck(this, RelayStoreData);

    var cachedRecords = {};
    var cachedRootCallMap = {};
    var queuedRecords = {};
    var records = {};
    var rootCallMap = {};
    var nodeRangeMap = {};

    var _createRecordCollection = createRecordCollection({
      cachedRecords: cachedRecords,
      cachedRootCallMap: cachedRootCallMap,
      cacheWriter: null,
      queuedRecords: queuedRecords,
      nodeRangeMap: nodeRangeMap,
      records: records,
      rootCallMap: rootCallMap
    });

    var cachedStore = _createRecordCollection.cachedStore;
    var queuedStore = _createRecordCollection.queuedStore;
    var recordStore = _createRecordCollection.recordStore;

    var rangeData = new GraphQLStoreRangeUtils();

    this._cacheManager = null;
    this._cachedRecords = cachedRecords;
    this._cachedRootCallMap = cachedRootCallMap;
    this._cachedStore = cachedStore;
    this._changeEmitter = new GraphQLStoreChangeEmitter(rangeData);
    this._mutationQueue = new RelayMutationQueue(this);
    this._nodeRangeMap = nodeRangeMap;
    this._pendingQueryTracker = new RelayPendingQueryTracker(this);
    this._queryRunner = new GraphQLQueryRunner(this);
    this._queryTracker = new RelayQueryTracker();
    this._queuedRecords = queuedRecords;
    this._queuedStore = queuedStore;
    this._records = records;
    this._recordStore = recordStore;
    this._rangeData = rangeData;
    this._rootCallMap = rootCallMap;
  }

  /**
   * Creates a garbage collector for this instance. After initialization all
   * newly added DataIDs will be registered in the created garbage collector.
   * This will show a warning if data has already been added to the instance.
   */

  RelayStoreData.prototype.initializeGarbageCollector = function initializeGarbageCollector(scheduler) {
    !!this._garbageCollector ? process.env.NODE_ENV !== 'production' ? invariant(false, 'RelayStoreData: Garbage collector is already initialized.') : invariant(false) : undefined;
    var shouldInitialize = this._isStoreDataEmpty();
    process.env.NODE_ENV !== 'production' ? warning(shouldInitialize, 'RelayStoreData: Garbage collection can only be initialized when no ' + 'data is present.') : undefined;
    if (shouldInitialize) {
      this._garbageCollector = new RelayGarbageCollector(this, scheduler);
    }
  };

  /**
   * Sets/clears the cache manager that is used to cache changes written to
   * the store.
   */

  RelayStoreData.prototype.injectCacheManager = function injectCacheManager(cacheManager) {
    var _createRecordCollection2 = createRecordCollection({
      cachedRecords: this._cachedRecords,
      cachedRootCallMap: this._cachedRootCallMap,
      cacheWriter: cacheManager ? cacheManager.getQueryWriter() : null,
      queuedRecords: this._queuedRecords,
      nodeRangeMap: this._nodeRangeMap,
      records: this._records,
      rootCallMap: this._rootCallMap
    });

    var cachedStore = _createRecordCollection2.cachedStore;
    var queuedStore = _createRecordCollection2.queuedStore;
    var recordStore = _createRecordCollection2.recordStore;

    this._cacheManager = cacheManager;
    this._cachedStore = cachedStore;
    this._queuedStore = queuedStore;
    this._recordStore = recordStore;
  };

  RelayStoreData.prototype.clearCacheManager = function clearCacheManager() {
    var _createRecordCollection3 = createRecordCollection({
      cachedRecords: this._cachedRecords,
      cachedRootCallMap: this._cachedRootCallMap,
      cacheWriter: null,
      queuedRecords: this._queuedRecords,
      nodeRangeMap: this._nodeRangeMap,
      records: this._records,
      rootCallMap: this._rootCallMap
    });

    var cachedStore = _createRecordCollection3.cachedStore;
    var queuedStore = _createRecordCollection3.queuedStore;
    var recordStore = _createRecordCollection3.recordStore;

    this._cacheManager = null;
    this._cachedStore = cachedStore;
    this._queuedStore = queuedStore;
    this._recordStore = recordStore;
  };

  RelayStoreData.prototype.hasCacheManager = function hasCacheManager() {
    return !!this._cacheManager;
  };

  /**
   * Returns whether a given record is affected by an optimistic update.
   */

  RelayStoreData.prototype.hasOptimisticUpdate = function hasOptimisticUpdate(dataID) {
    dataID = this.getRangeData().getCanonicalClientID(dataID);
    return this.getQueuedStore().hasOptimisticUpdate(dataID);
  };

  /**
   * Returns a list of client mutation IDs for queued mutations whose optimistic
   * updates are affecting the record corresponding the given dataID. Returns
   * null if the record isn't affected by any optimistic updates.
   */

  RelayStoreData.prototype.getClientMutationIDs = function getClientMutationIDs(dataID) {
    dataID = this.getRangeData().getCanonicalClientID(dataID);
    return this.getQueuedStore().getClientMutationIDs(dataID);
  };

  /**
   * Reads data for queries incrementally from disk cache.
   * It calls onSuccess when all the data has been loaded into memory.
   * It calls onFailure when some data is unabled to be satisfied from disk.
   */

  RelayStoreData.prototype.readFromDiskCache = function readFromDiskCache(queries, callbacks) {
    var _this = this;

    var cacheManager = this._cacheManager;
    !cacheManager ? process.env.NODE_ENV !== 'production' ? invariant(false, 'RelayStoreData: `readFromDiskCache` should only be called when cache ' + 'manager is available.') : invariant(false) : undefined;
    var changeTracker = new RelayChangeTracker();
    var profile = RelayProfiler.profile('RelayStoreData.readFromDiskCache');
    readRelayDiskCache(queries, this._queuedStore, this._cachedRecords, this._cachedRootCallMap, cacheManager, changeTracker, {
      onSuccess: function onSuccess() {
        _this._handleChangedAndNewDataIDs(changeTracker.getChangeSet());
        profile.stop();
        callbacks.onSuccess && callbacks.onSuccess();
      },
      onFailure: function onFailure() {
        _this._handleChangedAndNewDataIDs(changeTracker.getChangeSet());
        profile.stop();
        callbacks.onFailure && callbacks.onFailure();
      }
    });
  };

  /**
   * Write the results of a query into the base record store.
   */

  RelayStoreData.prototype.handleQueryPayload = function handleQueryPayload(query, response, forceIndex) {
    var profiler = RelayProfiler.profile('RelayStoreData.handleQueryPayload');
    var changeTracker = new RelayChangeTracker();
    var writer = new RelayQueryWriter(this._recordStore, this._queryTracker, changeTracker, {
      forceIndex: forceIndex,
      updateTrackedQueries: true
    });
    writeRelayQueryPayload(writer, query, response);
    this._handleChangedAndNewDataIDs(changeTracker.getChangeSet());
    profiler.stop();
  };

  /**
   * Write the results of an update into the base record store.
   */

  RelayStoreData.prototype.handleUpdatePayload = function handleUpdatePayload(operation, payload, _ref) {
    var configs = _ref.configs;
    var isOptimisticUpdate = _ref.isOptimisticUpdate;

    var profiler = RelayProfiler.profile('RelayStoreData.handleUpdatePayload');
    var changeTracker = new RelayChangeTracker();
    var store;
    if (isOptimisticUpdate) {
      var clientMutationID = payload[CLIENT_MUTATION_ID];
      !(typeof clientMutationID === 'string') ? process.env.NODE_ENV !== 'production' ? invariant(false, 'RelayStoreData.handleUpdatePayload(): Expected optimistic payload ' + 'to have a valid `%s`.', CLIENT_MUTATION_ID) : invariant(false) : undefined;
      store = this.getRecordStoreForOptimisticMutation(clientMutationID);
    } else {
      store = this._getRecordStoreForMutation();
    }
    var writer = new RelayQueryWriter(store, this._queryTracker, changeTracker, {
      forceIndex: generateForceIndex(),
      isOptimisticUpdate: isOptimisticUpdate,
      updateTrackedQueries: false
    });
    writeRelayUpdatePayload(writer, operation, payload, { configs: configs, isOptimisticUpdate: isOptimisticUpdate });
    this._handleChangedAndNewDataIDs(changeTracker.getChangeSet());
    profiler.stop();
  };

  /**
   * Given a query fragment and a data ID, returns a root query that applies
   * the fragment to the object specified by the data ID.
   */

  RelayStoreData.prototype.buildFragmentQueryForDataID = function buildFragmentQueryForDataID(fragment, dataID) {
    if (GraphQLStoreDataHandler.isClientID(dataID)) {
      var path = this._queuedStore.getPathToRecord(this._rangeData.getCanonicalClientID(dataID));
      !path ? process.env.NODE_ENV !== 'production' ? invariant(false, 'RelayStoreData.buildFragmentQueryForDataID(): Cannot refetch ' + 'record `%s` without a path.', dataID) : invariant(false) : undefined;
      return path.getQuery(fragment);
    }
    // Fragment fields cannot be spread directly into the root because they
    // may not exist on the `Node` type.
    return RelayQuery.Root.build(fragment.getDebugName() || 'UnknownQuery', RelayNodeInterface.NODE, dataID, [fragment], { identifyingArgName: RelayNodeInterface.ID }, NODE_TYPE);
  };

  RelayStoreData.prototype.getNodeData = function getNodeData() {
    return this._records;
  };

  RelayStoreData.prototype.getQueuedData = function getQueuedData() {
    return this._queuedRecords;
  };

  RelayStoreData.prototype.clearQueuedData = function clearQueuedData() {
    var _this2 = this;

    forEachObject(this._queuedRecords, function (_, key) {
      delete _this2._queuedRecords[key];
      _this2._changeEmitter.broadcastChangeForID(key);
    });
  };

  RelayStoreData.prototype.getCachedData = function getCachedData() {
    return this._cachedRecords;
  };

  RelayStoreData.prototype.getGarbageCollector = function getGarbageCollector() {
    return this._garbageCollector;
  };

  RelayStoreData.prototype.getMutationQueue = function getMutationQueue() {
    return this._mutationQueue;
  };

  /**
   * Get the record store with only the cached and base data (no queued data).
   */

  RelayStoreData.prototype.getCachedStore = function getCachedStore() {
    return this._cachedStore;
  };

  /**
   * Get the record store with full data (cached, base, queued).
   */

  RelayStoreData.prototype.getQueuedStore = function getQueuedStore() {
    return this._queuedStore;
  };

  /**
   * Get the record store with only the base data (no queued/cached data).
   */

  RelayStoreData.prototype.getRecordStore = function getRecordStore() {
    return this._recordStore;
  };

  RelayStoreData.prototype.getQueryTracker = function getQueryTracker() {
    return this._queryTracker;
  };

  RelayStoreData.prototype.getQueryRunner = function getQueryRunner() {
    return this._queryRunner;
  };

  RelayStoreData.prototype.getChangeEmitter = function getChangeEmitter() {
    return this._changeEmitter;
  };

  RelayStoreData.prototype.getRangeData = function getRangeData() {
    return this._rangeData;
  };

  RelayStoreData.prototype.getPendingQueryTracker = function getPendingQueryTracker() {
    return this._pendingQueryTracker;
  };

  /**
   * @deprecated
   *
   * Used temporarily by GraphQLStore, but all updates to this object are now
   * handled through a `RelayRecordStore` instance.
   */

  RelayStoreData.prototype.getRootCallData = function getRootCallData() {
    return this._rootCallMap;
  };

  RelayStoreData.prototype._isStoreDataEmpty = function _isStoreDataEmpty() {
    return _Object$keys(this._records).length === 0 && _Object$keys(this._queuedRecords).length === 0 && _Object$keys(this._cachedRecords).length === 0;
  };

  /**
   * Given a ChangeSet, broadcasts changes for updated DataIDs
   * and registers new DataIDs with the garbage collector.
   */

  RelayStoreData.prototype._handleChangedAndNewDataIDs = function _handleChangedAndNewDataIDs(changeSet) {
    var _this3 = this;

    var updatedDataIDs = _Object$keys(changeSet.updated);
    updatedDataIDs.forEach(function (id) {
      return _this3._changeEmitter.broadcastChangeForID(id);
    });
    if (this._garbageCollector) {
      var createdDataIDs = _Object$keys(changeSet.created);
      var garbageCollector = this._garbageCollector;
      createdDataIDs.forEach(function (dataID) {
        return garbageCollector.register(dataID);
      });
    }
  };

  RelayStoreData.prototype._getRecordStoreForMutation = function _getRecordStoreForMutation() {
    var records = this._records;
    var rootCallMap = this._rootCallMap;

    return new RelayRecordStore({ records: records }, { rootCallMap: rootCallMap }, this._nodeRangeMap, this._cacheManager ? this._cacheManager.getMutationWriter() : null);
  };

  RelayStoreData.prototype.getRecordStoreForOptimisticMutation = function getRecordStoreForOptimisticMutation(clientMutationID) {
    var cachedRecords = this._cachedRecords;
    var cachedRootCallMap = this._cachedRootCallMap;
    var rootCallMap = this._rootCallMap;
    var queuedRecords = this._queuedRecords;
    var records = this._records;

    return new RelayRecordStore({ cachedRecords: cachedRecords, queuedRecords: queuedRecords, records: records }, { cachedRootCallMap: cachedRootCallMap, rootCallMap: rootCallMap }, this._nodeRangeMap, null, // don't cache optimistic data
    clientMutationID);
  };

  return RelayStoreData;
})();

function createRecordCollection(_ref2) {
  var cachedRecords = _ref2.cachedRecords;
  var cachedRootCallMap = _ref2.cachedRootCallMap;
  var cacheWriter = _ref2.cacheWriter;
  var queuedRecords = _ref2.queuedRecords;
  var nodeRangeMap = _ref2.nodeRangeMap;
  var records = _ref2.records;
  var rootCallMap = _ref2.rootCallMap;

  return {
    queuedStore: new RelayRecordStore({ cachedRecords: cachedRecords, queuedRecords: queuedRecords, records: records }, { cachedRootCallMap: cachedRootCallMap, rootCallMap: rootCallMap }, nodeRangeMap),
    cachedStore: new RelayRecordStore({ cachedRecords: cachedRecords, records: records }, { cachedRootCallMap: cachedRootCallMap, rootCallMap: rootCallMap }, nodeRangeMap, cacheWriter),
    recordStore: new RelayRecordStore({ records: records }, { rootCallMap: rootCallMap }, nodeRangeMap, cacheWriter)
  };
}

RelayProfiler.instrumentMethods(RelayStoreData.prototype, {
  handleQueryPayload: 'RelayStoreData.prototype.handleQueryPayload',
  handleUpdatePayload: 'RelayStoreData.prototype.handleUpdatePayload'
});

module.exports = RelayStoreData;