Got basic query endpoint.

pull/7/head
Sander Vocke 5 years ago
parent 74be187db2
commit 6a728ae17b
  1. 43
      client/src/api.ts
  2. 5
      server/app.ts
  3. 45
      server/endpoints/QuerySongsEndpointHandler.ts
  4. 3
      server/test/integration/flows/ModifyArtistFlow.js
  5. 73
      server/test/integration/flows/QuerySongsFlow.js

@ -7,14 +7,51 @@
// a request structure, a response structure and
// a checking function which determines request validity.
// Query for songs.
// Query for songs (POST).
export const QuerySongsEndpoint = '/song/query';
export interface QuerySongsRequest {}
export enum SongQueryElemOp {
And = "AND",
Or = "OR",
}
export enum SongQueryFilterOp {
Eq = "EQ",
Ne = "NE",
In = "IN",
NotIn = "NOTIN"
}
export enum SongQueryElemProperty {
id = "id",
artistIds = "artistIds",
albumIds = "albumIds"
}
export interface SongQueryElem {
prop?: SongQueryElemProperty,
propOperand?: any,
propOperator?: SongQueryFilterOp,
children?: SongQueryElem[]
childrenOperator?: SongQueryElemOp,
}
export interface SongQuery extends SongQueryElem {}
export interface QuerySongsRequest {
query: SongQuery
}
export interface QuerySongsResponse {
ids: Number[]
}
export function checkQuerySongsElem(elem:any): boolean {
if(elem.childrenOperator && elem.children) {
elem.children.forEach((child:any) => {
if(!checkQuerySongsElem(child)) {
return false;
}
});
}
return (elem.childrenOperator && elem.children) ||
(elem.prop && elem.propOperand && elem.propOperator) ||
Object.keys(elem).length == 0;
}
export function checkQuerySongsRequest(req:any): boolean {
return true;
return "query" in req && checkQuerySongsElem(req.query);
}
// Get song details (GET).

@ -16,6 +16,7 @@ const invokeHandler = (handler:endpointTypes.EndpointHandler) => {
return async (req: any, res: any) => {
console.log("Incoming", req.method, " @ ", req.url);
await handler(req, res)
.catch(endpointTypes.catchUnhandledErrors)
.catch((_e:endpointTypes.EndpointError) => {
let e:endpointTypes.EndpointError = _e;
console.log("Error handling request: ", e.internalMessage);
@ -31,9 +32,9 @@ const SetupApp = (app: any) => {
// Set up REST API endpoints
app.post(api.CreateSongEndpoint, invokeHandler(CreateSongEndpointHandler));
app.get(api.QuerySongsEndpoint, invokeHandler(QuerySongsEndpointHandler));
app.post(api.QuerySongsEndpoint, invokeHandler(QuerySongsEndpointHandler));
app.post(api.CreateArtistEndpoint, invokeHandler(CreateArtistEndpointHandler));
app.get(api.QueryArtistsEndpoint, invokeHandler(QueryArtistsEndpointHandler));
app.post(api.QueryArtistsEndpoint, invokeHandler(QueryArtistsEndpointHandler));
app.put(api.ModifyArtistEndpoint, invokeHandler(ModifyArtistEndpointHandler));
app.put(api.ModifySongEndpoint, invokeHandler(ModifySongEndpointHandler));
app.get(api.SongDetailsEndpoint, invokeHandler(SongDetailsEndpointHandler));

@ -1,7 +1,46 @@
const models = require('../models');
const { Op } = require("sequelize");
import * as api from '../../client/src/api';
import { EndpointError, EndpointHandler, catchUnhandledErrors } from './types';
const getSequelizeWhere = (queryElem: api.SongQueryElem) => {
var and = [];
var sequelizeOps:any = {};
sequelizeOps[api.SongQueryFilterOp.Eq] = Op.eq;
sequelizeOps[api.SongQueryFilterOp.Ne] = Op.ne;
sequelizeOps[api.SongQueryFilterOp.In] = Op.in;
sequelizeOps[api.SongQueryFilterOp.NotIn] = Op.notIn;
sequelizeOps[api.SongQueryElemOp.And] = Op.and;
sequelizeOps[api.SongQueryElemOp.Or] = Op.or;
var sequelizeProps:any = {};
sequelizeProps[api.SongQueryElemProperty.id] = "id";
sequelizeProps[api.SongQueryElemProperty.artistIds] = "artistIds";
sequelizeProps[api.SongQueryElemProperty.albumIds] = "albumIds";
if (queryElem.prop && queryElem.propOperator && queryElem.propOperand) {
const prop = sequelizeProps[queryElem.prop];
const op = sequelizeOps[queryElem.propOperator];
var filter:any = {};
filter[op] = queryElem.propOperand;
var where:any = {};
where[prop] = filter;
and.push(where);
}
if (queryElem.childrenOperator && queryElem.children) {
const children = queryElem.children.map((child: api.SongQueryElem) => getSequelizeWhere(child));
const op = sequelizeOps[queryElem.childrenOperator];
var where:any = {};
where[op] = children;
and.push(where)
}
return {
[Op.and]: and
};
}
export const QuerySongsEndpointHandler: EndpointHandler = async (req: any, res: any) => {
if (!api.checkQuerySongsRequest(req)) {
const e: EndpointError = {
@ -10,7 +49,11 @@ export const QuerySongsEndpointHandler: EndpointHandler = async (req: any, res:
};
throw e;
}
await models.Song.findAll()
const reqObject: api.QuerySongsRequest = req.body;
await models.Song.findAll({
where: getSequelizeWhere(reqObject.query)
})
.then((songs: any[]) => {
const response: api.QuerySongsResponse = {
ids: songs.map((song: any) => {

@ -73,8 +73,7 @@ describe('PUT /artist with an existing artist', () => {
var req = chai.request(app).keepOpen();
init()
.then(() => createArtist(req))
createArtist(req)
.then(() => modifyArtist(req))
.then(() => checkArtist(req))
.then(() => req.close())

@ -0,0 +1,73 @@
const chai = require('chai');
const chaiHttp = require('chai-http');
const express = require('express');
const models = require('../../../models');
import { SetupApp } from '../../../app';
import { expect } from 'chai';
async function init() {
chai.use(chaiHttp);
const app = express();
SetupApp(app);
await models.sequelize.sync({ force: true });
return app;
}
describe('POST /song/query with no songs', () => {
it('should give empty list', done => {
init().then((app) => {
chai
.request(app)
.post('/song/query')
.send({
'query': {}
})
.end((err, res) => {
expect(err).to.be.null;
expect(res).to.have.status(200);
expect(res.body).to.deep.equal({
ids: []
});
done();
});
});
});
});
describe('POST /song/query with several songs', () => {
it('should give empty list', done => {
init().then((app) => {
async function createSong(req) {
await req
.post('/song')
.send({
title: "Song"
})
.then((res) => {
expect(res).to.have.status(200);
});
}
async function checkSongs(req) {
await req
.post('/song/query')
.send({ "query": {} })
.then((res) => {
expect(res).to.have.status(200);
expect(res.body).to.deep.equal({
ids: [1, 2, 3]
});
});
}
var req = chai.request(app).keepOpen();
createSong(req)
.then(() => createSong(req))
.then(() => createSong(req))
.then(() => checkSongs(req))
.then(() => req.close())
.then(done)
});
});
});
Loading…
Cancel
Save