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

'use strict';

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

var _extends = require('babel-runtime/helpers/extends')['default'];

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

Object.defineProperty(exports, '__esModule', {
  value: true
});

var RelayDeprecated = require('./RelayDeprecated');
var RelayFragmentReference = require('./RelayFragmentReference');

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

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

var forEachObject = require('fbjs/lib/forEachObject');
var fromGraphQL = require('./fromGraphQL');
var invariant = require('fbjs/lib/invariant');
var warning = require('fbjs/lib/warning');

/**
 * @public
 *
 * RelaySubscription is the base class for modeling subscriptions of data.
 */

var RelaySubscription = (function () {
  function RelaySubscription(props) {
    _classCallCheck(this, RelaySubscription);

    this._didShowFakeDataWarning = false;
    this._resolveProps(props);
  }

  /**
   * Wrapper around `buildRQL.Fragment` with contextual error messages.
   */

  /**
   * Each subscription has a server name which is used by clients to communicate the
   * type of subscription that should be executed on the server.
   */

  RelaySubscription.prototype.getSubscription = function getSubscription() {
    !false ? process.env.NODE_ENV !== 'production' ? invariant(false, '%s: Expected abstract method `getSubscription` to be implemented.', this.constructor.name) : invariant(false) : undefined;
  };

  /**
   * "Fat queries" represent a predetermined set of fields that may change as a
   * result of a subscription, and which should therefore be queried in order to get
   * a consistent view of the data after performing a subscription. In practice, we
   * query for a subset of those fields because we intersect the fat query with
   * the tracked query we have for a given node (ie. the pieces of data we've
   * previously queried for and have therefore written to the store).
   *
   * Fat queries can be written like normal graphql queries with one main
   * exception: fat queries use childless non-scalar fields to indicate that
   * anything under that field may change. For example, the fat query for
   * feedback_like contains the field `like_sentence` with no child fields.
   * This means that any field below `like_sentence` may change as a result of
   * feedback_like.
   *
   * When adding a fat query, consider *all* of the data that might change as a
   * result of the subscription - not just data that we currently use in Relay. We
   * don't need to worry about overfetching here (this query is never executed
   * on its own; the infrastructure always intersects it with what is actually
   * needed), and if we omit fields here we might get odd consistency behavior
   * in the future when we add new views or modify existing ones.
   */

  RelaySubscription.prototype.getFatQuery = function getFatQuery() {
    !false ? process.env.NODE_ENV !== 'production' ? invariant(false, '%s: Expected abstract method `getFatQuery` to be implemented.', this.constructor.name) : invariant(false) : undefined;
  };

  /**
   * These configurations are used to generate the query for the subscription to be
   * sent to the server and to correctly write the server's response into the
   * client store.
   *
   * Possible configuration types:
   *
   * -  FIELDS_CHANGE provides configuration for subscription fields.
   *    {
   *      type: RelaySubscriptionType.FIELDS_CHANGE;
   *      fieldIDs: {[fieldName: string]: DataID | Array<DataID>};
   *    }
   *    where fieldIDs map `fieldName`s from the fatQuery to a DataID or
   *    array of DataIDs to be updated in the store.
   *
   * -  RANGE_ADD provides configuration for adding a new edge to a range.
   *    {
   *      type: RelaySubscriptionType.RANGE_ADD;
   *      parentName: string;
   *      parentID: string;
   *      connectionName: string;
   *      edgeName: string;
   *      rangeBehaviors:
   *        {[call: string]: GraphQLMutatorConstants.RANGE_OPERATIONS};
   *    }
   *    where `parentName` is the field in the fatQuery that contains the range,
   *    `parentID` is the DataID of `parentName` in the store, `connectionName`
   *    is the name of the range, `edgeName` is the name of the key in server
   *    response that contains the newly created edge, `rangeBehaviors` maps
   *    stringified representation of calls on the connection to
   *    GraphQLMutatorConstants.RANGE_OPERATIONS.
   *
   * -  NODE_DELETE provides configuration for deleting a node and the
   *    corresponding edge from a range.
   *    {
   *      type: RelaySubscriptionType.NODE_DELETE;
   *      parentName: string;
   *      parentID: string;
   *      connectionName: string;
   *      deletedIDFieldName: string;
   *    }
   *    where `parentName`, `parentID` and `connectionName` refer to the same
   *    things as in RANGE_ADD, `deletedIDFieldName` is the name of the key in
   *    the server response that contains the DataID of the deleted node.
   *
   * -  RANGE_DELETE provides configuration for deleting an edge from a range
   *    but doesn't delete the node.
   *    {
   *      type: RelaySubscriptionType.RANGE_DELETE;
   *      parentName: string;
   *      parentID: string;
   *      connectionName: string;
   *      deletedIDFieldName: string;
   *      pathToConnection: Array<string>;
   *    }
   *    where `parentName`, `parentID`, `connectionName` and
   *    `deletedIDFieldName` refer to the same things as in NODE_DELETE,
   *    `pathToConnection` provides a path from `parentName` to
   *    `connectionName`.
   *
   * -  REQUIRED_CHILDREN is used to append additional children (fragments or
   *    fields) to the subscription query. Any data fetched as a result of these
   *    children is not written to the client store. Please avoid using this.
   *    {
   *      type: RelaySubscriptionType.REQUIRED_CHILDREN;
   *      children: Array<RelayQuery.Node>;
   *    }
   */

  RelaySubscription.prototype.getConfigs = function getConfigs() {
    !false ? process.env.NODE_ENV !== 'production' ? invariant(false, '%s: Expected abstract method `getConfigs` to be implemented.', this.constructor.name) : invariant(false) : undefined;
  };

  /**
   * These variables form the "input" to the subscription query sent to the server.
   */

  RelaySubscription.prototype.getVariables = function getVariables() {
    !false ? process.env.NODE_ENV !== 'production' ? invariant(false, '%s: Expected abstract method `getVariables` to be implemented.', this.constructor.name) : invariant(false) : undefined;
  };

  /**
   * An optional collision key allows a subscription to identify itself with other
   * subscriptions that affect the same fields. Subscriptions with the same collision
   * are sent to the server serially and in-order to avoid unpredictable and
   * potentially incorrect behavior.
   */

  RelaySubscription.prototype.getCollisionKey = function getCollisionKey() {
    return null;
  };

  RelaySubscription.prototype._resolveProps = function _resolveProps(props) {
    var _this = this;

    var fragments = RelayDeprecated.getSubscriptionFragments(this.constructor);
    var initialVariables = RelayDeprecated.getSubscriptionInitialVariables(this.constructor) || {};

    var resolvedProps = _extends({}, props);
    forEachObject(fragments, function (fragmentBuilder, fragmentName) {
      var propValue = props[fragmentName];
      process.env.NODE_ENV !== 'production' ? warning(propValue !== undefined, 'RelaySubscription: Expected data for fragment `%s` to be supplied to ' + '`%s` as a prop. Pass an explicit `null` if this is intentional.', fragmentName, _this.constructor.name) : undefined;

      if (!propValue) {
        return;
      }

      var fragment = fromGraphQL.Fragment(buildSubscriptionFragment(_this.constructor.name, fragmentName, fragmentBuilder, initialVariables));
      var concreteFragmentID = fragment.getConcreteFragmentID();

      if (fragment.isPlural()) {
        !Array.isArray(propValue) ? process.env.NODE_ENV !== 'production' ? invariant(false, 'RelaySubscription: Invalid prop `%s` supplied to `%s`, expected an ' + 'array of records because the corresponding fragment is plural.', fragmentName, _this.constructor.name) : invariant(false) : undefined;
        var dataIDs = propValue.reduce(function (acc, item, ii) {
          var eachFragmentPointer = item[concreteFragmentID];
          !eachFragmentPointer ? process.env.NODE_ENV !== 'production' ? invariant(false, 'RelaySubscription: Invalid prop `%s` supplied to `%s`, ' + 'expected element at index %s to have query data.', fragmentName, _this.constructor.name, ii) : invariant(false) : undefined;
          return acc.concat(eachFragmentPointer.getDataIDs());
        }, []);

        resolvedProps[fragmentName] = RelayStore.readAll(fragment, dataIDs);
      } else {
        !!Array.isArray(propValue) ? process.env.NODE_ENV !== 'production' ? invariant(false, 'RelaySubscription: Invalid prop `%s` supplied to `%s`, expected a ' + 'single record because the corresponding fragment is not plural.', fragmentName, _this.constructor.name) : invariant(false) : undefined;
        var fragmentPointer = propValue[concreteFragmentID];
        if (fragmentPointer) {
          var dataID = fragmentPointer.getDataID();
          resolvedProps[fragmentName] = RelayStore.read(fragment, dataID);
        } else {
          if (process.env.NODE_ENV !== 'production') {
            if (!_this._didShowFakeDataWarning) {
              _this._didShowFakeDataWarning = true;
              process.env.NODE_ENV !== 'production' ? warning(false, 'RelaySubscription: Expected prop `%s` supplied to `%s` to ' + 'be data fetched by Relay. This is likely an error unless ' + 'you are purposely passing in mock data that conforms to ' + 'the shape of this subscription\'s fragment.', fragmentName, _this.constructor.name) : undefined;
            }
          }
        }
      }
    });
    this.props = resolvedProps;
  };

  RelaySubscription.getFragment = function getFragment(fragmentName, variableMapping) {
    var _this2 = this;

    // TODO: Unify fragment API for containers and mutations, #7860172.
    var fragments = RelayDeprecated.getSubscriptionFragments(this);
    var fragmentBuilder = fragments[fragmentName];
    if (!fragmentBuilder) {
      !false ? process.env.NODE_ENV !== 'production' ? invariant(false, '%s.getFragment(): `%s` is not a valid fragment name. Available ' + 'fragments names: %s', this.name, fragmentName, _Object$keys(fragments).map(function (name) {
        return '`' + name + '`';
      }).join(', ')) : invariant(false) : undefined;
    }

    // $FlowFixMe - Deprecated APIs.
    var processQueryParams = this.processQueryParams;
    if (processQueryParams && !this.prepareVariables) {
      RelayDeprecated.warn({
        was: this.name + '.getQuery',
        now: this.name + '.getFragment'
      });
      this.prepareVariables = function (prevVariables, route) {
        return processQueryParams(route, prevVariables);
      };
    }
    var initialVariables = RelayDeprecated.getSubscriptionInitialVariables(this) || {};
    var prepareVariables = this.prepareVariables;

    return RelayFragmentReference.createForContainer(function () {
      return buildSubscriptionFragment(_this2.name, fragmentName, fragmentBuilder, initialVariables);
    }, initialVariables, variableMapping, prepareVariables);
  };

  /**
   * @deprecated
   */

  RelaySubscription.getQuery = function getQuery() {
    RelayDeprecated.warn({
      was: this.name + '.getQuery',
      now: this.name + '.getFragment'
    });
    return this.getFragment.apply(this, arguments);
  };

  return RelaySubscription;
})();

function buildSubscriptionFragment(subscriptionName, fragmentName, fragmentBuilder, variables) {
  var fragment = buildRQL.Fragment(fragmentBuilder, variables);
  !fragment ? process.env.NODE_ENV !== 'production' ? invariant(false, 'Relay.QL defined on subscription `%s` named `%s` is not a valid fragment. ' + 'A typical fragment is defined using: Relay.QL`fragment on Type {...}`', subscriptionName, fragmentName) : invariant(false) : undefined;
  return fragment;
}

module.exports = RelaySubscription;