import * as api from '../../client/src/api'; import { EndpointError, EndpointHandler, catchUnhandledErrors } from './types'; import Knex from 'knex'; export const ModifyAlbumEndpointHandler: EndpointHandler = async (req: any, res: any, knex: Knex) => { if (!api.checkModifyAlbumRequest(req)) { const e: EndpointError = { internalMessage: 'Invalid ModifyAlbum request: ' + JSON.stringify(req.body), httpStatus: 400 }; throw e; } const reqObject: api.ModifyAlbumRequest = req.body; console.log("Modify Album:", reqObject); await knex.transaction(async (trx) => { try { // Start retrieving the album itself. const album = await trx.select('id') .from('albums') .where({ id: req.params.id }) .then((r: any) => (r && r[0]) ? r[0]['id'] : undefined); const newAlbum = { ...album, ...reqObject }; // Start retrieving artists. const artistIdsPromise = newAlbum.artistIds ? trx.select('artistId') .from('artists_albums') .whereIn('id', newAlbum.artistIds) .then((as: any) => as.map((a: any) => a['artistId'])) : (async () => { return undefined })(); // Start retrieving tags. const tagIdsPromise = newAlbum.tagIds ? trx.select('id') .from('albums_tags') .whereIn('id', newAlbum.tagIds) .then((ts: any) => ts.map((t: any) => t['tagId'])) : (async () => { return undefined })(); // Wait for the requests to finish. var [artists, tags] = await Promise.all([artistIdsPromise, tagIdsPromise]);; // Check that we found all objects we need. if ((newAlbum.artistIds && artists.length !== newAlbum.artistIds.length) || (newAlbum.tagIds && tags.length !== newAlbum.tagIds.length) || !album) { const e: EndpointError = { internalMessage: 'Not all albums and/or artists and/or tags exist for ModifyAlbum request: ' + JSON.stringify(req.body), httpStatus: 400 }; throw e; } // Modify the album. const modifyAlbumPromise = trx('albums') .where({ 'id': req.params.id }) .update({ name: newAlbum.name, storeLinks: JSON.stringify(newAlbum.storeLinks || []), }) // Remove unlinked artists. // TODO: test this! const removeUnlinkedArtists = artists ? trx('artists_albums') .where({ 'albumId': req.params.id }) .whereNotIn('artistId', newAlbum.artistIds || []) .delete() : undefined; // Remove unlinked tags. // TODO: test this! const removeUnlinkedTags = tags ? trx('albums_tags') .where({ 'albumId': req.params.id }) .whereNotIn('tagId', newAlbum.tagIds || []) .delete() : undefined; // Link new artists. // TODO: test this! const addArtists = artists ? trx('artists_albums') .where({ 'albumId': req.params.id }) .then((as: any) => as.map((a: any) => a['artistId'])) .then((doneArtistIds: number[]) => { // Get the set of artists that are not yet linked const toLink = artists.filter((id: number) => { return !doneArtistIds.includes(id); }); const insertObjects = toLink.map((artistId: number) => { return { artistId: artistId, albumId: req.params.id, } }) // Link them return Promise.all( insertObjects.map((obj: any) => trx('artists_albums').insert(obj) ) ); }) : undefined; // Link new tags. // TODO: test this! const addTags = tags ? trx('albums_tags') .where({ 'albumId': req.params.id }) .then((ts: any) => ts.map((t: any) => t['tagId'])) .then((doneTagIds: number[]) => { // Get the set of tags that are not yet linked const toLink = tags.filter((id: number) => { return !doneTagIds.includes(id); }); const insertObjects = toLink.map((tagId: number) => { return { tagId: tagId, albumId: req.params.id, } }) // Link them return Promise.all( insertObjects.map((obj: any) => trx('albums_tags').insert(obj) ) ); }) : undefined; // Wait for all operations to finish. await Promise.all([ modifyAlbumPromise, removeUnlinkedArtists, removeUnlinkedTags, addArtists, addTags ]); // Respond to the request. res.status(200).send(); } catch (e) { catchUnhandledErrors(e); trx.rollback(); } }) }