'use strict';

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.clearRecord = exports.detachSubrecord = exports.saveRecordWithTransition = exports.transitionRecord = exports.deleteRecord = exports.revertRecord = exports.setFieldValue = exports.moveFieldValue = exports.deleteFieldValue = exports.sortFieldInstances = exports.addFieldInstance = exports.saveRecord = exports.createNewSubrecord = exports.createNewRecord = exports.readRecord = exports.validateRecordData = exports.validateFieldValue = exports.computeRecordData = exports.computeFieldValue = exports.DETACH_SUBRECORD = exports.VALIDATION_PASSED = exports.VALIDATION_FAILED = exports.REVERT_RECORD = exports.SET_FIELD_VALUE = exports.MOVE_FIELD_VALUE = exports.DELETE_FIELD_VALUE = exports.SORT_FIELD_INSTANCES = exports.ADD_FIELD_INSTANCE = exports.RECORD_TRANSITION_REJECTED = exports.RECORD_TRANSITION_FULFILLED = exports.RECORD_TRANSITION_STARTED = exports.RECORD_SAVE_REJECTED = exports.RECORD_SAVE_FULFILLED = exports.RECORD_SAVE_STARTED = exports.SUBRECORD_READ_FULFILLED = exports.RECORD_READ_REJECTED = exports.RECORD_READ_FULFILLED = exports.RECORD_READ_STARTED = exports.RECORD_DELETE_REJECTED = exports.RECORD_DELETE_FULFILLED = exports.RECORD_DELETE_STARTED = exports.SUBRECORD_CREATED = exports.RECORD_CREATED = exports.FIELD_COMPUTE_REJECTED = exports.FIELD_COMPUTE_FULFILLED = exports.CREATE_NEW_SUBRECORD = exports.CREATE_NEW_RECORD = exports.CLEAR_RECORD = undefined;

var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }();

var _reactIntl = require('react-intl');

var _get = require('lodash/get');

var _get2 = _interopRequireDefault(_get);

var _merge = require('lodash/merge');

var _merge2 = _interopRequireDefault(_merge);

var _immutable = require('immutable');

var _immutable2 = _interopRequireDefault(_immutable);

var _cspace = require('./cspace');

var _cspace2 = _interopRequireDefault(_cspace);

var _notificationHelpers = require('../helpers/notificationHelpers');

var _notificationHelpers2 = _interopRequireDefault(_notificationHelpers);

var _getErrorDescription = require('../helpers/getErrorDescription');

var _getErrorDescription2 = _interopRequireDefault(_getErrorDescription);

var _HierarchyReparentNotifier = require('../components/record/HierarchyReparentNotifier');

var _HierarchyReparentNotifier2 = _interopRequireDefault(_HierarchyReparentNotifier);

var _notification = require('./notification');

var _search = require('./search');

var _reducers = require('../reducers');

var _configHelpers = require('../helpers/configHelpers');

var _recordDataHelpers = require('../helpers/recordDataHelpers');

var _searchHelpers = require('../helpers/searchHelpers');

var _errorCodes = require('../constants/errorCodes');

var _notificationStatusCodes = require('../constants/notificationStatusCodes');

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

function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } } /* global window */

var deleteMessages = (0, _reactIntl.defineMessages)({
  deleting: {
    'id': 'action.record.deleting',
    'defaultMessage': '{hasTitle, select, yes {Deleting {title}\u2026} other {Deleting record\u2026}}'
  },
  errorDeleting: {
    'id': 'action.record.errorDeleting',
    'defaultMessage': '{hasTitle, select, yes {Error deleting {title}: {error}} other {Error deleting record: {error}}}'
  },
  deleted: {
    'id': 'action.record.deleted',
    'defaultMessage': '{hasTitle, select, yes {Deleted {title}} other {Deleted record}}'
  }
});

var saveMessages = (0, _reactIntl.defineMessages)({
  saving: {
    'id': 'action.record.saving',
    'defaultMessage': '{hasTitle, select, yes {Saving {title}\u2026} other {Saving record\u2026}}'
  },
  errorSaving: {
    'id': 'action.record.errorSaving',
    'defaultMessage': '{hasTitle, select, yes {Error saving {title}: {error}} other {Error saving record: {error}}}'
  },
  saved: {
    'id': 'action.record.saved',
    'defaultMessage': '{hasTitle, select, yes {Saved {title}} other {Saved record}}'
  }
});

var transitionMessages = {
  delete: (0, _reactIntl.defineMessages)({
    transitioning: {
      'id': 'action.record.transition.delete.transitioning',
      'defaultMessage': '{hasTitle, select, yes {Deleting {title}\u2026} other {Deleting record\u2026}}'
    },
    errorTransitioning: {
      'id': 'action.record.transition.delete.errorTransitioning',
      'defaultMessage': '{hasTitle, select, yes {Error deleting {title}: {error}} other {Error deleting record: {error}}}'
    },
    transitioned: {
      'id': 'action.record.transition.delete.transitioned',
      'defaultMessage': '{hasTitle, select, yes {Deleted {title}} other {Deleted record}}'
    }
  }),
  lock: (0, _reactIntl.defineMessages)({
    transitioning: {
      'id': 'action.record.transition.lock.transitioning',
      'defaultMessage': '{hasTitle, select, yes {Locking {title}\u2026} other {Locking record\u2026}}'
    },
    errorTransitioning: {
      'id': 'action.record.transition.lock.errorTransitioning',
      'defaultMessage': '{hasTitle, select, yes {Error locking {title}: {error}} other {Error locking record: {error}}}'
    },
    transitioned: {
      'id': 'action.record.transition.lock.transitioned',
      'defaultMessage': '{hasTitle, select, yes {Locked {title}} other {Locked record}}'
    }
  })
};

var CLEAR_RECORD = exports.CLEAR_RECORD = 'CLEAR_RECORD';
var CREATE_NEW_RECORD = exports.CREATE_NEW_RECORD = 'CREATE_NEW_RECORD';
var CREATE_NEW_SUBRECORD = exports.CREATE_NEW_SUBRECORD = 'CREATE_NEW_SUBRECORD';
var FIELD_COMPUTE_FULFILLED = exports.FIELD_COMPUTE_FULFILLED = 'FIELD_COMPUTE_FULFILLED';
var FIELD_COMPUTE_REJECTED = exports.FIELD_COMPUTE_REJECTED = 'FIELD_COMPUTE_REJECTED';
var RECORD_CREATED = exports.RECORD_CREATED = 'RECORD_CREATED';
var SUBRECORD_CREATED = exports.SUBRECORD_CREATED = 'SUBRECORD_CREATED';
var RECORD_DELETE_STARTED = exports.RECORD_DELETE_STARTED = 'RECORD_DELETE_STARTED';
var RECORD_DELETE_FULFILLED = exports.RECORD_DELETE_FULFILLED = 'RECORD_DELETE_FULFILLED';
var RECORD_DELETE_REJECTED = exports.RECORD_DELETE_REJECTED = 'RECORD_DELETE_REJECTED';
var RECORD_READ_STARTED = exports.RECORD_READ_STARTED = 'RECORD_READ_STARTED';
var RECORD_READ_FULFILLED = exports.RECORD_READ_FULFILLED = 'RECORD_READ_FULFILLED';
var RECORD_READ_REJECTED = exports.RECORD_READ_REJECTED = 'RECORD_READ_REJECTED';
var SUBRECORD_READ_FULFILLED = exports.SUBRECORD_READ_FULFILLED = 'SUBRECORD_READ_FULFILLED';
var RECORD_SAVE_STARTED = exports.RECORD_SAVE_STARTED = 'RECORD_SAVE_STARTED';
var RECORD_SAVE_FULFILLED = exports.RECORD_SAVE_FULFILLED = 'RECORD_SAVE_FULFILLED';
var RECORD_SAVE_REJECTED = exports.RECORD_SAVE_REJECTED = 'RECORD_SAVE_REJECTED';
var RECORD_TRANSITION_STARTED = exports.RECORD_TRANSITION_STARTED = 'RECORD_TRANSITION_STARTED';
var RECORD_TRANSITION_FULFILLED = exports.RECORD_TRANSITION_FULFILLED = 'RECORD_TRANSITION_FULFILLED';
var RECORD_TRANSITION_REJECTED = exports.RECORD_TRANSITION_REJECTED = 'RECORD_TRANSITION_REJECTED';
var ADD_FIELD_INSTANCE = exports.ADD_FIELD_INSTANCE = 'ADD_FIELD_INSTANCE';
var SORT_FIELD_INSTANCES = exports.SORT_FIELD_INSTANCES = 'SORT_FIELD_INSTANCES';
var DELETE_FIELD_VALUE = exports.DELETE_FIELD_VALUE = 'DELETE_FIELD_VALUE';
var MOVE_FIELD_VALUE = exports.MOVE_FIELD_VALUE = 'MOVE_FIELD_VALUE';
var SET_FIELD_VALUE = exports.SET_FIELD_VALUE = 'SET_FIELD_VALUE';
var REVERT_RECORD = exports.REVERT_RECORD = 'REVERT_RECORD';
var VALIDATION_FAILED = exports.VALIDATION_FAILED = 'VALIDATION_FAILED';
var VALIDATION_PASSED = exports.VALIDATION_PASSED = 'VALIDATION_PASSED';
var DETACH_SUBRECORD = exports.DETACH_SUBRECORD = 'DETACH_SUBRECORD';

var computeFieldValue = exports.computeFieldValue = function computeFieldValue(recordTypeConfig, csid, path, value) {
  return function (dispatch, getState) {
    var fieldDescriptor = (0, _get2.default)(recordTypeConfig, ['fields'].concat(_toConsumableArray((0, _configHelpers.dataPathToFieldDescriptorPath)(path))));
    var recordData = (0, _reducers.getRecordData)(getState(), csid);

    return (0, _recordDataHelpers.computeField)(value, [], recordData, fieldDescriptor, true).then(function (computedValue) {
      if (typeof computedValue !== 'undefined') {
        dispatch({
          type: FIELD_COMPUTE_FULFILLED,
          payload: computedValue,
          meta: {
            csid: csid,
            path: path
          }
        });
      }
    }).catch(function (error) {
      dispatch({
        type: FIELD_COMPUTE_REJECTED,
        payload: {
          code: _errorCodes.ERR_COMPUTE,
          error: error
        },
        meta: {
          csid: csid,
          path: path
        }
      });

      // TODO: Show an error notification?
    });
  };
};

var computeRecordData = exports.computeRecordData = function computeRecordData(recordTypeConfig, csid) {
  return function (dispatch, getState) {
    var recordData = (0, _reducers.getRecordData)(getState(), csid);

    return dispatch(computeFieldValue(recordTypeConfig, csid, [], recordData));
  };
};

var validateFieldValue = exports.validateFieldValue = function validateFieldValue(recordTypeConfig, csid, path, value) {
  return function (dispatch, getState) {
    var fieldDescriptor = (0, _get2.default)(recordTypeConfig, ['fields'].concat(_toConsumableArray((0, _configHelpers.dataPathToFieldDescriptorPath)(path))));
    var recordData = (0, _reducers.getRecordData)(getState(), csid);

    return (0, _recordDataHelpers.validateField)(value, [], recordData, fieldDescriptor, true).then(function (errors) {
      if (errors) {
        dispatch({
          type: VALIDATION_FAILED,
          payload: errors,
          meta: {
            csid: csid,
            path: path
          }
        });

        dispatch((0, _notification.showValidationNotification)(recordTypeConfig.name, csid));
      } else {
        dispatch({
          type: VALIDATION_PASSED,
          meta: {
            csid: csid,
            path: path
          }
        });

        dispatch((0, _notification.removeValidationNotification)());
      }
    });
  };
};

var validateRecordData = exports.validateRecordData = function validateRecordData(recordTypeConfig, csid) {
  return function (dispatch, getState) {
    var recordData = (0, _reducers.getRecordData)(getState(), csid);

    return dispatch(validateFieldValue(recordTypeConfig, csid, [], recordData));
  };
};

var initializeSubrecords = function initializeSubrecords(config, recordTypeConfig, vocabularyConfig, csid) {
  return function (dispatch, getState) {
    var subrecords = recordTypeConfig.subrecords;


    if (!subrecords) {
      return Promise.resolve();
    }

    var data = (0, _reducers.getRecordData)(getState(), csid);

    return Promise.all(Object.entries(subrecords).map(function (entry) {
      var _entry = _slicedToArray(entry, 2),
          subrecordName = _entry[0],
          subrecordConfig = _entry[1];

      var csidField = subrecordConfig.csidField,
          subresource = subrecordConfig.subresource,
          subrecordType = subrecordConfig.recordType,
          subrecordVocabulary = subrecordConfig.vocabulary;


      var subrecordCsidPromise = null;

      if (csidField) {
        subrecordCsidPromise = Promise.resolve((0, _recordDataHelpers.deepGet)(data, csidField));
      } else if (subresource) {
        var searchDescriptor = _immutable2.default.fromJS({
          csid: csid,
          subresource: subresource,
          recordType: recordTypeConfig.name,
          vocabulary: vocabularyConfig.name,
          searchQuery: {
            // Set page size to 1, and page to 0. This assumes we'll only ever care about the first
            // result.
            p: 0,
            size: 1
          }
        });

        var searchName = (0, _searchHelpers.getSubrecordSearchName)(csid, subrecordName);

        var listType = subresource ? (0, _get2.default)(config, ['subresources', subresource, 'listType']) : null;

        subrecordCsidPromise = dispatch((0, _search.search)(config, searchName, searchDescriptor, listType)).then(function () {
          var result = (0, _reducers.getSearchResult)(getState(), searchName, searchDescriptor);

          var subrecordCsid = void 0;

          if (result) {
            // Read the csid of the first item.

            var firstItem = (0, _searchHelpers.getFirstItem)(config, result, listType);

            if (firstItem) {
              subrecordCsid = firstItem.get('csid');
            }
          }

          return subrecordCsid;
        });
      }

      if (subrecordCsidPromise) {
        return subrecordCsidPromise.then(function (subrecordCsid) {
          var subrecordTypeConfig = (0, _get2.default)(config, ['recordTypes', subrecordType]);

          var subrecordVocabularyConfig = (0, _get2.default)(subrecordTypeConfig, ['vocabularies', subrecordVocabulary]);

          if (subrecordCsid) {
            return (
              // eslint-disable-next-line no-use-before-define
              dispatch(readRecord(config, subrecordTypeConfig, subrecordVocabularyConfig, subrecordCsid)).then(function () {
                return dispatch({
                  type: SUBRECORD_READ_FULFILLED,
                  meta: {
                    csid: csid,
                    subrecordCsid: subrecordCsid,
                    subrecordName: subrecordName
                  }
                });
              })
            );
          }

          // No existing subrecord. Create one as a default.

          // eslint-disable-next-line no-use-before-define
          return dispatch(createNewSubrecord(config, csid, csidField, subrecordName, subrecordTypeConfig, subrecordVocabularyConfig, undefined, true));
        });
      }

      return Promise.resolve();
    }));
  };
};

var doRead = function doRead(recordTypeConfig, vocabularyConfig, csid) {
  var _recordTypeConfig$ser = recordTypeConfig.serviceConfig,
      serviceType = _recordTypeConfig$ser.serviceType,
      recordServicePath = _recordTypeConfig$ser.servicePath;


  var vocabularyServicePath = vocabularyConfig ? vocabularyConfig.serviceConfig.servicePath : null;

  var pathParts = [recordServicePath];

  if (vocabularyServicePath) {
    pathParts.push(vocabularyServicePath);
    pathParts.push('items');
  }

  pathParts.push(csid);

  var path = pathParts.join('/');

  var requestConfig = {
    params: {
      wf_deleted: false
    }
  };

  if (serviceType === 'authority' || serviceType === 'object') {
    requestConfig.params.showRelations = true;
  }

  if (recordTypeConfig.requestConfig) {
    (0, _merge2.default)(requestConfig, recordTypeConfig.requestConfig('read'));
  }

  return (0, _cspace2.default)().read(path, requestConfig);
};

var readRecord = exports.readRecord = function readRecord(config, recordTypeConfig, vocabularyConfig, csid) {
  return function (dispatch, getState) {
    if ((0, _reducers.isRecordReadPending)(getState(), csid) || (0, _reducers.getRecordData)(getState(), csid)) {
      // We already have data for this record, or a request is already pending. Do nothing.

      return Promise.resolve();
    }

    dispatch({
      type: RECORD_READ_STARTED,
      meta: {
        recordTypeConfig: recordTypeConfig,
        csid: csid
      }
    });

    return doRead(recordTypeConfig, vocabularyConfig, csid).then(function (response) {
      return dispatch({
        type: RECORD_READ_FULFILLED,
        payload: response,
        meta: {
          config: config,
          recordTypeConfig: recordTypeConfig,
          csid: csid
        }
      });
    }).then(function () {
      return dispatch(initializeSubrecords(config, recordTypeConfig, vocabularyConfig, csid));
    }).catch(function (error) {
      return dispatch({
        type: RECORD_READ_REJECTED,
        payload: {
          code: _errorCodes.ERR_API,
          error: error
        },
        meta: {
          recordTypeConfig: recordTypeConfig,
          csid: csid
        }
      });
    });
  };
};

var createNewRecord = exports.createNewRecord = function createNewRecord(config, recordTypeConfig, vocabularyConfig, cloneCsid) {
  return function (dispatch, getState) {
    var readClone = void 0;

    if (cloneCsid) {
      var data = (0, _reducers.getRecordData)(getState(), cloneCsid);

      if (!data) {
        // We don't have data for the record to be cloned. Read it first.

        readClone = dispatch(readRecord(config, recordTypeConfig, vocabularyConfig, cloneCsid));
      }
    }

    if (!readClone) {
      // There's nothing to clone, or we already have the record data to be cloned. Perform an
      // async noop, so this function will be consistently async.

      readClone = new Promise(function (resolve) {
        window.setTimeout(function () {
          resolve();
        }, 0);
      });
    }

    return readClone.then(function () {
      return dispatch({
        type: CREATE_NEW_RECORD,
        meta: {
          config: config,
          recordTypeConfig: recordTypeConfig,
          cloneCsid: cloneCsid
        }
      });
    });
  };
};

var createNewSubrecord = exports.createNewSubrecord = function createNewSubrecord(config, csid, csidField, subrecordName, subrecordTypeConfig, subrecordVocabularyConfig, cloneCsid, isDefault) {
  return function (dispatch, getState) {
    var readClone = void 0;

    if (cloneCsid) {
      var data = (0, _reducers.getRecordData)(getState(), cloneCsid);

      if (!data) {
        // We don't have data for the record to be cloned. Read it first.

        readClone = dispatch(readRecord(config, subrecordTypeConfig, subrecordVocabularyConfig, cloneCsid));
      }
    }

    if (!readClone) {
      // There's nothing to clone, or we already have the record data to be cloned. Perform an
      // async noop, so this function will be consistently async.

      readClone = new Promise(function (resolve) {
        window.setTimeout(function () {
          resolve();
        }, 0);
      });
    }

    return readClone.then(function () {
      return dispatch({
        type: CREATE_NEW_SUBRECORD,
        meta: {
          config: config,
          csid: csid,
          csidField: csidField,
          subrecordName: subrecordName,
          subrecordTypeConfig: subrecordTypeConfig,
          cloneCsid: cloneCsid,
          isDefault: isDefault
        }
      });
    });
  };
};

var saveSubrecords = function saveSubrecords(config, recordTypeConfig, vocabularyConfig, csid, saveStage) {
  return function (dispatch, getState) {
    var subrecords = recordTypeConfig.subrecords;


    if (!subrecords) {
      return Promise.resolve();
    }

    return Promise.all(Object.entries(subrecords).filter(function (entry) {
      return entry[1].saveStage === saveStage;
    }).map(function (entry) {
      var _entry2 = _slicedToArray(entry, 2),
          subrecordName = _entry2[0],
          subrecordConfig = _entry2[1];

      var subrecordCsid = (0, _reducers.getRecordSubrecordCsid)(getState(), csid, subrecordName);

      if (subrecordCsid) {
        var csidField = subrecordConfig.csidField,
            saveCondition = subrecordConfig.saveCondition,
            subresource = subrecordConfig.subresource;


        if (saveCondition) {
          var subrecordData = (0, _reducers.getRecordData)(getState(), subrecordCsid);

          if (!saveCondition(subrecordData)) {
            return Promise.resolve();
          }
        }

        if (csidField) {
          var subrecordTypeConfig = (0, _get2.default)(config, ['recordTypes', subrecordConfig.recordType]);

          var subrecordVocabularyConfig = (0, _get2.default)(subrecordTypeConfig, ['vocabularies', subrecordConfig.vocabulary]);

          // eslint-disable-next-line no-use-before-define
          return dispatch(saveRecord(config, subrecordTypeConfig, subrecordVocabularyConfig, subrecordCsid, undefined, undefined, undefined, function (newRecordCsid) {
            dispatch({
              type: SUBRECORD_CREATED,
              meta: {
                csid: csid,
                csidField: csidField,
                subrecordName: subrecordName,
                subrecordCsid: newRecordCsid
              }
            });
          }, false));
        }

        if (subresource) {
          var searchName = (0, _searchHelpers.getSubrecordSearchName)(csid, subrecordName);
          var subrecordSubresourceConfig = (0, _get2.default)(config, ['subresources', subresource]);

          if (subrecordSubresourceConfig) {
            // eslint-disable-next-line no-use-before-define
            return dispatch(saveRecord(config, recordTypeConfig, vocabularyConfig, csid, subrecordSubresourceConfig, subrecordCsid, undefined, function (newRecordCsid) {
              dispatch({
                type: SUBRECORD_CREATED,
                meta: {
                  csid: csid,
                  searchName: searchName,
                  subrecordName: subrecordName,
                  subrecordCsid: newRecordCsid
                }
              });
            }, false));
          }
        }
      }

      return Promise.resolve();
    }));
  };
};

var saveRecord = exports.saveRecord = function saveRecord(config, recordTypeConfig, vocabularyConfig, csid, subresourceConfig, subresourceCsid, relatedSubjectCsid, onRecordCreated) {
  var showNotifications = arguments.length > 8 && arguments[8] !== undefined ? arguments[8] : true;
  return function (dispatch, getState, intl) {
    var currentRecordTypeConfig = void 0;
    var currentVocabularyConfig = void 0;
    var currentCsid = void 0;

    if (subresourceConfig) {
      currentRecordTypeConfig = (0, _get2.default)(config, ['recordTypes', subresourceConfig.recordType]);

      currentVocabularyConfig = (0, _get2.default)(currentRecordTypeConfig, ['vocabularies', subresourceConfig.vocabulary]);

      currentCsid = subresourceCsid;
    } else {
      currentRecordTypeConfig = recordTypeConfig;
      currentVocabularyConfig = vocabularyConfig;
      currentCsid = csid;
    }

    return dispatch(computeRecordData(currentRecordTypeConfig, currentCsid)).then(function () {
      return dispatch(validateRecordData(currentRecordTypeConfig, currentCsid));
    }).then(function () {
      if ((0, _reducers.getRecordValidationErrors)(getState(), currentCsid)) {
        return null;
      }

      dispatch({
        type: RECORD_SAVE_STARTED,
        meta: {
          csid: currentCsid
        }
      });

      var title = currentRecordTypeConfig.title ? currentRecordTypeConfig.title((0, _reducers.getRecordData)(getState(), currentCsid), { config: config, intl: intl }) : null;

      var notificationID = (0, _notificationHelpers2.default)();

      if (showNotifications) {
        dispatch((0, _notification.showNotification)({
          items: [{
            message: saveMessages.saving,
            values: {
              title: title,
              hasTitle: title ? 'yes' : ''
            }
          }],
          date: new Date(),
          status: _notificationStatusCodes.STATUS_PENDING
        }, notificationID));
      }

      return dispatch(saveSubrecords(config, currentRecordTypeConfig, currentVocabularyConfig, currentCsid, 'before')).then(function () {
        var data = (0, _reducers.getRecordData)(getState(), currentCsid);
        var isExisting = (0, _recordDataHelpers.isExistingRecord)(data);

        var recordServicePath = (0, _get2.default)(recordTypeConfig, ['serviceConfig', 'servicePath']);
        var vocabularyServicePath = (0, _get2.default)(vocabularyConfig, ['serviceConfig', 'servicePath']);

        var pathParts = [recordServicePath];

        if (vocabularyServicePath) {
          pathParts.push(vocabularyServicePath);
          pathParts.push('items');
        }

        if (subresourceConfig) {
          if (csid) {
            pathParts.push(csid);
          }

          var subresourceServicePath = (0, _get2.default)(subresourceConfig, ['serviceConfig', 'servicePath']);

          if (subresourceServicePath) {
            pathParts.push(subresourceServicePath);
          }
        }

        if (isExisting && currentCsid) {
          pathParts.push(currentCsid);
        }

        var path = pathParts.join('/');

        var requestConfig = {
          data: (0, _recordDataHelpers.prepareForSending)(data, currentRecordTypeConfig).toJS()
        };

        if (recordTypeConfig.requestConfig) {
          (0, _merge2.default)(requestConfig, recordTypeConfig.requestConfig('save', data));
        }

        if (isExisting) {
          return (0, _cspace2.default)().update(path, requestConfig).then(function (response) {
            return dispatch(saveSubrecords(config, currentRecordTypeConfig, currentVocabularyConfig, currentCsid, 'after')).then(function () {
              if (showNotifications) {
                dispatch((0, _notification.showNotification)({
                  items: [{
                    message: saveMessages.saved,
                    values: {
                      title: title,
                      hasTitle: title ? 'yes' : ''
                    }
                  }],
                  date: new Date(),
                  status: _notificationStatusCodes.STATUS_SUCCESS,
                  autoClose: true
                }, notificationID));
              }

              dispatch({
                type: RECORD_SAVE_FULFILLED,
                payload: response,
                meta: {
                  recordTypeConfig: currentRecordTypeConfig,
                  csid: currentCsid,
                  relatedSubjectCsid: relatedSubjectCsid
                }
              });
            }).then(function () {
              return dispatch(initializeSubrecords(config, currentRecordTypeConfig, currentVocabularyConfig, currentCsid));
            }).then(function () {
              return currentCsid;
            }).catch(function (error) {
              throw error;
            });
          }).catch(function (error) {
            return Promise.reject({
              code: _errorCodes.ERR_API,
              error: error
            });
          });
        }

        return (0, _cspace2.default)().create(path, requestConfig).then(function (response) {
          if (response.status === 201 && response.headers.location) {
            var location = response.headers.location;
            var newRecordCsid = location.substring(location.lastIndexOf('/') + 1);

            dispatch({
              type: RECORD_CREATED,
              meta: {
                currentCsid: currentCsid,
                newRecordCsid: newRecordCsid,
                recordTypeConfig: currentRecordTypeConfig
              }
            });

            return dispatch(saveSubrecords(config, currentRecordTypeConfig, currentVocabularyConfig, newRecordCsid, 'after')).then(function () {
              return doRead(currentRecordTypeConfig, currentVocabularyConfig, newRecordCsid);
            }).then(function (readResponse) {
              if (showNotifications) {
                dispatch((0, _notification.showNotification)({
                  items: [{
                    message: saveMessages.saved,
                    values: {
                      title: title,
                      hasTitle: title ? 'yes' : ''
                    }
                  }],
                  date: new Date(),
                  status: _notificationStatusCodes.STATUS_SUCCESS,
                  autoClose: true
                }, notificationID));
              }

              return dispatch({
                type: RECORD_SAVE_FULFILLED,
                payload: readResponse,
                meta: {
                  relatedSubjectCsid: relatedSubjectCsid,
                  recordTypeConfig: currentRecordTypeConfig,
                  csid: newRecordCsid
                }
              });
            }).then(function () {
              return dispatch(initializeSubrecords(config, currentRecordTypeConfig, currentVocabularyConfig, newRecordCsid));
            }).then(function () {
              return Promise.resolve(onRecordCreated ? onRecordCreated(newRecordCsid) : null);
            }).then(function () {
              return newRecordCsid;
            });
          }

          var error = new Error('Expected response with status 201 and a location header');
          error.response = response;

          throw error;
        }).catch(function (error) {
          return Promise.reject({
            code: _errorCodes.ERR_API,
            error: error
          });
        });
      }).catch(function (error) {
        if (showNotifications) {
          dispatch((0, _notification.showNotification)({
            items: [{
              message: saveMessages.errorSaving,
              values: {
                title: title,
                hasTitle: title ? 'yes' : '',
                error: (0, _getErrorDescription2.default)(error)
              }
            }],
            date: new Date(),
            status: _notificationStatusCodes.STATUS_ERROR
          }, notificationID));
        }

        dispatch({
          type: RECORD_SAVE_REJECTED,
          payload: error,
          meta: {
            csid: currentCsid
          }
        });

        throw error;
      });
    });
  };
};

var addFieldInstance = exports.addFieldInstance = function addFieldInstance(recordTypeConfig, csid, path, position) {
  return function (dispatch) {
    dispatch({
      type: ADD_FIELD_INSTANCE,
      meta: {
        csid: csid,
        path: path,
        position: position,
        recordTypeConfig: recordTypeConfig
      }
    });

    return dispatch(computeRecordData(recordTypeConfig, csid)).then(function () {
      return dispatch(validateRecordData(recordTypeConfig, csid));
    });
  };
};

var sortFieldInstances = exports.sortFieldInstances = function sortFieldInstances(recordTypeConfig, csid, path, byField) {
  return function (dispatch) {
    dispatch({
      type: SORT_FIELD_INSTANCES,
      meta: {
        csid: csid,
        path: path,
        byField: byField,
        recordTypeConfig: recordTypeConfig
      }
    });

    return dispatch(computeRecordData(recordTypeConfig, csid)).then(function () {
      return dispatch(validateRecordData(recordTypeConfig, csid));
    });
  };
};

var deleteFieldValue = exports.deleteFieldValue = function deleteFieldValue(recordTypeConfig, csid, path) {
  return function (dispatch) {
    dispatch({
      type: DELETE_FIELD_VALUE,
      meta: {
        csid: csid,
        path: path
      }
    });

    return dispatch(computeRecordData(recordTypeConfig, csid)).then(function () {
      return dispatch(validateRecordData(recordTypeConfig, csid));
    });
  };
};

var moveFieldValue = exports.moveFieldValue = function moveFieldValue(recordTypeConfig, csid, path, newPosition) {
  return function (dispatch) {
    dispatch({
      type: MOVE_FIELD_VALUE,
      meta: {
        csid: csid,
        path: path,
        newPosition: newPosition
      }
    });

    return dispatch(computeRecordData(recordTypeConfig, csid)).then(function () {
      return dispatch(validateRecordData(recordTypeConfig, csid));
    });
  };
};

var setFieldValue = exports.setFieldValue = function setFieldValue(recordTypeConfig, csid, path, value) {
  return function (dispatch) {
    dispatch({
      type: SET_FIELD_VALUE,
      payload: value,
      meta: {
        csid: csid,
        path: path
      }
    });

    return dispatch(computeRecordData(recordTypeConfig, csid)).then(function () {
      return dispatch(validateRecordData(recordTypeConfig, csid));
    });
  };
};

var revertRecord = exports.revertRecord = function revertRecord(recordTypeConfig, csid) {
  return function (dispatch) {
    dispatch({
      type: REVERT_RECORD,
      meta: {
        recordTypeConfig: recordTypeConfig,
        csid: csid
      }
    });

    // Clear validation errors. Could maybe revalidate here, but to be consistent, we should also
    // validate when a record is first loaded.

    dispatch({
      type: VALIDATION_PASSED,
      meta: {
        csid: csid,
        path: []
      }
    });

    dispatch((0, _notification.removeValidationNotification)());
    dispatch((0, _notification.removeNotification)(_HierarchyReparentNotifier2.default.notificationID));
  };
};

var deleteRecord = exports.deleteRecord = function deleteRecord(config, recordTypeConfig, vocabularyConfig, csid, relatedSubjectCsid) {
  return function (dispatch, getState, intl) {
    var data = (0, _reducers.getRecordData)(getState(), csid);
    var title = recordTypeConfig.title(data, { config: config, intl: intl });
    var notificationID = (0, _notificationHelpers2.default)();

    dispatch((0, _notification.showNotification)({
      items: [{
        message: deleteMessages.deleting,
        values: {
          title: title,
          hasTitle: title ? 'yes' : ''
        }
      }],
      date: new Date(),
      status: _notificationStatusCodes.STATUS_PENDING
    }, notificationID));

    dispatch({
      type: RECORD_DELETE_STARTED,
      meta: {
        recordTypeConfig: recordTypeConfig,
        csid: csid
      }
    });

    var recordServicePath = recordTypeConfig.serviceConfig.servicePath;

    var vocabularyServicePath = vocabularyConfig ? vocabularyConfig.serviceConfig.servicePath : null;

    var pathParts = [recordServicePath];

    if (vocabularyServicePath) {
      pathParts.push(vocabularyServicePath);
      pathParts.push('items');
    }

    if (csid) {
      pathParts.push(csid);
    }

    var path = pathParts.join('/');

    return (0, _cspace2.default)().delete(path).then(function (response) {
      dispatch((0, _notification.showNotification)({
        items: [{
          message: deleteMessages.deleted,
          values: {
            title: title,
            hasTitle: title ? 'yes' : ''
          }
        }],
        date: new Date(),
        status: _notificationStatusCodes.STATUS_SUCCESS,
        autoClose: true
      }, notificationID));

      return dispatch({
        type: RECORD_DELETE_FULFILLED,
        payload: response,
        meta: {
          recordTypeConfig: recordTypeConfig,
          csid: csid,
          relatedSubjectCsid: relatedSubjectCsid
        }
      });
    }).catch(function (error) {
      dispatch((0, _notification.showNotification)({
        items: [{
          message: deleteMessages.errorDeleting,
          values: {
            title: title,
            hasTitle: title ? 'yes' : '',
            error: (0, _getErrorDescription2.default)(error)
          }
        }],
        date: new Date(),
        status: _notificationStatusCodes.STATUS_ERROR
      }, notificationID));

      return dispatch({
        type: RECORD_DELETE_REJECTED,
        payload: {
          code: _errorCodes.ERR_API,
          error: error
        },
        meta: {
          recordTypeConfig: recordTypeConfig,
          csid: csid
        }
      });
    });
  };
};

var transitionRecord = exports.transitionRecord = function transitionRecord(config, recordTypeConfig, vocabularyConfig, csid, transitionName, relatedSubjectCsid) {
  return function (dispatch, getState, intl) {
    var data = (0, _reducers.getRecordData)(getState(), csid);
    var title = recordTypeConfig.title(data, { config: config, intl: intl });
    var notificationID = (0, _notificationHelpers2.default)();

    var messages = transitionMessages[transitionName];

    if (messages) {
      dispatch((0, _notification.showNotification)({
        items: [{
          message: messages.transitioning,
          values: {
            title: title,
            hasTitle: title ? 'yes' : ''
          }
        }],
        date: new Date(),
        status: _notificationStatusCodes.STATUS_PENDING
      }, notificationID));
    }

    dispatch({
      type: RECORD_TRANSITION_STARTED,
      meta: {
        recordTypeConfig: recordTypeConfig,
        csid: csid,
        transitionName: transitionName
      }
    });

    var recordServicePath = recordTypeConfig.serviceConfig.servicePath;

    var vocabularyServicePath = vocabularyConfig ? vocabularyConfig.serviceConfig.servicePath : null;

    var pathParts = [recordServicePath];

    if (vocabularyServicePath) {
      pathParts.push(vocabularyServicePath);
      pathParts.push('items');
    }

    if (csid) {
      pathParts.push(csid);
    }

    pathParts.push('workflow');
    pathParts.push(transitionName);

    var path = pathParts.join('/');

    return (0, _cspace2.default)().update(path).then(function (response) {
      return transitionName === 'delete' ? response
      // For all transitions other than delete, re-read the record to obtain the new workflow
      // state.
      : doRead(recordTypeConfig, vocabularyConfig, csid);
    }).then(function (response) {
      if (messages) {
        dispatch((0, _notification.showNotification)({
          items: [{
            message: messages.transitioned,
            values: {
              transitionName: transitionName,
              title: title,
              hasTitle: title ? 'yes' : ''
            }
          }],
          date: new Date(),
          status: _notificationStatusCodes.STATUS_SUCCESS,
          autoClose: true
        }, notificationID));
      }

      return dispatch({
        type: RECORD_TRANSITION_FULFILLED,
        payload: response,
        meta: {
          recordTypeConfig: recordTypeConfig,
          csid: csid,
          transitionName: transitionName,
          relatedSubjectCsid: relatedSubjectCsid,
          // We don't get data back from the transition request. Rather than making a separate
          // request to get the actual updated time of the record, just make it the current time.
          updatedTimestamp: new Date().toISOString()
        }
      });
    }).catch(function (error) {
      if (messages) {
        dispatch((0, _notification.showNotification)({
          items: [{
            message: messages.errorTransitioning,
            values: {
              transitionName: transitionName,
              title: title,
              hasTitle: title ? 'yes' : '',
              error: (0, _getErrorDescription2.default)(error)
            }
          }],
          date: new Date(),
          status: _notificationStatusCodes.STATUS_ERROR
        }, notificationID));
      }

      return dispatch({
        type: RECORD_TRANSITION_REJECTED,
        payload: {
          code: _errorCodes.ERR_API,
          error: error
        },
        meta: {
          recordTypeConfig: recordTypeConfig,
          csid: csid,
          transitionName: transitionName
        }
      });
    });
  };
};

var saveRecordWithTransition = exports.saveRecordWithTransition = function saveRecordWithTransition(config, recordTypeConfig, vocabularyConfig, csid, subresourceConfig, subresourceCsid, relatedSubjectCsid, transitionName, onRecordCreated) {
  var showNotifications = arguments.length > 9 && arguments[9] !== undefined ? arguments[9] : true;
  return function (dispatch) {
    return dispatch(saveRecord(config, recordTypeConfig, vocabularyConfig, csid, subresourceConfig, subresourceCsid, relatedSubjectCsid, onRecordCreated, showNotifications)).then(function (savedCsid) {
      return dispatch(transitionRecord(config, recordTypeConfig, vocabularyConfig, savedCsid, transitionName, relatedSubjectCsid));
    });
  };
};

var detachSubrecord = exports.detachSubrecord = function detachSubrecord(config, csid, csidField, subrecordName, subrecordTypeConfig) {
  return {
    type: DETACH_SUBRECORD,
    meta: {
      config: config,
      csid: csid,
      csidField: csidField,
      subrecordName: subrecordName,
      subrecordTypeConfig: subrecordTypeConfig
    }
  };
};

var clearRecord = exports.clearRecord = function clearRecord(csid) {
  return {
    type: CLEAR_RECORD,
    meta: {
      csid: csid
    }
  };
};