const models = require('../models'); const { Op } = require("sequelize"); import * as api from '../../client/src/api'; import { EndpointError, EndpointHandler, catchUnhandledErrors } from './types'; const sequelizeOps: any = { [api.SongQueryFilterOp.Eq]: Op.eq, [api.SongQueryFilterOp.Ne]: Op.ne, [api.SongQueryFilterOp.In]: Op.in, [api.SongQueryFilterOp.NotIn]: Op.notIn, [api.SongQueryFilterOp.Like]: Op.like, [api.SongQueryElemOp.And]: Op.and, [api.SongQueryElemOp.Or]: Op.or, }; const sequelizeProps: any = { [api.SongQueryElemProperty.title]: "title", [api.SongQueryElemProperty.id]: "id", [api.SongQueryElemProperty.artistNames]: "$Artists.name$", [api.SongQueryElemProperty.albumNames]: "$Albums.name$", }; // Returns the "where" clauses for Sequelize, per object type. const getSequelizeWhere = (queryElem: api.SongQueryElem) => { var where: any = { [Op.and]: [] }; if (queryElem.prop && queryElem.propOperator && queryElem.propOperand) { // Visit a filter-like subquery leaf. where[Op.and].push({ [sequelizeProps[queryElem.prop]]: { [sequelizeOps[queryElem.propOperator]]: queryElem.propOperand } }); } if (queryElem.childrenOperator && queryElem.children) { // Recursively visit a nested subquery. const children = queryElem.children.map((child: api.SongQueryElem) => getSequelizeWhere(child)); where[Op.and].push({ [sequelizeOps[queryElem.childrenOperator]]: children }); } return where; } export const QuerySongsEndpointHandler: EndpointHandler = async (req: any, res: any) => { if (!api.checkQuerySongsRequest(req.body)) { const e: EndpointError = { internalMessage: 'Invalid QuerySongs request: ' + JSON.stringify(req.body), httpStatus: 400 }; throw e; } const reqObject: api.QuerySongsRequest = req.body; try { console.log('Song query:', reqObject.query, "where: ", getSequelizeWhere(reqObject.query)) const songs = await models.Song.findAll({ // NOTE: have to disable limit and offset because of bug: https://github.com/sequelize/sequelize/issues/11938. // Custom pagination is implemented before responding. where: getSequelizeWhere(reqObject.query), include: [models.Artist, models.Album, models.Tag], //limit: reqObject.limit, //offset: reqObject.offset, }) const response: api.QuerySongsResponse = { songs: await Promise.all(songs.map(async (song: any) => { const artists = await song.getArtists(); const tags = await song.getTags(); return { id: song.id, title: song.title, storeLinks: song.storeLinks, artists: artists.map((artist: any) => { return { id: artist.id, name: artist.name, } }), tags: tags.map((tag: any) => { return { id: tag.id, name: tag.name, } }), }; }).slice(reqObject.offset, reqObject.offset + reqObject.limit)) // TODO: custom pagination due to bug mentioned above }; res.send(response); } catch (e) { catchUnhandledErrors(e); } }