From b7279dfdefc6c238bdb42f1dc7ffd410e69aa2d4 Mon Sep 17 00:00:00 2001 From: Sander Vocke Date: Tue, 14 Jul 2020 16:56:55 +0200 Subject: [PATCH] Working on a refactor that allows multiple-to-multiple relations between songs, albums and artists. --- client/src/api.ts | 18 ++++-- server/endpoints/CreateSongEndpointHandler.ts | 62 ++++++++++++++----- server/endpoints/ListSongsEndpointHandler.ts | 12 +++- server/models/album.js | 12 ++++ server/models/artist.js | 21 ++++--- server/models/song.js | 23 ++++--- 6 files changed, 104 insertions(+), 44 deletions(-) create mode 100644 server/models/album.js diff --git a/client/src/api.ts b/client/src/api.ts index fd9e2dc..58ce1c1 100644 --- a/client/src/api.ts +++ b/client/src/api.ts @@ -13,8 +13,14 @@ export interface ListSongsRequest {} export interface ListSongsResponseItem { title: String; id: Number; - artistName: String; - artistId: Number; + artists: { + name: String; + id: Number; + }[]; + albums: { + name: String; + id: Number; + }[]; } export interface ListSongsResponse extends Array{}; export function checkListSongsRequest(req:any): boolean { @@ -37,21 +43,23 @@ export function checkListArtistsRequest(req:any): boolean { export const CreateSongEndpoint = '/song/create'; export interface CreateSongRequest { title: String; - artistId: Number; + artistIds?: Number[]; + albumIds?: Number[]; } export interface CreateSongResponse { id: Number; } export function checkCreateSongRequest(req:any): boolean { return "body" in req && - "title" in req.body && - "artistId" in req.body; + "title" in req.body; } // Create a new artist. export const CreateArtistEndpoint = '/artist/create'; export interface CreateArtistRequest { name: String; + songIds?: Number[]; + albumIds?: Number[]; } export interface CreateArtistResponse { id: Number; diff --git a/server/endpoints/CreateSongEndpointHandler.ts b/server/endpoints/CreateSongEndpointHandler.ts index cf1bba9..25a976a 100644 --- a/server/endpoints/CreateSongEndpointHandler.ts +++ b/server/endpoints/CreateSongEndpointHandler.ts @@ -8,27 +8,61 @@ export const CreateSongEndpointHandler = (req: any, res: any) => { return; } const reqObject: api.CreateSongRequest = req.body; - console.log("Request create song: ", reqObject); // TODO: remove - // First check that the artist exists. - models.Artist.findAll({ - where: { id: reqObject.artistId } - }) - .then((artist: any[]) => { - if (artist.length != 1) { - console.log('Invalid CreateSong request: ' + JSON.stringify(req.body) - + ". There is no artist with id " + reqObject.artistId + "."); - res.sendStatus(400); - return; - } + + // Start retrieving the artist instances to link the song to. + var artistInstancePromises: Promise[] = []; + reqObject.artistIds?.forEach((artistId: Number) => { + artistInstancePromises.push( + models.Artist.findAll({ + where: { id: artistId } + }) + .then((artist: any[]) => { + if (artist.length != 1) { + throw 'There is no artist with id ' + artistId + '.'; + } + return artist[0]; + }) + ); + }); + var artistInstancesPromise = Promise.all(artistInstancePromises); + + // Start retrieving the album instances to link the song to. + var albumInstancePromises: Promise[] = []; + reqObject.albumIds?.forEach((albumId: Number) => { + albumInstancePromises.push( + models.Album.findAll({ + where: { id: albumId } + }) + .then((album: any[]) => { + if (album.length != 1) { + throw 'There is no album with id ' + albumId + '.'; + } + return album[0]; + }) + ); + }); + var albumInstancesPromise = Promise.all(albumInstancePromises); + + // Upon finish retrieving artists and albums, create the song and associate it. + Promise.all([artistInstancesPromise, albumInstancesPromise]) + .then((values: any) => { + var [artists, albums] = values; models.Song.create({ title: reqObject.title, - ArtistId: reqObject.artistId }) + .then(Promise.all([ + (song: any) => { + song.addArtists(artists); + }, + (song: any) => { + song.addAlbums(albums); + } + ])) .then((song: any) => { const responseObject: api.CreateSongResponse = { id: song.id }; res.status(200).send(responseObject); }) - }) + }); } \ No newline at end of file diff --git a/server/endpoints/ListSongsEndpointHandler.ts b/server/endpoints/ListSongsEndpointHandler.ts index dce496d..30d20ac 100644 --- a/server/endpoints/ListSongsEndpointHandler.ts +++ b/server/endpoints/ListSongsEndpointHandler.ts @@ -8,7 +8,7 @@ export const ListSongsEndpointHandler = (req: any, res: any) => { return; } models.Song.findAll({ - include: [models.Artist] + include: [models.Artist, models.Album] }) .then((songs: any[]) => { console.log(songs); @@ -16,8 +16,14 @@ export const ListSongsEndpointHandler = (req: any, res: any) => { return { title: song.title, id: song.id, - artistName: song.Artist.name, - artistId: song.ArtistId, + artists: song.Artists.map((artist: any) => { return { + name: artist.name, + id: artist.id, + }; }), + albums: song.Albums.map((album: any) => { return { + name: album.name, + id: album.id, + }; }), }; }); res.send(response); diff --git a/server/models/album.js b/server/models/album.js new file mode 100644 index 0000000..92edb7e --- /dev/null +++ b/server/models/album.js @@ -0,0 +1,12 @@ +module.exports = (sequelize, DataTypes) => { + var Album = sequelize.define('Album', { + name: DataTypes.STRING, + }); + + Album.associate = function (models) { + models.Album.belongsToMany(models.Song, { through: "SongAlbums" }) + models.Album.belongsToMany(models.Artist, { through: "AlbumArtists" }) + }; + + return Album; +}; \ No newline at end of file diff --git a/server/models/artist.js b/server/models/artist.js index 6ddc6f9..74bca35 100644 --- a/server/models/artist.js +++ b/server/models/artist.js @@ -1,11 +1,12 @@ module.exports = (sequelize, DataTypes) => { - var Artist = sequelize.define('Artist', { - name: DataTypes.STRING, - }); - - Artist.associate = function(models) { - models.Artist.hasMany(models.Song); - }; - - return Artist; - }; \ No newline at end of file + var Artist = sequelize.define('Artist', { + name: DataTypes.STRING, + }); + + Artist.associate = function (models) { + models.Artist.belongsToMany(models.Song, { through: "SongArtists" }); + models.Artist.belongsToMany(models.Album, { through: "AlbumArtists" }) + }; + + return Artist; +}; \ No newline at end of file diff --git a/server/models/song.js b/server/models/song.js index 899c095..6293546 100644 --- a/server/models/song.js +++ b/server/models/song.js @@ -1,13 +1,12 @@ module.exports = (sequelize, DataTypes) => { - var Song = sequelize.define('Song', { - title: DataTypes.STRING, - }); - - Song.associate = function(models) { - models.Song.belongsTo(models.Artist, { - onDelete: "CASCADE" - }); - }; - - return Song; - }; \ No newline at end of file + var Song = sequelize.define('Song', { + title: DataTypes.STRING, + }); + + Song.associate = function (models) { + models.Song.belongsToMany(models.Artist, { through: "SongArtists" }); + models.Song.belongsToMany(models.Album, { through: "SongAlbums" }); + }; + + return Song; +}; \ No newline at end of file