import * as api from '../../client/src/api'; import { EndpointError, EndpointHandler, catchUnhandledErrors } from './types'; import Knex from 'knex'; import asJson from '../lib/asJson'; export const GetArtist: EndpointHandler = async (req: any, res: any, knex: Knex) => { if (!api.checkArtistDetailsRequest(req)) { const e: EndpointError = { internalMessage: 'Invalid GetArtist request: ' + JSON.stringify(req.body), httpStatus: 400 }; throw e; } const { id: userId } = req.user; try { const tagIds = Array.from(new Set((await knex.select('tagId') .from('artists_tags') .where({ 'artistId': req.params.id }) ).map((tag: any) => tag['tagId']))); const results = await knex.select(['id', 'name', 'storeLinks']) .from('artists') .where({ 'user': userId }) .where({ 'id': req.params.id }); if (results[0]) { const response: api.ArtistDetailsResponse = { name: results[0].name, tagIds: tagIds, storeLinks: asJson(results[0].storeLinks), } await res.send(response); } else { await res.status(404).send({}); } } catch (e) { catchUnhandledErrors(e) } } export const PostArtist: EndpointHandler = async (req: any, res: any, knex: Knex) => { if (!api.checkCreateArtistRequest(req)) { const e: EndpointError = { internalMessage: 'Invalid PostArtist request: ' + JSON.stringify(req.body), httpStatus: 400 }; throw e; } const reqObject: api.CreateArtistRequest = req.body; const { id: userId } = req.user; console.log("User ", userId, ": Create artist ", reqObject) await knex.transaction(async (trx) => { try { // Retrieve tag instances to link the artist to. const tags: number[] = reqObject.tagIds ? Array.from(new Set( (await trx.select('id').from('tags') .where({ 'user': userId }) .whereIn('id', reqObject.tagIds)) .map((tag: any) => tag['id']) )) : []; if (reqObject.tagIds && tags && tags.length !== reqObject.tagIds.length) { const e: EndpointError = { internalMessage: 'Not all tags exist for CreateArtist request: ' + JSON.stringify(req.body), httpStatus: 400 }; throw e; } // Create the artist. const artistId = (await trx('artists') .insert({ name: reqObject.name, storeLinks: JSON.stringify(reqObject.storeLinks || []), user: userId, }) .returning('id') // Needed for Postgres )[0]; // Link the tags via the linking table. if (tags && tags.length) { await trx('artists_tags').insert( tags.map((tagId: number) => { return { artistId: artistId, tagId: tagId, } }) ) } const responseObject: api.CreateSongResponse = { id: artistId }; await res.status(200).send(responseObject); } catch (e) { catchUnhandledErrors(e); trx.rollback(); } }); } export const PutArtist: EndpointHandler = async (req: any, res: any, knex: Knex) => { if (!api.checkModifyArtistRequest(req)) { const e: EndpointError = { internalMessage: 'Invalid PutArtist request: ' + JSON.stringify(req.body), httpStatus: 400 }; throw e; } const reqObject: api.ModifyArtistRequest = req.body; const { id: userId } = req.user; console.log("User ", userId, ": Put Artist ", reqObject); await knex.transaction(async (trx) => { try { const artistId = parseInt(req.params.id); // Start retrieving the artist itself. const artistPromise = trx.select('id') .from('artists') .where({ 'user': userId }) .where({ id: artistId }) .then((r: any) => (r && r[0]) ? r[0]['id'] : undefined) // Start retrieving tags. const tagIdsPromise = reqObject.tagIds ? trx.select('id') .from('artists_tags') .whereIn('id', reqObject.tagIds) .then((ts: any) => ts.map((t: any) => t['tagId'])) : (async () => { return undefined })(); // Wait for the requests to finish. var [artist, tags] = await Promise.all([artistPromise, tagIdsPromise]);; // Check that we found all objects we need. if ((reqObject.tagIds && tags.length !== reqObject.tagIds.length) || !artist) { const e: EndpointError = { internalMessage: 'Not all artists and/or tags exist for ModifyArtist request: ' + JSON.stringify(req.body), httpStatus: 400 }; throw e; } // Modify the artist. var update: any = {}; if ("name" in reqObject) { update["name"] = reqObject.name; } if ("storeLinks" in reqObject) { update["storeLinks"] = JSON.stringify(reqObject.storeLinks || []); } const modifyArtistPromise = trx('artists') .where({ 'user': userId }) .where({ 'id': artistId }) .update(update) // Remove unlinked tags. // TODO: test this! const removeUnlinkedTags = tags ? trx('artists_tags') .where({ 'artistId': artistId }) .whereNotIn('tagId', reqObject.tagIds || []) .delete() : undefined; // Link new tags. // TODO: test this! const addTags = tags ? trx('artists_tags') .where({ 'artistId': artistId }) .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, artistId: artistId, } }) // Link them return Promise.all( insertObjects.map((obj: any) => trx('artists_tags').insert(obj) ) ); }) : undefined; // Wait for all operations to finish. await Promise.all([ modifyArtistPromise, removeUnlinkedTags, addTags ]); // Respond to the request. res.status(200).send(); } catch (e) { catchUnhandledErrors(e); trx.rollback(); } }) }