Source: sync.js

'use strict';

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

var _freeze = require('babel-runtime/core-js/object/freeze');

var _freeze2 = _interopRequireDefault(_freeze);

exports.default = pagedSync;

var _collection = require('lodash/collection');

var _lang = require('lodash/lang');

var _createRequestConfig = require('./create-request-config');

var _createRequestConfig2 = _interopRequireDefault(_createRequestConfig);

var _linkGetters = require('./mixins/link-getters');

var _linkGetters2 = _interopRequireDefault(_linkGetters);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

/**
 * @memberof Sync
 * @typedef SyncCollection
 * @prop {Array<Entities.Entry>} entries - All existing entries on first sync. New and updated entries on subsequent syncs.
 * @prop {Array<Entities.Asset>} assets - All existing assets on first sync. New and updated assets on subsequent syncs.
 * @prop {Array<Sync.DeletedEntry>} deletedEntries - List of deleted Entries since last sync
 * @prop {Array<Sync.DeletedAsset>} deletedAssets - List of deleted Assets since last sync
 * @prop {string} nextSyncToken - Token to be sent to the next sync call
 */

/**
 * Deleted Entries are the same as Entries, but only appear on the sync API.
 * @memberof Sync
 * @typedef DeletedEntry
 * @type Entities.Entry
 */

/**
 * Deleted Assets are the same as Assets, but only appear on the sync API.
 * @memberof Sync
 * @typedef DeletedAsset
 * @type Entities.Asset
 */

/**
 * This module retrieves all the available pages for a sync operation
 * @private
 * @param {Object} http - HTTP client
 * @param {Object} query - Query object
 * @param {boolean} resolveLinks - If links should be resolved
 * @return {Promise<SyncCollection>}
 */
/**
 * See <a href="https://www.contentful.com/developers/docs/concepts/sync/">Synchronization</a> for more information.
 * @namespace Sync
 */
function pagedSync(http, query, resolveLinks) {
  if (!query || !query.initial && !query.nextSyncToken) {
    throw new Error('Please provide one of `initial` or `nextSyncToken` parameters for syncing');
  }

  if (query && query.content_type && !query.type) {
    query.type = 'Entry';
  } else if (query && query.content_type && query.type && query.type !== 'Entry') {
    throw new Error('When using the `content_type` filter your `type` parameter cannot be different from `Entry`.');
  }

  if (query.nextSyncToken) {
    query.sync_token = query.nextSyncToken;
    delete query.initial;
    delete query.nextSyncToken;
  }

  return getSyncPage(http, [], query).then(function (response) {
    // clones response.items used in includes because we don't want these to be mutated
    if (resolveLinks) {
      (0, _linkGetters2.default)(response.items, mapIncludeItems((0, _lang.cloneDeep)(response.items)));
    }
    // maps response items again after getters are attached
    var mappedResponseItems = mapResponseItems(response.items);
    mappedResponseItems.nextSyncToken = response.nextSyncToken;
    return (0, _freeze2.default)(mappedResponseItems);
  }, function (error) {
    throw error.data;
  });
}

/**
 * @private
 * @param {Array<Entities.Entry|Entities.Array|Sync.DeletedEntry|Sync.DeletedAsset>} items
 * @return {Object} Entities mapped to an object for each entity type
 */
function mapResponseItems(items) {
  return {
    entries: (0, _collection.filter)(items, ['sys.type', 'Entry']),
    assets: (0, _collection.filter)(items, ['sys.type', 'Asset']),
    deletedEntries: (0, _collection.filter)(items, ['sys.type', 'DeletedEntry']),
    deletedAssets: (0, _collection.filter)(items, ['sys.type', 'DeletedAsset'])
  };
}

/**
 * Creates an object similar to the one retrieved on `includes` from the `entries`
 * endpoint, for usage with the link getters mixin
 * @private
 * @param {Array<Entities.Entry|Entities.Array|Sync.DeletedEntry|Sync.DeletedAsset>} items
 * @return {Object}
 */
function mapIncludeItems(items) {
  return {
    Entry: (0, _collection.filter)(items, ['sys.type', 'Entry']),
    Asset: (0, _collection.filter)(items, ['sys.type', 'Asset'])
  };
}

/**
 * If the response contains a nextPageUrl, extracts the sync token to get the
 * next page and calls itself again with that token.
 * Otherwise, if the response contains a nextSyncUrl, extracts the sync token
 * and returns it.
 * On each call of this function, any retrieved items are collected in the
 * supplied items array, which gets returned in the end
 * @private
 * @param {Object} http
 * @param {Array<Entities.Entry|Entities.Array|Sync.DeletedEntry|Sync.DeletedAsset>} items
 * @param {Object} query
 * @return {Promise<{items: Array, nextSyncToken: string}>}
 */
function getSyncPage(http, items, query) {
  return http.get('sync', (0, _createRequestConfig2.default)({ query: query })).then(function (response) {
    var data = response.data;
    items = items.concat(data.items);
    if (data.nextPageUrl) {
      delete query.initial;
      query.sync_token = getToken(data.nextPageUrl);
      return getSyncPage(http, items, query);
    } else if (data.nextSyncUrl) {
      return {
        items: items,
        nextSyncToken: getToken(data.nextSyncUrl)
      };
    }
  });
}

/**
 * Extracts token out of an url
 * @private
 */
function getToken(url) {
  var urlParts = url.split('?');
  return urlParts.length > 0 ? urlParts[1].replace('sync_token=', '') : '';
}