UNPKG

@sanity/transaction-collator

Version:

Collate events from a series of transactions

230 lines (218 loc) 9.97 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.mutationsToEventTypeAndDocumentId = mutationsToEventTypeAndDocumentId; exports.transactionsToEvents = transactionsToEvents; var _uniq2 = _interopRequireDefault(require("lodash/uniq")); var _isEqual2 = _interopRequireDefault(require("lodash/isEqual")); var _ndjsonToArray = require("./utils/ndjsonToArray"); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; } function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; } function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return typeof key === "symbol" ? key : String(key); } function _toPrimitive(input, hint) { if (typeof input !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (typeof res !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); } var EDIT_EVENT_TIME_TRESHHOLD_MS = 1000 * 60 * 5; // 5 minutes function transactionsToEvents(documentIds, transactions) { var rawItems = Array.isArray(transactions) ? transactions : (0, _ndjsonToArray.ndjsonToArray)(transactions); return rawItems // Make sure we only deal with transactions that are relevant for our documents .filter(transaction => transaction.documentIDs && transaction.documentIDs.some(id => documentIds.includes(id))) // ensure transactions are sorted by time .sort(compareTimestamp) // Turn a transaction into a classified HistoryEvent .map((transaction, index) => mapToEvents(transaction, documentIds, index)) // Chunk and group edit events .reduce(reduceEdits, []) // Manipulate truncation events to be able to restore to published version .reduce(createReduceTruncatedFn(), []); } function mapToEvents(transaction, documentIds) { var index = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0; var _mutationsToEventType = mutationsToEventTypeAndDocumentId(filterRelevantMutations(transaction.mutations, documentIds), index), type = _mutationsToEventType.type, documentId = _mutationsToEventType.documentId; var timestamp = transaction.timestamp; var userIds = findUserIds(transaction, type); return { type, documentIDs: transaction.documentIDs, displayDocumentId: documentId, rev: transaction.id, userIds, transactionIds: [transaction.id], startTime: timestamp, endTime: timestamp }; } function reduceEdits(acc, current, index, arr) { var nextEvent = arr[index + 1]; var skipEvent = current.type === 'edited' && nextEvent && nextEvent.type === 'edited' && new Date(nextEvent.endTime).getTime() - new Date(current.endTime).getTime() < EDIT_EVENT_TIME_TRESHHOLD_MS && (0, _isEqual2.default)(current.documentIDs, nextEvent.documentIDs); if (skipEvent) { // Lift authors over to next event nextEvent.userIds = (0, _uniq2.default)(nextEvent.userIds.concat(current.userIds)); // Lift list of transactions over to next event nextEvent.transactionIds = (0, _uniq2.default)(current.transactionIds.concat(nextEvent.transactionIds)); // Set startTime on next event to be this one if not done already // (then startTime and endTime would be different) if (current.startTime === current.endTime) { nextEvent.startTime = current.startTime; } } else { acc.push(current); } return acc; } function createReduceTruncatedFn() { var truncated; return (acc, current, index, arr) => { truncated = truncated || arr.filter(event => event.type === 'truncated'); if (!truncated.includes(current)) { acc.push(current); } if (index === arr.length - 1) { var draftTruncationEvent = truncated.find(evt => !!evt.displayDocumentId && evt.displayDocumentId.startsWith('drafts.')); var publishedTruncationEvent = truncated.find(evt => !!evt.displayDocumentId && !evt.displayDocumentId.startsWith('drafts.')); if (draftTruncationEvent && publishedTruncationEvent) { acc.unshift(_objectSpread(_objectSpread({}, draftTruncationEvent), {}, { type: 'edited' })); acc.unshift(publishedTruncationEvent); } else if (publishedTruncationEvent) { acc.unshift(publishedTruncationEvent); } else if (draftTruncationEvent) { acc.unshift(draftTruncationEvent); } } return acc; }; } function mutationsToEventTypeAndDocumentId(mutations, transactionIndex) { var withoutPatches = mutations.filter(mut => mut.patch === undefined); var createOrReplaceMutation = withoutPatches.find(mut => mut.createOrReplace !== undefined); var createOrReplacePatch = createOrReplaceMutation && createOrReplaceMutation.createOrReplace; var createMutation = withoutPatches.find(mut => mut.create !== undefined); var createPatch = createMutation && createMutation.create; var createIfNotExistsMutation = withoutPatches.find(mut => mut.createIfNotExists !== undefined); var createIfNotExistsPatch = createIfNotExistsMutation && createIfNotExistsMutation.createIfNotExists; var deleteMutation = withoutPatches.find(mut => mut.delete !== undefined); var deletePatch = deleteMutation && deleteMutation.delete; var squashedMutation = withoutPatches.find(mut => mut.createSquashed !== undefined); var squashedPatch = squashedMutation && squashedMutation.createSquashed; var createValue = createOrReplacePatch || createPatch || createIfNotExistsPatch; // Created if (transactionIndex === 0) { var type = 'created'; if (createOrReplacePatch) { return { type, documentId: createOrReplacePatch._id }; } if (createIfNotExistsPatch) { return { type, documentId: createIfNotExistsPatch._id }; } if (createPatch) { return { type, documentId: createPatch._id }; } } // (re) created if (transactionIndex > 0 && mutations.length === 1 && createIfNotExistsPatch) { var _type = createIfNotExistsPatch._id.startsWith('.draft') ? 'edited' : 'published'; return { type: _type, documentId: createIfNotExistsPatch._id }; } // Published if ((createOrReplacePatch || createPatch || createIfNotExistsPatch) && deletePatch && deletePatch.id.startsWith('drafts.')) { return { type: 'published', documentId: createValue && createValue._id || null }; } // Unpublished if (withoutPatches.length === 2 && (createIfNotExistsPatch || createPatch) && deletePatch && !deletePatch.id.startsWith('drafts.')) { return { type: 'unpublished', documentId: createValue && createValue._id || null }; } // Restored to previous version if (createOrReplacePatch && createOrReplacePatch._id.startsWith('drafts.') || createPatch && createPatch._id.startsWith('drafts.') || createIfNotExistsPatch && createIfNotExistsPatch._id.startsWith('drafts.')) { return { type: 'edited', documentId: createValue && createValue._id || null }; } // Discard drafted changes if (mutations.length === 1 && deletePatch && deletePatch.id.startsWith('drafts.')) { return { type: 'discardDraft', documentId: deletePatch.id.replace('drafts.', '') }; } // Truncated history if (mutations.length === 1 && squashedPatch) { return { type: 'truncated', documentId: squashedPatch.document._id }; } // Deleted if (mutations.every(mut => mut.delete !== undefined)) { return { type: 'deleted', documentId: null }; } // Edited var patchedMutation = mutations.find(mut => mut.patch !== undefined); if (patchedMutation && patchedMutation.patch) { return { type: 'edited', documentId: patchedMutation.patch.id }; } // Edited (createOrReplace) if (createOrReplacePatch) { return { type: 'edited', documentId: createOrReplacePatch._id }; } return { type: 'unknown', documentId: null }; } function findUserIds(transaction, type) { // The truncated event is kind of special if (type === 'truncated') { var createSquasedMut = transaction.mutations.find(mut => mut.createSquashed !== undefined); var createSquasedPatch = createSquasedMut && createSquasedMut.createSquashed; if (createSquasedPatch) { return createSquasedPatch.authors; } } // Default is to return the transaction author return [transaction.author]; } function compareTimestamp(a, b) { return new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime(); } function filterRelevantMutations(mutations, documentIds) { return mutations.filter(mut => { return Object.keys(mut).map(key => { var val = mut[key]; return val.id || val._id || val.document && val.document._id || false; }).some(id => id && documentIds.includes(id)); }); }