func/entity.js

"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.getAdditionalEntityProps = getAdditionalEntityProps;
exports.getEntity = getEntity;
exports.getEntityModelByType = getEntityModelByType;
exports.getEntityModels = getEntityModels;
exports.getEntityParentAlias = getEntityParentAlias;
exports.getEntitySetMetadataByType = getEntitySetMetadataByType;
exports.recursivelyGetRedirectBBID = recursivelyGetRedirectBBID;
var _camelCase2 = _interopRequireDefault(require("lodash/camelCase"));
var _mapKeys2 = _interopRequireDefault(require("lodash/mapKeys"));
var _snakeCase2 = _interopRequireDefault(require("lodash/snakeCase"));
var _pick2 = _interopRequireDefault(require("lodash/pick"));
var _util = require("../util");
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/*
 * Copyright (C) 2018  Shivam Tripathi
 * Copyright (C) 2019  Nicolas Pelletier
 * Some parts adapted from bookbrainz-site
 *
 * 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.
*/

/**
 * @param  {Object} entityData - Object holding all data related to an entity
 * @param  {string} entityType - The type of the entity
 * @returns {Object} - Returns all the additional entity specific data
*/
function getAdditionalEntityProps(entityData, entityType) {
  switch (entityType) {
    case 'Author':
      {
        const {
          typeId,
          genderId,
          beginAreaId,
          beginDate,
          endDate,
          ended,
          endAreaId
        } = entityData;
        const [beginYear, beginMonth, beginDay] = (0, _util.parseDate)(beginDate);
        const [endYear, endMonth, endDay] = (0, _util.parseDate)(endDate);
        return {
          beginAreaId,
          beginDate,
          beginDay,
          beginMonth,
          beginYear,
          endAreaId,
          endDate,
          endDay,
          endMonth,
          endYear,
          ended,
          genderId,
          typeId
        };
      }
    case 'Edition':
      return (0, _pick2.default)(entityData, ['editionGroupBbid', 'width', 'height', 'depth', 'weight', 'pages', 'formatId', 'statusId']);
    case 'Publisher':
      {
        const {
          typeId,
          areaId,
          beginDate,
          endDate,
          ended
        } = entityData;
        const [beginYear, beginMonth, beginDay] = (0, _util.parseDate)(beginDate);
        const [endYear, endMonth, endDay] = (0, _util.parseDate)(endDate);
        return {
          areaId,
          beginDate,
          beginDay,
          beginMonth,
          beginYear,
          endDate,
          endDay,
          endMonth,
          endYear,
          ended,
          typeId
        };
      }
    case 'EditionGroup':
    case 'Work':
      return (0, _pick2.default)(entityData, ['typeId']);
    case 'Series':
      return (0, _pick2.default)(entityData, ['entityType', 'orderingTypeId']);
    default:
      return null;
  }
}
/**
 * @param  {string} entityType - Entity type string
 * @returns {Object} - Returns entitySetMetadata (derivedSets)
*/
function getEntitySetMetadataByType(entityType) {
  if (entityType === 'Edition') {
    return [{
      entityIdField: 'languageSetId',
      idField: 'id',
      name: 'languageSet',
      propName: 'languages'
    }, {
      entityIdField: 'publisherSetId',
      idField: 'bbid',
      name: 'publisherSet',
      propName: 'publishers'
    }, {
      entityIdField: 'releaseEventSetId',
      idField: 'id',
      mutableFields: ['date', 'areaId'],
      name: 'releaseEventSet',
      propName: 'releaseEvents'
    }];
  } else if (entityType === 'Work') {
    return [{
      entityIdField: 'languageSetId',
      idField: 'id',
      name: 'languageSet',
      propName: 'languages'
    }];
  }
  return [];
}

/**
 * Returns all entity models defined in bookbrainz-data-js
 *
 * @param {object} orm - the BookBrainz ORM, initialized during app setup
 * @returns {object} - Object mapping model name to the entity model
*/
function getEntityModels(orm) {
  const {
    Author,
    Edition,
    EditionGroup,
    Publisher,
    Series,
    Work
  } = orm;
  return {
    Author,
    Edition,
    EditionGroup,
    Publisher,
    Series,
    Work
  };
}

/**
 * Retrieves the Bookshelf entity model with the given the model name
 *
 * @param {object} orm - the BookBrainz ORM, initialized during app setup
 * @param {string} type - Name or type of model
 * @throws {Error} Throws a custom error if the param 'type' does not
 * map to a model
 * @returns {object} - Bookshelf model object with the type specified in the
 * single param
*/
function getEntityModelByType(orm, type) {
  const entityModels = getEntityModels(orm);
  if (!entityModels[type]) {
    throw new Error('Unrecognized entity type');
  }
  return entityModels[type];
}

/**
 * Finds the bbid an entity redirects to, if any.
 * Do a recursive search in case the redirected bbid also redirects, etc.
 * @param {object} orm - the BookBrainz ORM, initialized during app setup
 * @param {string} bbid - The target entity's bbid.
 * @param {any} transacting - Optional ORM transaction object
 * @returns {string} The final bbid to redirect to
 */
async function recursivelyGetRedirectBBID(orm, bbid, transacting) {
  const redirectSQLQuery = `SELECT target_bbid FROM bookbrainz.entity_redirect WHERE source_bbid = '${bbid}'`;
  const redirectQueryResults = await (transacting || orm.bookshelf.knex).raw(redirectSQLQuery);
  if (redirectQueryResults.rows && redirectQueryResults.rows.length) {
    const redirectedBBID = redirectQueryResults.rows[0].target_bbid;
    return recursivelyGetRedirectBBID(orm, redirectedBBID);
  }
  return bbid;
}

/**
 * Fetches an entity with related data
 * @param {object} orm - the BookBrainz ORM, initialized during app setup
 * @param {string} entityType - The entity model name.
 * @param {string} bbid - The target entity's bbid.
 * @param {string[]} relations - Extra model relationships to fetch along with the entity
 * @returns {Promise} A Promise that resolves to the entity in JSON format
 */
async function getEntity(orm, entityType, bbid, relations = []) {
  // if bbid appears in entity_redirect table, use that bbid instead
  // Do a recursive search in case the redirected bbid also redirects, etc.
  const finalBBID = await recursivelyGetRedirectBBID(orm, bbid);
  const Model = getEntityModelByType(orm, entityType);
  const entity = await new Model({
    bbid: finalBBID
  }).fetch({
    require: true,
    withRelated: relations
  });
  return entity.toJSON();
}

/**
 * Fetches an entity's last known default alias from its revision parent.
 * This is necessary to display the name of a 'deleted' entity
 * @param {object} orm - the BookBrainz ORM, initialized during app setup
 * @param {string} entityType - The entity model name.
 * @param {string} bbid - The target entity's bbid.
 * @returns {Promise} The returned Promise returns the entity's
 * 					   parent default alias
 */
async function getEntityParentAlias(orm, entityType, bbid) {
  const rawSql = `
		SELECT alias.name,
			alias.sort_name,
			alias.id,
			alias.language_id,
			alias.primary
		FROM bookbrainz.${(0, _snakeCase2.default)(entityType)}
		LEFT JOIN bookbrainz.alias ON alias.id = default_alias_id
		WHERE bbid = '${bbid}' AND master = FALSE
		ORDER BY revision_id DESC
		LIMIT 1;
	`;

  // Query the database to get the parent revision default alias
  const queryResult = await orm.bookshelf.knex.raw(rawSql);
  if (!Array.isArray(queryResult.rows)) {
    return null;
  }
  const rows = queryResult.rows.map(rawQueryResult => {
    // Convert query keys to camelCase
    const queriedResult = (0, _mapKeys2.default)(rawQueryResult, (value, key) => (0, _camelCase2.default)(key));
    return queriedResult;
  });
  return rows[0];
}