diff --git a/src/client/components/pages/parts/search-results.js b/src/client/components/pages/parts/search-results.js index c38291be0..da5d7c470 100644 --- a/src/client/components/pages/parts/search-results.js +++ b/src/client/components/pages/parts/search-results.js @@ -29,6 +29,12 @@ import {genEntityIconHTMLElement} from '../../../helpers/entity'; const {Alert, Badge, Button, ButtonGroup, Table} = bootstrap; +// Main entities have a BBID but some other indexed types +// have an ID field instead (collections, editors, areas) +function getId(entity) { + return entity.bbid ?? entity.id; +} + /** * Renders the document and displays the 'SearchResults' page. * @returns {ReactElement} a HTML document which displays the SearchResults. @@ -89,8 +95,8 @@ class SearchResults extends React.Component { // eslint-disable-next-line react/no-access-state-in-setstate const oldSelected = this.state.selected; let newSelected; - if (oldSelected.find(selected => selected.bbid === entity.bbid)) { - newSelected = oldSelected.filter(selected => selected.bbid !== entity.bbid); + if (oldSelected.find(selected => getId(selected) === getId(entity))) { + newSelected = oldSelected.filter(selected => getId(selected) !== getId(entity)); } else { newSelected = [...oldSelected, entity]; @@ -148,6 +154,7 @@ class SearchResults extends React.Component { if (!result) { return null; } + const id = getId(result); const name = result.defaultAlias ? result.defaultAlias.name : '(unnamed)'; @@ -160,19 +167,19 @@ class SearchResults extends React.Component { const disambiguation = result.disambiguation ? ({result.disambiguation.comment}) : ''; // No redirect link for Area entity results const link = result.type === 'Area' ? - `//musicbrainz.org/area/${result.bbid}` : - `/${_kebabCase(result.type)}/${result.bbid}`; + `//musicbrainz.org/area/${id}` : + `/${_kebabCase(result.type)}/${id}`; /* eslint-disable react/jsx-no-bind */ return ( - + { !this.props.condensed && { this.props.user ? selected.bbid === result.bbid)} + checked={this.state.selected.find(selected => getId(selected) === id)} className="checkboxes" type="checkbox" onChange={() => this.toggleRow(result)} @@ -212,7 +219,7 @@ class SearchResults extends React.Component { this.props.user ?
selected.bbid)} + bbids={this.state.selected.map(getId)} closeModalAndShowMessage={this.closeModalAndShowMessage} entityType={this.state.selected[0]?.type} handleCloseModal={this.onCloseModal} diff --git a/src/common/helpers/search.js b/src/common/helpers/search.ts similarity index 75% rename from src/common/helpers/search.js rename to src/common/helpers/search.ts index 9ef5b446f..e1092311e 100644 --- a/src/common/helpers/search.js +++ b/src/common/helpers/search.ts @@ -17,10 +17,11 @@ */ /* eslint-disable camelcase */ -import * as commonUtils from '../../common/helpers/utils'; +import * as commonUtils from './utils'; import {camelCase, isString, snakeCase, upperFirst} from 'lodash'; import ElasticSearch from '@elastic/elasticsearch'; +import type {EntityTypeString} from 'bookbrainz-data/lib/types/entity'; import httpStatus from 'http-status'; import log from 'log'; @@ -48,6 +49,37 @@ function sanitizeEntityType(type) { return snakeCase(type); } +type IndexableEntities = EntityTypeString | 'Editor' | 'Collection' | 'Area'; +const commonProperties = ['bbid', 'id', 'name', 'type', 'disambiguation']; + +/* We don't currently want to index the entire Model in ElasticSearch, + which contains a lot of fields we don't use as well as some internal props (_pivot props) + This utility function prepares the Model into a minimal object that will be indexed +*/ +export function getDocumentToIndex(entity:any, entityType: IndexableEntities) { + const additionalProperties = []; + switch (entityType) { + case 'Work': + additionalProperties.push('authors'); + break; + default: + break; + } + let aliases = entity.related('aliasSet')?.related('aliases')?.toJSON({ignorePivot: true, visible: 'name'}); + if (!aliases) { + // Some models don't have the same aliasSet structure, i.e. Collection, Editor, Area, … + const name = entity.get('name'); + aliases = {name}; + } + const identifiers = entity.related('identifierSet')?.related('identifiers')?.toJSON({ignorePivot: true, visible: 'value'}); + + return { + ...entity.toJSON({ignorePivot: true, visible: commonProperties.concat(additionalProperties)}), + aliases, + identifiers: identifiers ?? null + }; +} + async function _fetchEntityModelsForESResults(orm, results) { const {Area, Editor, UserCollection} = orm; @@ -60,10 +92,10 @@ async function _fetchEntityModelsForESResults(orm, results) { // Special cases first if (entityStub.type === 'Area') { - const area = await Area.forge({gid: entityStub.bbid}) + const area = await Area.forge({gid: entityStub.id}) .fetch({withRelated: ['areaType']}); - const areaJSON = area.toJSON(); + const areaJSON = area.toJSON({omitPivot: true}); const areaParents = await area.parents(); areaJSON.defaultAlias = { name: areaJSON.name @@ -75,27 +107,27 @@ async function _fetchEntityModelsForESResults(orm, results) { return areaJSON; } if (entityStub.type === 'Editor') { - const editor = await Editor.forge({id: entityStub.bbid}) + const editor = await Editor.forge({id: entityStub.id}) .fetch(); - const editorJSON = editor.toJSON(); + const editorJSON = editor.toJSON({omitPivot: true}); editorJSON.defaultAlias = { name: editorJSON.name }; editorJSON.type = 'Editor'; - editorJSON.bbid = entityStub.bbid; + editorJSON.id = entityStub.id; return editorJSON; } if (entityStub.type === 'Collection') { - const collection = await UserCollection.forge({id: entityStub.bbid}) + const collection = await UserCollection.forge({id: entityStub.id}) .fetch(); - const collectionJSON = collection.toJSON(); + const collectionJSON = collection.toJSON({omitPivot: true}); collectionJSON.defaultAlias = { name: collectionJSON.name }; collectionJSON.type = 'Collection'; - collectionJSON.bbid = entityStub.bbid; + collectionJSON.id = entityStub.id; return collectionJSON; } // Regular entity @@ -103,7 +135,7 @@ async function _fetchEntityModelsForESResults(orm, results) { const entity = await model.forge({bbid: entityStub.bbid}) .fetch({require: false, withRelated: ['defaultAlias.language', 'disambiguation', 'aliasSet.aliases', 'identifierSet.identifiers', 'relationshipSet.relationships.source', 'relationshipSet.relationships.target', 'relationshipSet.relationships.type', 'annotation']}); - const entityJSON = entity?.toJSON(); + const entityJSON = entity?.toJSON({omitPivot: true}); if (entityJSON && entityJSON.relationshipSet) { entityJSON.relationshipSet.relationships = await Promise.all(entityJSON.relationshipSet.relationships.map(async (rel) => { rel.source = await commonUtils.getEntity(orm, rel.source.bbid, rel.source.type); @@ -145,7 +177,7 @@ export async function _bulkIndexEntities(entities) { const bulkOperations = entitiesToIndex.reduce((accumulator, entity) => { accumulator.push({ index: { - _id: entity.bbid, + _id: entity.bbid ?? entity.id, _index, _type: snakeCase(entity.type) } @@ -160,19 +192,19 @@ export async function _bulkIndexEntities(entities) { // eslint-disable-next-line no-await-in-loop const response = await _client.bulk({ body: bulkOperations - }); + }).catch(error => { log.error('error bulk indexing entities for search:', error); }); /* * In case of failed index operations, the promise won't be rejected; * instead, we have to inspect the response and respond to any failures * individually. */ - if (response.errors === true) { + if (response?.errors === true) { entitiesToIndex = response.items.reduce((accumulator, item) => { // We currently only handle queue overrun if (item.index.status === httpStatus.TOO_MANY_REQUESTS) { const failedEntity = entities.find( - (element) => element.bbid === item.index._id + (element) => (element.bbid ?? element.id) === item.index._id ); accumulator.push(failedEntity); @@ -223,7 +255,7 @@ export async function autocomplete(orm, query, type, size = 42) { else { queryBody = { match: { - 'aliasSet.aliases.name.autocomplete': { + 'aliases.name.autocomplete': { minimum_should_match: '80%', query } @@ -248,26 +280,28 @@ export async function autocomplete(orm, query, type, size = 42) { // eslint-disable-next-line consistent-return export function indexEntity(entity) { + const entityType = entity.get('type'); + const document = getDocumentToIndex(entity, entityType); if (entity) { return _client.index({ - body: entity, - id: entity.bbid, + body: document, + id: entity.get('bbid') || entity.get('id'), index: _index, - type: snakeCase(entity.type) + type: snakeCase(entityType) }).catch(error => { log.error('error indexing entity for search:', error); }); } } export function deleteEntity(entity) { return _client.delete({ - id: entity.bbid, + id: entity.bbid ?? entity.id, index: _index, type: snakeCase(entity.type) }).catch(error => { log.error('error deleting entity from index:', error); }); } export function refreshIndex() { - return _client.indices.refresh({index: _index}); + return _client.indices.refresh({index: _index}).catch(error => { log.error('error refreshing search index:', error); }); } export async function generateIndex(orm) { @@ -276,7 +310,7 @@ export async function generateIndex(orm) { mappings: { _default_: { properties: { - 'aliasSet.aliases': { + aliases: { properties: { name: { fields: { @@ -291,8 +325,7 @@ export async function generateIndex(orm) { }, type: 'text' } - }, - type: 'object' + } }, authors: { analyzer: 'trigrams', @@ -341,7 +374,8 @@ export async function generateIndex(orm) { type: 'ngram' } } - } + }, + 'index.mapping.ignore_malformed': true } }; @@ -384,7 +418,7 @@ export async function generateIndex(orm) { {model: EditionGroup, relations: []}, {model: Publisher, relations: ['area']}, {model: Series, relations: ['seriesOrderingType']}, - {model: Work, relations: ['workType', 'relationshipSet.relationships.type']} + {model: Work, relations: ['relationshipSet.relationships.type']} ]; // Update the indexed entries for each entity type @@ -400,6 +434,7 @@ export async function generateIndex(orm) { ); const entityLists = await Promise.all(behaviorPromise); /* eslint-disable @typescript-eslint/no-unused-vars */ + const entityFetchOrder:EntityTypeString[] = ['Author', 'Edition', 'EditionGroup', 'Publisher', 'Series', 'Work']; const [authorsCollection, editionCollection, editionGroupCollection, @@ -413,40 +448,40 @@ export async function generateIndex(orm) { const relationshipSet = workEntity.related('relationshipSet'); if (relationshipSet) { const authorWroteWorkRels = relationshipSet.related('relationships')?.filter(relationshipModel => relationshipModel.get('typeId') === 8); - const authorNames = authorWroteWorkRels.map(relationshipModel => { + const authorNames = []; + authorWroteWorkRels.forEach(relationshipModel => { // Search for the Author in the already fetched BookshelfJS Collection const source = authorsCollection.get(relationshipModel.get('sourceBbid')); - if (!source) { - // This shouldn't happen, but just in case - return null; + const name = source?.related('defaultAlias')?.get('name'); + if (name) { + authorNames.push(name); } - return source.toJSON().defaultAlias.name; }); workEntity.set('authors', authorNames); } }); // Index all the entities - for (const entityList of entityLists) { - const listArray = entityList.toJSON(); + entityLists.forEach((entityList, idx) => { + const entityType:EntityTypeString = entityFetchOrder[idx]; + const listArray = entityList.map(entity => getDocumentToIndex(entity, entityType)); listIndexes.push(_processEntityListForBulk(listArray)); - } + }); + await Promise.all(listIndexes); const areaCollection = await Area.forge() .fetchAll(); - const areas = areaCollection.toJSON(); + const areas = areaCollection.toJSON({omitPivot: true}); - /** To index names, we use aliasSet.aliases.name and bbid, which Areas don't have. - * We massage the area to return a similar format as BB entities - */ - const processedAreas = areas.map((area) => new Object({ - aliasSet: { - aliases: [ - {name: area.name} - ] - }, - bbid: area.gid, + /** To index names, we use aliases.name and type, which Areas don't have. + * We massage the area to return a similar format as BB entities + */ + const processedAreas = areas.map((area) => ({ + aliases: [ + {name: area.name} + ], + id: area.gid, type: 'Area' })); await _processEntityListForBulk(processedAreas); @@ -455,36 +490,31 @@ export async function generateIndex(orm) { // no bots .where('type_id', 1) .fetchAll(); - const editors = editorCollection.toJSON(); - - /** To index names, we use aliasSet.aliases.name and bbid, which Editors don't have. - * We massage the editor to return a similar format as BB entities - */ - const processedEditors = editors.map((editor) => new Object({ - aliasSet: { - aliases: [ - {name: editor.name} - ] - }, - bbid: editor.id, + const editors = editorCollection.toJSON({omitPivot: true}); + + /** To index names, we use aliases.name and type, which Editors don't have. + * We massage the editor to return a similar format as BB entities + */ + const processedEditors = editors.map((editor) => ({ + aliases: [ + {name: editor.name} + ], + id: editor.id, type: 'Editor' })); await _processEntityListForBulk(processedEditors); const userCollections = await UserCollection.forge().where({public: true}) .fetchAll(); - const userCollectionsJSON = userCollections.toJSON(); - - /** To index names, we use aliasSet.aliases.name and bbid, which UserCollections don't have. - * We massage the editor to return a similar format as BB entities - */ - const processedCollections = userCollectionsJSON.map((collection) => new Object({ - aliasSet: { - aliases: [ - {name: collection.name} - ] - }, - bbid: collection.id, + const userCollectionsJSON = userCollections.toJSON({omitPivot: true}); + + /** To index names, we use aliases.name and type, which UserCollections don't have. + * We massage the editor to return a similar format as BB entities + */ + const processedCollections = userCollectionsJSON.map((collection) => ({ + aliases: [ + {name: collection.name} + ], id: collection.id, type: 'Collection' })); @@ -495,7 +525,7 @@ export async function generateIndex(orm) { export async function checkIfExists(orm, name, type) { const {bookshelf} = orm; - const bbids = await new Promise((resolve, reject) => { + const bbids:string[] = await new Promise((resolve, reject) => { bookshelf.transaction(async (transacting) => { try { const result = await orm.func.alias.getBBIDsWithMatchingAlias( @@ -535,10 +565,10 @@ export function searchByName(orm, name, type, size, from) { query: { multi_match: { fields: [ - 'aliasSet.aliases.name^3', - 'aliasSet.aliases.name.search', + 'aliases.name^3', + 'aliases.name.search', 'disambiguation', - 'identifierSet.identifiers.value' + 'identifiers.value' ], minimum_should_match: '80%', query: name, diff --git a/src/server/helpers/collectionRouteUtils.js b/src/server/helpers/collectionRouteUtils.js index bd018365b..24b8c534c 100644 --- a/src/server/helpers/collectionRouteUtils.js +++ b/src/server/helpers/collectionRouteUtils.js @@ -16,11 +16,10 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -import * as handler from '../helpers/handler'; import * as search from '../../common/helpers/search'; import {camelCase, differenceWith, isEqual, toLower, upperFirst} from 'lodash'; import {BadRequestError} from '../../common/helpers/error'; - +import log from 'log'; /** * A handler for create or edit actions on collections. @@ -89,22 +88,11 @@ export async function collectionCreateOrEditHandler(req, res, next) { // if it's new or the name is changed, // we need to update this collection in ElasticSearch index if (isNew || res.locals.collection.name !== newCollection.get('name')) { - const collectionPromiseForES = new Promise((resolve) => { - const collectionForES = { - aliasSet: { - aliases: [ - {name: newCollection.get('name')} - ] - }, - bbid: newCollection.get('id'), - id: newCollection.get('id'), - type: 'Collection' - }; - resolve(collectionForES); - }); - return handler.sendPromiseResult(res, collectionPromiseForES, search.indexEntity); + // Type needed for indexing + newCollection.set('type', 'Collection'); + await search.indexEntity(newCollection); } - return res.send(newCollection.toJSON()); + return res.status(200).send(newCollection.toJSON()); } catch (err) { return next(err); diff --git a/src/server/routes/editor.tsx b/src/server/routes/editor.tsx index dd6aa8145..e0ffbf335 100644 --- a/src/server/routes/editor.tsx +++ b/src/server/routes/editor.tsx @@ -20,7 +20,6 @@ import * as auth from '../helpers/auth'; import * as commonUtils from '../../common/helpers/utils'; import * as error from '../../common/helpers/error'; -import * as handler from '../helpers/handler'; import * as propHelpers from '../../client/helpers/props'; import * as search from '../../common/helpers/search'; import {AdminActionType, PrivilegeType} from '../../common/helpers/privileges-utils'; @@ -42,6 +41,7 @@ import _ from 'lodash'; import express from 'express'; import {getOrderedCollectionsForEditorPage} from '../helpers/collections'; import {getOrderedRevisionForEditorPage} from '../helpers/revisions'; +import log from 'log'; import target from '../templates/target'; @@ -122,8 +122,8 @@ function isCurrentUser(reqUserID, sessionUser) { return reqUserID === sessionUser.id; } -router.post('/edit/handler', auth.isAuthenticatedForHandler, (req, res) => { - async function runAsync() { +router.post('/edit/handler', auth.isAuthenticatedForHandler, async (req, res) => { + try { const {Editor} = req.app.locals.orm; if (!isCurrentUser(req.body.id, req.user)) { @@ -140,10 +140,10 @@ router.post('/edit/handler', auth.isAuthenticatedForHandler, (req, res) => { throw new error.NotFoundError('Editor not found', req); }); if (editor.get('areaId') === req.body.areaId && - editor.get('bio') === req.body.bio && - editor.get('name') === req.body.name && - editor.get('titleUnlockId') === req.body.title && - editor.get('genderId') === req.body.genderId + editor.get('bio') === req.body.bio && + editor.get('name') === req.body.name && + editor.get('titleUnlockId') === req.body.title && + editor.get('genderId') === req.body.genderId ) { throw new error.FormSubmissionError('No change to the profile'); } @@ -164,19 +164,18 @@ router.post('/edit/handler', auth.isAuthenticatedForHandler, (req, res) => { }); res.locals.user.name = req.body.name; + const editorJSON = modifiedEditor.toJSON(); - return { - aliasSet: { - aliases: [ - {name: editorJSON.name} - ] - }, - bbid: editorJSON.id, - type: 'Editor' - }; - } - handler.sendPromiseResult(res, runAsync(), search.indexEntity); + // Type needed for indexing + modifiedEditor.set('type', 'Editor'); + await search.indexEntity(modifiedEditor); + + return res.status(200).send(editorJSON); + } + catch (err) { + return error.sendErrorAsJSON(res, err); + } }); router.post('/privs/edit/handler', auth.isAuthenticatedForHandler, auth.isAuthorized(ADMIN), async (req, res, next) => { diff --git a/src/server/routes/entity/entity.tsx b/src/server/routes/entity/entity.tsx index 5839cac14..21ebee34c 100644 --- a/src/server/routes/entity/entity.tsx +++ b/src/server/routes/entity/entity.tsx @@ -478,7 +478,7 @@ export function handleDelete( editorJSON.id, body.note ); - return savedMainEntity.toJSON(); + return savedMainEntity.toJSON({omitPivot: true}); }); return handler.sendPromiseResult(res, entityDeletePromise, search.deleteEntity); @@ -1047,7 +1047,7 @@ export async function indexAutoCreatedEditionGroup(orm, newEdition, transacting) transacting, withRelated: 'aliasSet.aliases' }); - await search.indexEntity(editionGroup.toJSON()); + await search.indexEntity(editionGroup.toJSON({omitPivot: true})); } catch (err) { log.error('Could not reindex edition group after edition creation:', err); @@ -1165,33 +1165,28 @@ export async function processSingleEntity(formBody, JSONEntity, reqSession, await indexAutoCreatedEditionGroup(orm, savedMainEntity, transacting); } - const entityJSON = savedMainEntity.toJSON(); - if (entityJSON && entityJSON.relationshipSet) { - entityJSON.relationshipSet.relationships = await Promise.all(entityJSON.relationshipSet.relationships.map(async (rel) => { + const entityRelationships = savedMainEntity.related('relationshipSet')?.related('relationships'); + if (savedMainEntity.get('type') === 'Work' && entityRelationships?.length) { + const authorsOfWork = await Promise.all(entityRelationships.toJSON().filter( + // "Author wrote Work" relationship + (relation) => relation.typeId === 8 + ).map(async (relationshipJSON) => { try { - rel.source = await commonUtils.getEntity(orm, rel.source.bbid, rel.source.type, {require: false, transacting}); - rel.target = await commonUtils.getEntity(orm, rel.target.bbid, rel.target.type, {require: false, transacting}); + const {source} = relationshipJSON; + const sourceEntity = await commonUtils.getEntity( + orm, source.bbid, source.type, {require: false, transacting} + ); + return sourceEntity.name; } catch (err) { log.error(err); } - return rel; + return null; })); // Attach a work's authors for search indexing - if (savedMainEntity.get('type') === 'Work') { - const authorsOfWork = entityJSON.relationshipSet.relationships - .filter( - // "Author wrote Work" relationship - (relation) => relation.typeId === 8 - ) - .map((relation) => { - const {source} = relation; - return source.name; - }); - entityJSON.authors = authorsOfWork; - } + savedMainEntity.set('authors', authorsOfWork.filter(Boolean)); } - return entityJSON; + return savedMainEntity; } catch (err) { log.error(err); @@ -1199,7 +1194,7 @@ export async function processSingleEntity(formBody, JSONEntity, reqSession, } } -export function handleCreateOrEditEntity( +export async function handleCreateOrEditEntity( req: PassportRequest, res: $Response, entityType: EntityTypeString, @@ -1209,16 +1204,15 @@ export function handleCreateOrEditEntity( const {orm}: {orm?: any} = req.app.locals; const editorJSON = req.user; const {bookshelf} = orm; - const entityEditPromise = bookshelf.transaction((transacting) => + const savedEntityModel = await bookshelf.transaction((transacting) => processSingleEntity(req.body, res.locals.entity, req.session, entityType, orm, editorJSON, derivedProps, isMergeOperation, transacting)); - const achievementPromise = entityEditPromise.then( - (entityJSON) => processAchievement(orm, editorJSON.id, entityJSON) - ); - return handler.sendPromiseResult( - res, - achievementPromise, - search.indexEntity - ); + const entityJSON = savedEntityModel.toJSON(); + + await processAchievement(orm, editorJSON.id, entityJSON); + + await search.indexEntity(savedEntityModel); + + return res.status(200).send(entityJSON); } type AliasEditorT = { diff --git a/src/server/routes/entity/process-unified-form.ts b/src/server/routes/entity/process-unified-form.ts index 7b3726904..9a1fdf581 100644 --- a/src/server/routes/entity/process-unified-form.ts +++ b/src/server/routes/entity/process-unified-form.ts @@ -2,6 +2,7 @@ import * as achievement from '../../helpers/achievement'; import type {Request as $Request, Response as $Response} from 'express'; import {FormSubmissionError, sendErrorAsJSON} from '../../../common/helpers/error'; +import {_bulkIndexEntities, getDocumentToIndex} from '../../../common/helpers/search'; import {authorToFormState, transformNewForm as authorTransform} from './author'; import {editionGroupToFormState, transformNewForm as editionGroupTransform} from './edition-group'; import {editionToFormState, transformNewForm as editionTransform} from './edition'; @@ -13,7 +14,6 @@ import type { EntityTypeString } from 'bookbrainz-data/lib/types/entity'; import _ from 'lodash'; -import {_bulkIndexEntities} from '../../../common/helpers/search'; import log from 'log'; import {processSingleEntity} from './entity'; @@ -206,7 +206,7 @@ export async function handleCreateMultipleEntities( } // log indexing error if any try { - _bulkIndexEntities(processedAchievements); + _bulkIndexEntities(processedEntities.map(en => getDocumentToIndex(en, en.get('type')))); } catch (err) { log.error(err); diff --git a/src/server/routes/register.js b/src/server/routes/register.js index 4a88d7ffa..56936a3eb 100644 --- a/src/server/routes/register.js +++ b/src/server/routes/register.js @@ -93,65 +93,58 @@ router.get('/details', middleware.loadGenders, (req, res) => { })); }); -router.post('/handler', (req, res) => { - const {Editor, EditorType} = req.app.locals.orm; +router.post('/handler', async (req, res) => { + try { + const {Editor, EditorType} = req.app.locals.orm; + + // Check whether the user is logged in - if so, redirect to profile page + if (req.user) { + return res.redirect(`/editor/${req.user.id}`); + } + + if (!req.session.mbProfile) { + return res.redirect('/auth'); + } + + // Fetch the default EditorType from the database + const editorType = await EditorType.forge({label: 'Editor'}) + .fetch({require: true}); + + // Create a new Editor and add to the database + + const editor = await new Editor({ + cachedMetabrainzName: req.session.mbProfile.sub, + genderId: req.body.gender, + metabrainzUserId: req.session.mbProfile.metabrainz_user_id, + name: req.body.displayName, + typeId: editorType.id + }) + .save(); - // Check whether the user is logged in - if so, redirect to profile page - if (req.user) { - return res.redirect(`/editor/${req.user.id}`); - } + req.session.mbProfile = null; - if (!req.session.mbProfile) { - return res.redirect('/auth'); - } + const editorJSON = editor.toJSON(); - // Fetch the default EditorType from the database - const registerPromise = EditorType.forge({label: 'Editor'}) - .fetch({require: true}) - .then( - // Create a new Editor and add to the database - (editorType) => - new Editor({ - cachedMetabrainzName: req.session.mbProfile.sub, - genderId: req.body.gender, - metabrainzUserId: req.session.mbProfile.metabrainz_user_id, - name: req.body.displayName, - typeId: editorType.id - }) - .save() - ) - .then((editor) => { - req.session.mbProfile = null; - const editorJSON = editor.toJSON(); - - // in ES index, we're storing the editor as entity - const editorForES = {}; - editorForES.bbid = editorJSON.id; - editorForES.aliasSet = { - aliases: [ - {name: editorJSON.name} - ] - }; - editorForES.type = 'Editor'; - return editorForES; - }) - .catch((err) => { - log.debug(err); - - if (_.isMatch(err, {constraint: 'editor_name_key'})) { - throw new error.FormSubmissionError( - 'That username already exists - please try using another,' + - ' or contact us to have your existing BookBrainz account' + - ' linked to a MusicBrainz account.' - ); - } - - throw new error.FormSubmissionError( - 'Something went wrong when registering, please try again!' - ); - }); - - return handler.sendPromiseResult(res, registerPromise, search.indexEntity); + // Type needed for indexing + editor.set('type', 'Editor'); + await search.indexEntity(editor); + + return res.status(200).send(editorJSON); + } + catch (err) { + if (_.isMatch(err, {constraint: 'editor_name_key'})) { + return error.sendErrorAsJSON(res, new error.FormSubmissionError( + 'That username already exists - please try using another,' + + ' or contact us to have your existing BookBrainz account' + + ' linked to a MusicBrainz account.' + )); + } + log.error(err); + + return error.sendErrorAsJSON(res, new error.FormSubmissionError( + 'Something went wrong when registering, please try again!' + )); + } }); export default router;