UNPKG

cspace-ui

Version:
418 lines (412 loc) 14.2 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.unrelateBidirectional = exports.unrelate = exports.showRelationNotification = exports.find = exports.deleteRelation = exports.createBidirectional = exports.create = exports.clearState = exports.checkForRelations = exports.batchUnrelateBidirectional = exports.batchUnrelate = exports.batchCreateBidirectional = exports.batchCreate = exports.CONCURRENCY_LIMIT = void 0; var _immutable = _interopRequireDefault(require("immutable")); var _reactIntl = require("react-intl"); var _get = _interopRequireDefault(require("lodash/get")); var _pLimit = _interopRequireDefault(require("p-limit")); var _session = _interopRequireDefault(require("../helpers/session")); var _notification = require("./notification"); var _getErrorDescription = _interopRequireDefault(require("../helpers/getErrorDescription")); var _reducers = require("../reducers"); var _errorCodes = require("../constants/errorCodes"); var _notificationStatusCodes = require("../constants/notificationStatusCodes"); var _actionCodes = require("../constants/actionCodes"); function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; } const clearState = () => ({ type: _actionCodes.CLEAR_RELATION_STATE }); exports.clearState = clearState; const messages = (0, _reactIntl.defineMessages)({ related: { "id": "action.relation.related", "defaultMessage": "{objectCount, plural, =0 {No records} one {# record} other {# records}} related to {subjectTitle}." }, batchCreateError: { "id": "action.relation.batchCreateError", "defaultMessage": "Some records could not be related: {error}" }, batchUnrelateError: { "id": "action.relation.batchUnrelateError", "defaultMessage": "Some records could not be unrelated: {error}" }, multipleSubjectsRelated: { "id": "searchToRelateModal.multipleSubjectsRelated", "defaultMessage": "{objectCount, plural, =0 {No records} one {# record} other {# records}} related to each of {subjectCount, number} search results." } }); const notificationID = 'action.relation'; const relatePayload = ({ csid: subjectCsid }, { csid: objectCsid }, relationshipType) => ({ data: { document: { 'rel:relations_common': { '@xmlns:rel': 'http://collectionspace.org/services/relation', subjectCsid, objectCsid, relationshipType } } } }); const CONCURRENCY_LIMIT = exports.CONCURRENCY_LIMIT = 6; const showRelationNotification = (message, values) => (0, _notification.showNotification)({ items: [{ message, values }], date: new Date(), status: _notificationStatusCodes.STATUS_SUCCESS, autoClose: true }); /* * Find a relation, given at least the subject csid and object csid, and optionally the subject * record type, object record type, and predicate. This function assumes that there will be very * few (typically zero or one) results, and retrieves them all without paginating. */ exports.showRelationNotification = showRelationNotification; const find = (config, subject, object, predicate) => (dispatch, getState) => { if (!(subject.csid || object.csid)) { throw new Error('subject csid or object csid must be supplied'); } if ((0, _reducers.getRelationFindResult)(getState(), subject, object, predicate)) { // Already have a result for this descriptor. Do nothing. // TODO: Also check for a pending find request. return null; } dispatch({ type: _actionCodes.RELATION_FIND_STARTED, meta: { subject, object, predicate } }); const params = { prd: predicate, wf_deleted: 'false', pgSz: '0' }; if (subject) { const { csid, recordType } = subject; params.sbj = csid; if (recordType) { params.sbjType = (0, _get.default)(config, ['recordTypes', recordType, 'serviceConfig', 'objectName']); } } if (object) { const { csid, recordType } = object; params.obj = csid; if (recordType) { params.objType = (0, _get.default)(config, ['recordTypes', recordType, 'serviceConfig', 'objectName']); } } const requestConfig = { params }; return (0, _session.default)().read('/relations', requestConfig).then(response => dispatch({ type: _actionCodes.RELATION_FIND_FULFILLED, payload: response, meta: { subject, object, predicate } })).catch(error => dispatch({ type: _actionCodes.RELATION_FIND_REJECTED, payload: { code: _errorCodes.ERR_API, error }, meta: { subject, object, predicate } })); }; /** * Check if any relations exist for a given record csid and predicate, and optionally, a related * csid. Resolves to true or false. */ exports.find = find; const checkForRelations = (csid1, predicate, csid2) => () => { const params = { prd: predicate, sbj: csid1, andReciprocal: 'true', wf_deleted: 'false', pgSz: '1' }; if (typeof csid2 !== 'undefined') { params.obj = csid2; } const requestConfig = { params }; return (0, _session.default)().read('/relations', requestConfig).then(response => { const totalItems = (0, _get.default)(response, ['data', 'rel:relations-common-list', 'totalItems']); return totalItems && parseInt(totalItems, 10) > 0; }); }; exports.checkForRelations = checkForRelations; const deleteRelation = csid => dispatch => { if (!csid) { throw new Error('csid must be supplied'); } // return Promise.resolve(); dispatch({ type: _actionCodes.RELATION_DELETE_STARTED, meta: { csid } }); return (0, _session.default)().delete(`/relations/${csid}`).then(response => dispatch({ type: _actionCodes.RELATION_DELETE_FULFILLED, payload: response, meta: { csid } })).catch(error => { dispatch({ type: _actionCodes.RELATION_DELETE_REJECTED, payload: { code: _errorCodes.ERR_API, error }, meta: { csid } }); return Promise.reject(error); }); }; exports.deleteRelation = deleteRelation; const doUnrelate = (config, subject, object, predicate) => (dispatch, getState) => { if (!(subject.csid && object.csid)) { throw new Error('subject csid and object csid must be supplied'); } const existingResult = (0, _reducers.getRelationFindResult)(getState(), subject, object, predicate); let promise; if (existingResult) { promise = Promise.resolve(existingResult); } else { promise = dispatch(find(config, subject, object, predicate)).then(() => (0, _reducers.getRelationFindResult)(getState(), subject, object, predicate)); } return promise.then(findResult => { const list = findResult.get('rel:relations-common-list'); let items = list.get('relation-list-item'); if (!_immutable.default.List.isList(items)) { items = _immutable.default.List.of(items); } return Promise.all(items.map(item => dispatch(deleteRelation(item.get('csid'))))); }); }; const unrelate = (config, subject, object, predicate) => dispatch => dispatch(doUnrelate(config, subject, object, predicate)).then(() => dispatch({ type: _actionCodes.SUBJECT_RELATIONS_UPDATED, meta: { subject, updatedTime: new Date().toISOString() } })).catch(() => {}); exports.unrelate = unrelate; const unrelateBidirectional = (config, subject, object, predicate) => dispatch => dispatch(unrelate(config, subject, object, predicate)).then(() => dispatch(unrelate(config, object, subject, predicate))).catch(() => {}); exports.unrelateBidirectional = unrelateBidirectional; const batchUnrelate = (config, subject, objects, predicate) => dispatch => objects.reduce((promise, object) => promise.then(() => dispatch(doUnrelate(config, subject, object, predicate))), Promise.resolve()).then(() => dispatch({ type: _actionCodes.SUBJECT_RELATIONS_UPDATED, meta: { subject, updatedTime: new Date().toISOString() } })).catch(error => { dispatch((0, _notification.showNotification)({ items: [{ message: messages.batchUnrelateError, values: { error: (0, _getErrorDescription.default)(error) } }], date: new Date(), status: _notificationStatusCodes.STATUS_ERROR }, notificationID)); }); exports.batchUnrelate = batchUnrelate; const batchUnrelateBidirectional = (config, subject, objects, predicate) => dispatch => // For the passed subject, we only want to dispatch SUBJECT_RELATIONS_UPDATED once at the end, so // doUnrelate is used. The passed objects should be unique; for the reverse relations (where the // object becomes the subject), SUBJECT_RELATIONS_UPDATED may be dispatched immediately, so // unrelate is used. // Send these requests one at a time, to avoid DOSing the server. objects.reduce((promise, object) => promise.then(() => dispatch(doUnrelate(config, subject, object, predicate))).then(() => dispatch(unrelate(config, object, subject, predicate))), Promise.resolve()).then(() => dispatch({ type: _actionCodes.SUBJECT_RELATIONS_UPDATED, meta: { subject, updatedTime: new Date().toISOString() } })).catch(error => { dispatch((0, _notification.showNotification)({ items: [{ message: messages.batchUnrelateError, values: { error: (0, _getErrorDescription.default)(error) } }], date: new Date(), status: _notificationStatusCodes.STATUS_ERROR }, notificationID)); }); exports.batchUnrelateBidirectional = batchUnrelateBidirectional; const doCreate = (subject, object, predicate) => dispatch => { dispatch({ type: _actionCodes.RELATION_SAVE_STARTED, meta: { subject, object, predicate } }); return (0, _session.default)().create('/relations', relatePayload(subject, object, predicate)).then(response => dispatch({ type: _actionCodes.RELATION_SAVE_FULFILLED, payload: response, meta: { subject, object, predicate } })).catch(error => { dispatch({ type: _actionCodes.RELATION_SAVE_REJECTED, payload: { code: _errorCodes.ERR_API, error }, meta: { subject, object, predicate } }); return Promise.reject(error); }); }; const batchCreate = (subject, objects, predicate) => dispatch => objects.reduce((promise, object) => promise.then(() => dispatch(checkForRelations(subject.csid, predicate, object.csid))).then(relationExists => { if (relationExists) { return Promise.resolve(); } return dispatch(doCreate(subject, object, predicate)); }), Promise.resolve()).then(() => dispatch({ type: _actionCodes.SUBJECT_RELATIONS_UPDATED, meta: { subject, updatedTime: new Date().toISOString() } })).catch(error => { dispatch((0, _notification.showNotification)({ items: [{ message: messages.batchCreateError, values: { error: (0, _getErrorDescription.default)(error) } }], date: new Date(), status: _notificationStatusCodes.STATUS_ERROR }, notificationID)); }); exports.batchCreate = batchCreate; const batchCreateBidirectional = (subjects, objects, predicate) => dispatch => { dispatch({ type: _actionCodes.RELATION_SAVE_STARTED, meta: { subjects, objects, predicate } }); const limit = (0, _pLimit.default)(CONCURRENCY_LIMIT); const promises = subjects.flatMap(subject => objects.map(object => limit(() => dispatch(checkForRelations(subject.csid, predicate, object.csid)).then(relationExists => { if (relationExists) { return Promise.reject(new Error('Relation already exists')); } return (0, _session.default)().create('/relations', relatePayload(subject, object, predicate)).then(() => (0, _session.default)().create('/relations', relatePayload(object, subject, predicate))); })))); return Promise.all(promises).then(() => { const shorterArray = objects.length < subjects.length ? objects : subjects; const longerArray = objects.length >= subjects.length ? objects : subjects; if (shorterArray.length > 5) { // when the shorterArray length is more than 5, // a single multipleSubjectsRelated notification is shown dispatch(showRelationNotification(messages.multipleSubjectsRelated, { objectCount: objects.length, subjectCount: subjects.length })); } else { // otherwise for each subject, a notification is shown shorterArray.forEach(item => { dispatch(showRelationNotification(messages.related, { objectCount: longerArray.length, subjectTitle: item.title })); }); } shorterArray.forEach(item => { dispatch({ type: _actionCodes.SUBJECT_RELATIONS_UPDATED, meta: { subject: item, updatedTime: new Date().toISOString() } }); }); }).catch(error => { dispatch({ type: _actionCodes.RELATION_SAVE_REJECTED, payload: { code: _errorCodes.ERR_API, error }, meta: { subjects, objects, predicate } }); dispatch((0, _notification.showNotification)({ items: [{ message: messages.batchCreateError, values: { error: (0, _getErrorDescription.default)(error) } }], date: new Date(), status: _notificationStatusCodes.STATUS_ERROR }, notificationID)); }); }; exports.batchCreateBidirectional = batchCreateBidirectional; const create = (subject, object, predicate) => dispatch => dispatch(doCreate(subject, object, predicate)).then(() => dispatch({ type: _actionCodes.SUBJECT_RELATIONS_UPDATED, meta: { subject, updatedTime: new Date().toISOString() } })).catch(() => {}); exports.create = create; const createBidirectional = (subject, object, predicate) => dispatch => dispatch(doCreate(subject, object, predicate)).then(() => dispatch(doCreate(object, subject, predicate))).then(() => dispatch({ type: _actionCodes.SUBJECT_RELATIONS_UPDATED, meta: { subject, updatedTime: new Date().toISOString() } })).catch(() => {}); exports.createBidirectional = createBidirectional;