Made API more RESTful, got modify working

pull/7/head
Sander Vocke 5 years ago
parent e07a76acc1
commit 74be187db2
  1. 34
      client/src/api.ts
  2. 6
      server/app.ts
  3. 6
      server/endpoints/ModifyArtistEndpointHandler.ts
  4. 12
      server/endpoints/ModifySongEndpointHandler.ts
  5. 8
      server/test/integration/flows/CreateArtistFlow.js
  6. 30
      server/test/integration/flows/CreateSongFlow.js
  7. 12
      server/test/integration/flows/ModifyArtistFlow.js

@ -17,8 +17,8 @@ export function checkQuerySongsRequest(req:any): boolean {
return true; return true;
} }
// Get song details. // Get song details (GET).
export const SongDetailsEndpoint = '/song/details/:id'; export const SongDetailsEndpoint = '/song/:id';
export interface SongDetailsRequest {} export interface SongDetailsRequest {}
export interface SongDetailsResponse { export interface SongDetailsResponse {
title: String, title: String,
@ -39,8 +39,8 @@ export function checkQueryArtistsRequest(req:any): boolean {
return true; return true;
} }
// Get artist details. // Get artist details (GET).
export const ArtistDetailsEndpoint = '/artist/details/:id'; export const ArtistDetailsEndpoint = '/artist/:id';
export interface ArtistDetailsRequest {} export interface ArtistDetailsRequest {}
export interface ArtistDetailsResponse { export interface ArtistDetailsResponse {
name: String name: String
@ -49,8 +49,8 @@ export function checkArtistDetailsRequest(req:any): boolean {
return true; return true;
} }
// Create a new song. // Create a new song (POST).
export const CreateSongEndpoint = '/song/create'; export const CreateSongEndpoint = '/song';
export interface CreateSongRequest { export interface CreateSongRequest {
title: String; title: String;
artistIds?: Number[]; artistIds?: Number[];
@ -64,18 +64,20 @@ export function checkCreateSongRequest(req:any): boolean {
"title" in req.body; "title" in req.body;
} }
// Modify an existing song. // Modify an existing song (PUT).
export const ModifySongEndpoint = '/song/modify'; export const ModifySongEndpoint = '/song/:id';
export interface ModifySongRequest extends CreateSongRequest { export interface ModifySongRequest {
id: Number; title?: String;
artistIds?: Number[];
albumIds?: Number[];
} }
export interface ModifySongResponse {} export interface ModifySongResponse {}
export function checkModifySongRequest(req:any): boolean { export function checkModifySongRequest(req:any): boolean {
return true; return true;
} }
// Create a new artist. // Create a new artist (POST).
export const CreateArtistEndpoint = '/artist/create'; export const CreateArtistEndpoint = '/artist';
export interface CreateArtistRequest { export interface CreateArtistRequest {
name: String; name: String;
songIds?: Number[]; songIds?: Number[];
@ -89,10 +91,10 @@ export function checkCreateArtistRequest(req:any): boolean {
"name" in req.body; "name" in req.body;
} }
// Modify an existing artist. // Modify an existing artist (PUT).
export const ModifyArtistEndpoint = '/artist/modify'; export const ModifyArtistEndpoint = '/artist/:id';
export interface ModifyArtistRequest extends CreateArtistRequest { export interface ModifyArtistRequest {
id: Number; name?: String,
} }
export interface ModifyArtistResponse {} export interface ModifyArtistResponse {}
export function checkModifyArtistRequest(req:any): boolean { export function checkModifyArtistRequest(req:any): boolean {

@ -14,12 +14,14 @@ import * as endpointTypes from './endpoints/types';
const invokeHandler = (handler:endpointTypes.EndpointHandler) => { const invokeHandler = (handler:endpointTypes.EndpointHandler) => {
return async (req: any, res: any) => { return async (req: any, res: any) => {
console.log("Incoming", req.method, " @ ", req.url);
await handler(req, res) await handler(req, res)
.catch((_e:endpointTypes.EndpointError) => { .catch((_e:endpointTypes.EndpointError) => {
let e:endpointTypes.EndpointError = _e; let e:endpointTypes.EndpointError = _e;
console.log("Error handling request: ", e.internalMessage); console.log("Error handling request: ", e.internalMessage);
res.sendStatus(e.httpStatus); res.sendStatus(e.httpStatus);
}) })
console.log("Finished handling", req.method, "@", req.url);
}; };
} }
@ -32,8 +34,8 @@ const SetupApp = (app: any) => {
app.get(api.QuerySongsEndpoint, invokeHandler(QuerySongsEndpointHandler)); app.get(api.QuerySongsEndpoint, invokeHandler(QuerySongsEndpointHandler));
app.post(api.CreateArtistEndpoint, invokeHandler(CreateArtistEndpointHandler)); app.post(api.CreateArtistEndpoint, invokeHandler(CreateArtistEndpointHandler));
app.get(api.QueryArtistsEndpoint, invokeHandler(QueryArtistsEndpointHandler)); app.get(api.QueryArtistsEndpoint, invokeHandler(QueryArtistsEndpointHandler));
app.post(api.ModifyArtistEndpoint, invokeHandler(ModifyArtistEndpointHandler)); app.put(api.ModifyArtistEndpoint, invokeHandler(ModifyArtistEndpointHandler));
app.post(api.ModifySongEndpoint, invokeHandler(ModifySongEndpointHandler)); app.put(api.ModifySongEndpoint, invokeHandler(ModifySongEndpointHandler));
app.get(api.SongDetailsEndpoint, invokeHandler(SongDetailsEndpointHandler)); app.get(api.SongDetailsEndpoint, invokeHandler(SongDetailsEndpointHandler));
app.get(api.ArtistDetailsEndpoint, invokeHandler(ArtistDetailsEndpointHandler)); app.get(api.ArtistDetailsEndpoint, invokeHandler(ArtistDetailsEndpointHandler));
} }

@ -13,12 +13,12 @@ export const ModifyArtistEndpointHandler: EndpointHandler = async (req: any, res
const reqObject:api.ModifyArtistRequest = req.body; const reqObject:api.ModifyArtistRequest = req.body;
await models.Artist.findAll({ await models.Artist.findAll({
where: { id: reqObject.id } where: { id: req.params.id }
}) })
.then(async (artists: any[]) => { .then(async (artists: any[]) => {
if (artists.length != 1) { if (artists.length != 1) {
const e: EndpointError = { const e: EndpointError = {
internalMessage: 'There is no artist with id ' + reqObject.id + '.', internalMessage: 'There is no artist with id ' + req.params.id + '.',
httpStatus: 400 httpStatus: 400
}; };
throw e; throw e;
@ -28,7 +28,7 @@ export const ModifyArtistEndpointHandler: EndpointHandler = async (req: any, res
await artist.save(); await artist.save();
}) })
.then(() => { .then(() => {
res.status(200); res.status(200).send({});
}) })
.catch(catchUnhandledErrors); .catch(catchUnhandledErrors);
} }

@ -57,12 +57,12 @@ export const ModifySongEndpointHandler: EndpointHandler = async (req: any, res:
// Start retrieving the song to modify. // Start retrieving the song to modify.
var songInstancePromise: Promise<any> = var songInstancePromise: Promise<any> =
models.Song.findAll({ models.Song.findAll({
where: { id: reqObject.id } where: { id: req.params.id }
}) })
.then((song: any[]) => { .then((song: any[]) => {
if (song.length != 1) { if (song.length != 1) {
const e: EndpointError = { const e: EndpointError = {
internalMessage: 'There is no song with id ' + reqObject.id + '.', internalMessage: 'There is no song with id ' + req.params.id + '.',
httpStatus: 400 httpStatus: 400
}; };
throw e; throw e;
@ -70,13 +70,13 @@ export const ModifySongEndpointHandler: EndpointHandler = async (req: any, res:
return song[0]; return song[0];
}); });
// Upon finish retrieving artists and albums, create the song and associate it. // Upon finish retrieving artists and albums, modify the song.
await Promise.all([artistInstancesPromise, albumInstancesPromise, songInstancePromise]) await Promise.all([artistInstancesPromise, albumInstancesPromise, songInstancePromise])
.then(async (values: any) => { .then(async (values: any) => {
var [artists, albums, song] = values; var [artists, albums, song] = values;
song.setArtists(artists); if(reqObject.artistIds) { song.setArtists(artists) };
song.setAlbums(albums); if(reqObject.albumIds) { song.setAlbums(albums) };
song.setTitle(reqObject.title); if(reqObject.title) { song.setTitle(reqObject.title) };
await song.save(); await song.save();
}) })
.then(() => { .then(() => {

@ -13,12 +13,12 @@ async function init() {
return app; return app;
} }
describe('POST /artist/create with no name', () => { describe('POST /artist with no name', () => {
it('should fail', done => { it('should fail', done => {
init().then((app) => { init().then((app) => {
chai chai
.request(app) .request(app)
.post('/artist/create') .post('/artist')
.send({}) .send({})
.end((err, res) => { .end((err, res) => {
expect(err).to.be.null; expect(err).to.be.null;
@ -29,12 +29,12 @@ describe('POST /artist/create with no name', () => {
}); });
}); });
describe('POST /artist/create with a correct request', () => { describe('POST /artist with a correct request', () => {
it('should succeed', done => { it('should succeed', done => {
init().then((app) => { init().then((app) => {
chai chai
.request(app) .request(app)
.post('/artist/create') .post('/artist')
.send({ .send({
name: "MyArtist" name: "MyArtist"
}) })

@ -13,12 +13,12 @@ async function init() {
return app; return app;
} }
describe('POST /song/create with no title', () => { describe('POST /song with no title', () => {
it('should fail', done => { it('should fail', done => {
init().then((app) => { init().then((app) => {
chai chai
.request(app) .request(app)
.post('/song/create') .post('/song')
.send({}) .send({})
.end((err, res) => { .end((err, res) => {
expect(err).to.be.null; expect(err).to.be.null;
@ -29,12 +29,12 @@ describe('POST /song/create with no title', () => {
}); });
}); });
describe('POST /song/create with only a title', () => { describe('POST /song with only a title', () => {
it('should return the first available id', done => { it('should return the first available id', done => {
init().then((app) => { init().then((app) => {
chai chai
.request(app) .request(app)
.post('/song/create') .post('/song')
.send({ .send({
title: "MySong" title: "MySong"
}) })
@ -50,12 +50,12 @@ describe('POST /song/create with only a title', () => {
}); });
}); });
describe('POST /song/create with a nonexistent artist Id', () => { describe('POST /song with a nonexistent artist Id', () => {
it('should fail', done => { it('should fail', done => {
init().then((app) => { init().then((app) => {
chai chai
.request(app) .request(app)
.post('/song/create') .post('/song')
.send({ .send({
title: "MySong", title: "MySong",
artistIds: [1] artistIds: [1]
@ -69,12 +69,12 @@ describe('POST /song/create with a nonexistent artist Id', () => {
}); });
}); });
describe('POST /song/create with an existing artist Id', () => { describe('POST /song with an existing artist Id', () => {
it('should succeed', done => { it('should succeed', done => {
init().then((app) => { init().then((app) => {
async function createArtist() { async function createArtist() {
await chai.request(app) await chai.request(app)
.post('/artist/create') .post('/artist')
.send({ .send({
name: "MyArtist" name: "MyArtist"
}) })
@ -88,7 +88,7 @@ describe('POST /song/create with an existing artist Id', () => {
async function createSong() { async function createSong() {
chai.request(app) chai.request(app)
.post('/song/create') .post('/song')
.send({ .send({
title: "MySong", title: "MySong",
artistIds: [1] artistIds: [1]
@ -109,12 +109,12 @@ describe('POST /song/create with an existing artist Id', () => {
}); });
}); });
describe('POST /song/create with two existing artist Ids', () => { describe('POST /song with two existing artist Ids', () => {
it('should succeed', done => { it('should succeed', done => {
init().then((app) => { init().then((app) => {
async function createArtist(name, expectId) { async function createArtist(name, expectId) {
await chai.request(app) await chai.request(app)
.post('/artist/create') .post('/artist')
.send({ .send({
name: name name: name
}) })
@ -128,7 +128,7 @@ describe('POST /song/create with two existing artist Ids', () => {
async function createSong() { async function createSong() {
chai.request(app) chai.request(app)
.post('/song/create') .post('/song')
.send({ .send({
title: "MySong", title: "MySong",
artistIds: [1, 2] artistIds: [1, 2]
@ -150,12 +150,12 @@ describe('POST /song/create with two existing artist Ids', () => {
}); });
}); });
describe('POST /song/create with an existent and a nonexistent artist Id', () => { describe('POST /song with an existent and a nonexistent artist Id', () => {
it('should fail', done => { it('should fail', done => {
init().then((app) => { init().then((app) => {
async function createArtist(name, expectId) { async function createArtist(name, expectId) {
await chai.request(app) await chai.request(app)
.post('/artist/create') .post('/artist')
.send({ .send({
name: name name: name
}) })
@ -169,7 +169,7 @@ describe('POST /song/create with an existent and a nonexistent artist Id', () =>
async function createSong() { async function createSong() {
chai.request(app) chai.request(app)
.post('/song/create') .post('/song')
.send({ .send({
title: "MySong", title: "MySong",
artistIds: [1, 2] artistIds: [1, 2]

@ -13,12 +13,12 @@ async function init() {
return app; return app;
} }
describe('POST /artist/modify on nonexistent artist', () => { describe('PUT /artist on nonexistent artist', () => {
it('should fail', done => { it('should fail', done => {
init().then((app) => { init().then((app) => {
chai chai
.request(app) .request(app)
.post('/artist/modify') .put('/artist/1')
.send({ .send({
id: 1, id: 1,
name: "NewArtistName" name: "NewArtistName"
@ -31,12 +31,12 @@ describe('POST /artist/modify on nonexistent artist', () => {
}); });
}); });
describe('POST /artist/modify with an existing artist', () => { describe('PUT /artist with an existing artist', () => {
it('should succeed', done => { it('should succeed', done => {
init().then((app) => { init().then((app) => {
async function createArtist(req) { async function createArtist(req) {
await req await req
.post('/artist/create') .post('/artist')
.send({ .send({
name: "MyArtist" name: "MyArtist"
}) })
@ -50,7 +50,7 @@ describe('POST /artist/modify with an existing artist', () => {
async function modifyArtist(req) { async function modifyArtist(req) {
await req await req
.post('/artist/modify') .put('/artist/1')
.send({ .send({
name: "MyNewArtist", name: "MyNewArtist",
id: 1 id: 1
@ -62,7 +62,7 @@ describe('POST /artist/modify with an existing artist', () => {
async function checkArtist(req) { async function checkArtist(req) {
await req await req
.get('/artist/details/1') .get('/artist/1')
.then((res) => { .then((res) => {
expect(res).to.have.status(200); expect(res).to.have.status(200);
expect(res.body).to.deep.equal({ expect(res.body).to.deep.equal({

Loading…
Cancel
Save