server/helpers/revisions.js

"use strict";

require("core-js/modules/es.object.keys.js");
require("core-js/modules/es.symbol.js");
require("core-js/modules/es.array.filter.js");
require("core-js/modules/es.object.get-own-property-descriptor.js");
require("core-js/modules/web.dom-collections.for-each.js");
require("core-js/modules/es.object.get-own-property-descriptors.js");
require("core-js/modules/es.weak-map.js");
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
var _typeof = require("@babel/runtime/helpers/typeof");
Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.getAssociatedEntityRevisions = getAssociatedEntityRevisions;
exports.getOrderedRevisionForEditorPage = getOrderedRevisionForEditorPage;
exports.getOrderedRevisions = getOrderedRevisions;
exports.getOrderedRevisionsForEntityPage = getOrderedRevisionsForEntityPage;
var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
var _objectWithoutProperties2 = _interopRequireDefault(require("@babel/runtime/helpers/objectWithoutProperties"));
var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));
require("core-js/modules/es.array.map.js");
require("core-js/modules/es.date.to-json.js");
require("core-js/modules/web.url.to-json.js");
require("core-js/modules/es.string.iterator.js");
require("core-js/modules/es.array.iterator.js");
require("core-js/modules/web.dom-collections.iterator.js");
require("core-js/modules/es.array.find-index.js");
require("core-js/modules/es.parse-int.js");
require("core-js/modules/es.object.to-string.js");
require("core-js/modules/es.promise.js");
require("core-js/modules/es.array.concat.js");
require("core-js/modules/es.array.join.js");
var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));
var _flatMap2 = _interopRequireDefault(require("lodash/flatMap"));
var error = _interopRequireWildcard(require("../../common/helpers/error"));
var _excluded = ["author", "id"],
  _excluded2 = ["author", "id"];
/*
 * Copyright (C) 2020 Prabal Singh
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */
function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || _typeof(obj) !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
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) { (0, _defineProperty2.default)(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 getRevisionModels(orm) {
  var AuthorRevision = orm.AuthorRevision,
    EditionRevision = orm.EditionRevision,
    EditionGroupRevision = orm.EditionGroupRevision,
    PublisherRevision = orm.PublisherRevision,
    SeriesRevision = orm.SeriesRevision,
    WorkRevision = orm.WorkRevision;
  return [AuthorRevision, EditionGroupRevision, EditionRevision, PublisherRevision, SeriesRevision, WorkRevision];
}
/* eslint-disable no-await-in-loop */
/**
 * Fetches the entities affected by a revision, their alias
 * or in case of deleted entities their last know alias.
 * It then attaches the necessary information to each revisions's entities array
 *
 * @param {array} revisions - the array of revisions
 * @param {object} orm - the BookBrainz ORM, initialized during app setup
 * @returns {array} - The modified revisions array
 */
function getAssociatedEntityRevisions(_x, _x2) {
  return _getAssociatedEntityRevisions.apply(this, arguments);
}
/**
 * Fetches revisions for Show All Revisions/Index Page
 * Fetches the last 'size' number of revisions with offset 'from'
 *
 * @param {number} from - the offset value
 * @param {number} size - no. of last revisions required
 * @param {object} orm - the BookBrainz ORM, initialized during app setup
 * @returns {array} - orderedRevisions
 */
function _getAssociatedEntityRevisions() {
  _getAssociatedEntityRevisions = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee(revisions, orm) {
    var revisionIDs, RevisionModels, Entity, i, EntityRevision, entityRevisions, entityRevisionsJSON, _loop, index;
    return _regenerator.default.wrap(function _callee$(_context2) {
      while (1) {
        switch (_context2.prev = _context2.next) {
          case 0:
            revisionIDs = revisions.map(function (_ref) {
              var revisionId = _ref.revisionId;
              return revisionId;
            });
            RevisionModels = getRevisionModels(orm);
            Entity = orm.Entity;
            i = 0;
          case 4:
            if (!(i < RevisionModels.length)) {
              _context2.next = 21;
              break;
            }
            EntityRevision = RevisionModels[i];
            _context2.next = 8;
            return new EntityRevision().query(function (qb) {
              qb.whereIn('id', revisionIDs);
            }).fetchAll({
              merge: false,
              remove: false,
              require: false,
              withRelated: ['data.aliasSet.defaultAlias']
            }).catch(EntityRevision.NotFoundError, function (err) {
              // eslint-disable-next-line no-console
              console.log(err);
            });
          case 8:
            entityRevisions = _context2.sent;
            if (!(entityRevisions && entityRevisions.length)) {
              _context2.next = 18;
              break;
            }
            entityRevisionsJSON = entityRevisions.toJSON();
            _loop = /*#__PURE__*/_regenerator.default.mark(function _loop(index) {
              var entityRevision, entity, type, bbid, entityProps, revisionIndex;
              return _regenerator.default.wrap(function _loop$(_context) {
                while (1) {
                  switch (_context.prev = _context.next) {
                    case 0:
                      entityRevision = entityRevisionsJSON[index];
                      _context.next = 3;
                      return new Entity({
                        bbid: entityRevision.bbid
                      }).fetch();
                    case 3:
                      entity = _context.sent;
                      type = entity.get('type');
                      bbid = entity.get('bbid');
                      entityProps = {
                        bbid: bbid,
                        type: type
                      };
                      if (!entityRevision.data) {
                        _context.next = 11;
                        break;
                      }
                      entityProps.defaultAlias = entityRevision.data.aliasSet.defaultAlias;
                      _context.next = 14;
                      break;
                    case 11:
                      _context.next = 13;
                      return orm.func.entity.getEntityParentAlias(orm, type, bbid);
                    case 13:
                      entityProps.parentAlias = _context.sent;
                    case 14:
                      // Find the revision by id and attach the current entity to it
                      revisionIndex = revisions.findIndex(function (rev) {
                        return rev.revisionId === entityRevision.id;
                      });
                      revisions[revisionIndex].entities.push(entityProps);
                    case 16:
                    case "end":
                      return _context.stop();
                  }
                }
              }, _loop);
            });
            index = 0;
          case 13:
            if (!(index < entityRevisionsJSON.length)) {
              _context2.next = 18;
              break;
            }
            return _context2.delegateYield(_loop(index), "t0", 15);
          case 15:
            index++;
            _context2.next = 13;
            break;
          case 18:
            i++;
            _context2.next = 4;
            break;
          case 21:
            return _context2.abrupt("return", revisions);
          case 22:
          case "end":
            return _context2.stop();
        }
      }
    }, _callee);
  }));
  return _getAssociatedEntityRevisions.apply(this, arguments);
}
function getOrderedRevisions(_x3, _x4, _x5) {
  return _getOrderedRevisions.apply(this, arguments);
}
/**
 * Fetches revisions for an Editor
 * Fetches the last 'size' revisions with offset 'from'
 *
 * @param {number} from - the offset value
 * @param {number} size - no. of last revisions required
 * @param {object} req - req is an object containing information about the HTTP request
 * @returns {array} - orderedRevisions for particular Editor
 * @description
 * This checks whether Editor is valid or not.
 * If Editor is valid then this extracts the revisions done by that editor;
 * and then add associated entities in that array;
 */
function _getOrderedRevisions() {
  _getOrderedRevisions = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee2(from, size, orm) {
    var Revision, revisions, revisionsJSON, formattedRevisions;
    return _regenerator.default.wrap(function _callee2$(_context3) {
      while (1) {
        switch (_context3.prev = _context3.next) {
          case 0:
            Revision = orm.Revision;
            _context3.next = 3;
            return new Revision().orderBy('created_at', 'DESC').fetchPage({
              limit: size,
              offset: from,
              withRelated: ['author']
            });
          case 3:
            revisions = _context3.sent;
            revisionsJSON = revisions.toJSON();
            /* Massage the revisions to match the expected format */
            formattedRevisions = revisionsJSON.map(function (rev) {
              delete rev.authorId;
              var editor = rev.author,
                revisionId = rev.id,
                otherProps = (0, _objectWithoutProperties2.default)(rev, _excluded);
              return _objectSpread({
                editor: editor,
                entities: [],
                revisionId: revisionId
              }, otherProps);
            });
            /* Fetch associated ${entity}_revisions and last know alias for deleted entities */
            return _context3.abrupt("return", getAssociatedEntityRevisions(formattedRevisions, orm));
          case 7:
          case "end":
            return _context3.stop();
        }
      }
    }, _callee2);
  }));
  return _getOrderedRevisions.apply(this, arguments);
}
function getOrderedRevisionForEditorPage(_x6, _x7, _x8) {
  return _getOrderedRevisionForEditorPage.apply(this, arguments);
}
/**
 * @name recursivelyGetMergedEntitiesBBIDs
 * @async
 * @param {*} orm - The BookshelfJS ORM object
 * @param {array} bbids - Array of target BBIDs we want to get the source BBID of (if they exist)
 * @returns {Promise<Array<string>>} - Returns a promise resolving to an recursed array of BBIDs containing all descendants
 * @description This recursive function fetches all the merged BBIDs that are pointing to an array of BBIDs
 * An example; if entity A was merged into entity B, BBID A will point to BBID B.
 * Now if entity B is merged in entity C, BBID B will point to BBID C.
 * To allow for reverting merges cleanly, BBID A still points to B and not C (trust me on this).
 * In order to fetch the complete history tree containing all three entities, we need to recursively check
 * if a source_bbid appears as a target_bbid in other rows.
 */
function _getOrderedRevisionForEditorPage() {
  _getOrderedRevisionForEditorPage = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee3(from, size, req) {
    var _req$app$locals$orm, Editor, Revision, revisions, revisionsJSON, formattedRevisions, orderedRevisions;
    return _regenerator.default.wrap(function _callee3$(_context4) {
      while (1) {
        switch (_context4.prev = _context4.next) {
          case 0:
            _req$app$locals$orm = req.app.locals.orm, Editor = _req$app$locals$orm.Editor, Revision = _req$app$locals$orm.Revision; // If editor isn't present, throw an error
            _context4.next = 3;
            return new Editor({
              id: req.params.id
            }).fetch().catch(Editor.NotFoundError, function () {
              throw new error.NotFoundError('Editor not found', req);
            });
          case 3:
            _context4.next = 5;
            return new Revision().query('where', 'author_id', '=', parseInt(req.params.id, 10)).orderBy('created_at', 'DESC').fetchPage({
              limit: size,
              offset: from,
              withRelated: [{
                'notes': function notes(q) {
                  q.orderBy('note.posted_at');
                }
              }, 'notes.author']
            });
          case 5:
            revisions = _context4.sent;
            revisionsJSON = revisions.toJSON();
            formattedRevisions = revisionsJSON.map(function (rev) {
              delete rev.authorId;
              var editor = rev.author,
                revisionId = rev.id,
                otherProps = (0, _objectWithoutProperties2.default)(rev, _excluded2);
              return _objectSpread({
                editor: editor,
                entities: [],
                revisionId: revisionId
              }, otherProps);
            });
            _context4.next = 10;
            return getAssociatedEntityRevisions(formattedRevisions, req.app.locals.orm);
          case 10:
            orderedRevisions = _context4.sent;
            return _context4.abrupt("return", orderedRevisions);
          case 12:
          case "end":
            return _context4.stop();
        }
      }
    }, _callee3);
  }));
  return _getOrderedRevisionForEditorPage.apply(this, arguments);
}
function recursivelyGetMergedEntitiesBBIDs(_x9, _x10) {
  return _recursivelyGetMergedEntitiesBBIDs.apply(this, arguments);
}
function _recursivelyGetMergedEntitiesBBIDs() {
  _recursivelyGetMergedEntitiesBBIDs = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee5(orm, bbids) {
    var returnValue;
    return _regenerator.default.wrap(function _callee5$(_context6) {
      while (1) {
        switch (_context6.prev = _context6.next) {
          case 0:
            returnValue = [];
            _context6.next = 3;
            return Promise.all(bbids.map( /*#__PURE__*/function () {
              var _ref2 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee4(bbid) {
                var thisLevelBBIDs, nextLevelBBIDs;
                return _regenerator.default.wrap(function _callee4$(_context5) {
                  while (1) {
                    switch (_context5.prev = _context5.next) {
                      case 0:
                        _context5.next = 2;
                        return orm.bookshelf.knex.select('source_bbid').from('bookbrainz.entity_redirect').where('target_bbid', bbid);
                      case 2:
                        thisLevelBBIDs = _context5.sent;
                        // Flatten the returned array of objects
                        thisLevelBBIDs = (0, _flatMap2.default)(thisLevelBBIDs, 'source_bbid');
                        _context5.next = 6;
                        return recursivelyGetMergedEntitiesBBIDs(orm, thisLevelBBIDs);
                      case 6:
                        nextLevelBBIDs = _context5.sent;
                        returnValue.push.apply(returnValue, (0, _toConsumableArray2.default)(thisLevelBBIDs).concat((0, _toConsumableArray2.default)(nextLevelBBIDs)));
                      case 8:
                      case "end":
                        return _context5.stop();
                    }
                  }
                }, _callee4);
              }));
              return function (_x16) {
                return _ref2.apply(this, arguments);
              };
            }()));
          case 3:
            return _context6.abrupt("return", returnValue);
          case 4:
          case "end":
            return _context6.stop();
        }
      }
    }, _callee5);
  }));
  return _recursivelyGetMergedEntitiesBBIDs.apply(this, arguments);
}
function getOrderedRevisionsForEntityPage(_x11, _x12, _x13, _x14, _x15) {
  return _getOrderedRevisionsForEntityPage.apply(this, arguments);
}
function _getOrderedRevisionsForEntityPage() {
  _getOrderedRevisionsForEntityPage = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee6(orm, from, size, RevisionModel, bbid) {
    var otherMergedBBIDs, revisions, revisionsJSON;
    return _regenerator.default.wrap(function _callee6$(_context7) {
      while (1) {
        switch (_context7.prev = _context7.next) {
          case 0:
            _context7.next = 2;
            return recursivelyGetMergedEntitiesBBIDs(orm, [bbid]);
          case 2:
            otherMergedBBIDs = _context7.sent;
            _context7.next = 5;
            return new RevisionModel().query(function (qb) {
              qb.distinct("".concat(RevisionModel.prototype.tableName, ".id"), 'revision.created_at');
              qb.whereIn('bbid', [bbid].concat((0, _toConsumableArray2.default)(otherMergedBBIDs)));
              qb.join('bookbrainz.revision', "".concat(RevisionModel.prototype.tableName, ".id"), '=', 'bookbrainz.revision.id');
              qb.orderBy('revision.created_at', 'DESC');
            }).fetchPage({
              limit: size,
              offset: from,
              withRelated: ['revision.author', {
                'revision.notes': function revisionNotes(q) {
                  q.orderBy('note.posted_at');
                }
              }, 'revision.notes.author']
            });
          case 5:
            revisions = _context7.sent;
            revisionsJSON = revisions ? revisions.toJSON() : [];
            return _context7.abrupt("return", revisionsJSON.map(function (rev) {
              var revision = rev.revision;
              var editor = revision.author;
              var revisionId = revision.id;
              delete revision.author;
              delete revision.authorId;
              delete revision.id;
              return _objectSpread({
                editor: editor,
                revisionId: revisionId
              }, revision);
            }));
          case 8:
          case "end":
            return _context7.stop();
        }
      }
    }, _callee6);
  }));
  return _getOrderedRevisionsForEntityPage.apply(this, arguments);
}
//# sourceMappingURL=revisions.js.map