Working on a refactor that allows multiple-to-multiple relations between songs, albums and artists.

pull/7/head
Sander Vocke 5 years ago
parent e6371d7d48
commit b7279dfdef
  1. 18
      client/src/api.ts
  2. 62
      server/endpoints/CreateSongEndpointHandler.ts
  3. 12
      server/endpoints/ListSongsEndpointHandler.ts
  4. 12
      server/models/album.js
  5. 21
      server/models/artist.js
  6. 23
      server/models/song.js

@ -13,8 +13,14 @@ export interface ListSongsRequest {}
export interface ListSongsResponseItem { export interface ListSongsResponseItem {
title: String; title: String;
id: Number; id: Number;
artistName: String; artists: {
artistId: Number; name: String;
id: Number;
}[];
albums: {
name: String;
id: Number;
}[];
} }
export interface ListSongsResponse extends Array<ListSongsResponseItem>{}; export interface ListSongsResponse extends Array<ListSongsResponseItem>{};
export function checkListSongsRequest(req:any): boolean { export function checkListSongsRequest(req:any): boolean {
@ -37,21 +43,23 @@ export function checkListArtistsRequest(req:any): boolean {
export const CreateSongEndpoint = '/song/create'; export const CreateSongEndpoint = '/song/create';
export interface CreateSongRequest { export interface CreateSongRequest {
title: String; title: String;
artistId: Number; artistIds?: Number[];
albumIds?: Number[];
} }
export interface CreateSongResponse { export interface CreateSongResponse {
id: Number; id: Number;
} }
export function checkCreateSongRequest(req:any): boolean { export function checkCreateSongRequest(req:any): boolean {
return "body" in req && return "body" in req &&
"title" in req.body && "title" in req.body;
"artistId" in req.body;
} }
// Create a new artist. // Create a new artist.
export const CreateArtistEndpoint = '/artist/create'; export const CreateArtistEndpoint = '/artist/create';
export interface CreateArtistRequest { export interface CreateArtistRequest {
name: String; name: String;
songIds?: Number[];
albumIds?: Number[];
} }
export interface CreateArtistResponse { export interface CreateArtistResponse {
id: Number; id: Number;

@ -8,27 +8,61 @@ export const CreateSongEndpointHandler = (req: any, res: any) => {
return; return;
} }
const reqObject: api.CreateSongRequest = req.body; const reqObject: api.CreateSongRequest = req.body;
console.log("Request create song: ", reqObject); // TODO: remove
// First check that the artist exists. // Start retrieving the artist instances to link the song to.
models.Artist.findAll({ var artistInstancePromises: Promise<any>[] = [];
where: { id: reqObject.artistId } reqObject.artistIds?.forEach((artistId: Number) => {
}) artistInstancePromises.push(
.then((artist: any[]) => { models.Artist.findAll({
if (artist.length != 1) { where: { id: artistId }
console.log('Invalid CreateSong request: ' + JSON.stringify(req.body) })
+ ". There is no artist with id " + reqObject.artistId + "."); .then((artist: any[]) => {
res.sendStatus(400); if (artist.length != 1) {
return; 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<any>[] = [];
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({ models.Song.create({
title: reqObject.title, title: reqObject.title,
ArtistId: reqObject.artistId
}) })
.then(Promise.all([
(song: any) => {
song.addArtists(artists);
},
(song: any) => {
song.addAlbums(albums);
}
]))
.then((song: any) => { .then((song: any) => {
const responseObject: api.CreateSongResponse = { const responseObject: api.CreateSongResponse = {
id: song.id id: song.id
}; };
res.status(200).send(responseObject); res.status(200).send(responseObject);
}) })
}) });
} }

@ -8,7 +8,7 @@ export const ListSongsEndpointHandler = (req: any, res: any) => {
return; return;
} }
models.Song.findAll({ models.Song.findAll({
include: [models.Artist] include: [models.Artist, models.Album]
}) })
.then((songs: any[]) => { .then((songs: any[]) => {
console.log(songs); console.log(songs);
@ -16,8 +16,14 @@ export const ListSongsEndpointHandler = (req: any, res: any) => {
return { return {
title: song.title, title: song.title,
id: song.id, id: song.id,
artistName: song.Artist.name, artists: song.Artists.map((artist: any) => { return {
artistId: song.ArtistId, name: artist.name,
id: artist.id,
}; }),
albums: song.Albums.map((album: any) => { return {
name: album.name,
id: album.id,
}; }),
}; };
}); });
res.send(response); res.send(response);

@ -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;
};

@ -1,11 +1,12 @@
module.exports = (sequelize, DataTypes) => { module.exports = (sequelize, DataTypes) => {
var Artist = sequelize.define('Artist', { var Artist = sequelize.define('Artist', {
name: DataTypes.STRING, name: DataTypes.STRING,
}); });
Artist.associate = function(models) { Artist.associate = function (models) {
models.Artist.hasMany(models.Song); models.Artist.belongsToMany(models.Song, { through: "SongArtists" });
}; models.Artist.belongsToMany(models.Album, { through: "AlbumArtists" })
};
return Artist;
}; return Artist;
};

@ -1,13 +1,12 @@
module.exports = (sequelize, DataTypes) => { module.exports = (sequelize, DataTypes) => {
var Song = sequelize.define('Song', { var Song = sequelize.define('Song', {
title: DataTypes.STRING, title: DataTypes.STRING,
}); });
Song.associate = function(models) { Song.associate = function (models) {
models.Song.belongsTo(models.Artist, { models.Song.belongsToMany(models.Artist, { through: "SongArtists" });
onDelete: "CASCADE" models.Song.belongsToMany(models.Album, { through: "SongAlbums" });
}); };
};
return Song;
return Song; };
};
Loading…
Cancel
Save