Expand query to support different levels of response detail.

editsong
Sander Vocke 5 years ago
parent 1148bc6bff
commit c28db21b18
  1. 17
      client/src/api.ts
  2. 35
      server/endpoints/Query.ts
  3. 93
      server/test/integration/flows/QueryFlow.js

@ -88,7 +88,12 @@ export enum QueryElemProperty {
albumStoreLinks = "albumStoreLinks", //Note: treated as a JSON string for filter operations albumStoreLinks = "albumStoreLinks", //Note: treated as a JSON string for filter operations
} }
export enum OrderByType { export enum OrderByType {
Name = 0, Name = 'name',
}
export enum QueryResponseType {
Details = 'details', // Returns detailed result items.
Ids = 'ids', // Returns IDs only.
Count = 'count', // Returns an item count only.
} }
export interface QueryElem { export interface QueryElem {
prop?: QueryElemProperty, prop?: QueryElemProperty,
@ -108,12 +113,13 @@ export interface QueryRequest {
query: Query, query: Query,
offsetsLimits: OffsetsLimits, offsetsLimits: OffsetsLimits,
ordering: Ordering, ordering: Ordering,
responseType: QueryResponseType
} }
export interface QueryResponse { export interface QueryResponse {
songs: SongDetails[], songs: SongDetails[] | number[] | number, // Details | IDs | count, depending on QueryResponseType
artists: ArtistDetails[], artists: ArtistDetails[] | number[] | number,
tags: TagDetails[], tags: TagDetails[] | number[] | number,
albums: AlbumDetails[], albums: AlbumDetails[] | number[] | number,
} }
export interface OffsetsLimits { export interface OffsetsLimits {
songOffset?: number, songOffset?: number,
@ -141,6 +147,7 @@ export function checkQueryRequest(req: any): boolean {
return 'query' in req return 'query' in req
&& 'offsetsLimits' in req && 'offsetsLimits' in req
&& 'ordering' in req && 'ordering' in req
&& 'responseType' in req
&& checkQueryElem(req.query); && checkQueryElem(req.query);
} }

@ -383,7 +383,16 @@ export const Query: EndpointHandler = async (req: any, res: any, knex: Knex) =>
songsAlbumsPromise, songsAlbumsPromise,
]); ]);
const response: api.QueryResponse = { var response: api.QueryResponse = {
songs: [],
artists: [],
albums: [],
tags: [],
};
switch (reqObject.responseType) {
case api.QueryResponseType.Details: {
response = {
songs: songs.map((song: any) => { songs: songs.map((song: any) => {
const id = song['songs.id']; const id = song['songs.id'];
return toApiSong(song, songsArtists[id], songsTags[id], songsAlbums[id]); return toApiSong(song, songsArtists[id], songsTags[id], songsAlbums[id]);
@ -397,6 +406,30 @@ export const Query: EndpointHandler = async (req: any, res: any, knex: Knex) =>
tags: tags.map((tag: any) => { tags: tags.map((tag: any) => {
return toApiTag(tag); return toApiTag(tag);
}), }),
};
break;
}
case api.QueryResponseType.Ids: {
response = {
songs: songs.map((song: any) => song['songs.id']),
artists: artists.map((artist: any) => artist['artists.id']),
albums: albums.map((album: any) => album['albums.id']),
tags: tags.map((tag: any) => tag['tags.id']),
};
break;
}
case api.QueryResponseType.Count: {
response = {
songs: songs.length,
artists: artists.length,
albums: albums.length,
tags: tags.length,
};
break;
}
default: {
throw new Error("Unimplemented response type.")
}
} }
console.log("Query repsonse", response); console.log("Query repsonse", response);

@ -39,10 +39,11 @@ describe('POST /query with no songs', () => {
}, },
'ordering': { 'ordering': {
'orderBy': { 'orderBy': {
'type': 0, 'type': 'name',
}, },
'ascending': true 'ascending': true
} },
'responseType': 'details',
}) })
expect(res).to.have.status(200); expect(res).to.have.status(200);
expect(res.body).to.deep.equal({ expect(res.body).to.deep.equal({
@ -114,10 +115,11 @@ describe('POST /query with several songs and filters', () => {
}, },
'ordering': { 'ordering': {
'orderBy': { 'orderBy': {
'type': 0, 'type': 'name',
}, },
'ascending': true 'ascending': true
} },
'responseType': 'details',
}) })
.then((res) => { .then((res) => {
expect(res).to.have.status(200); expect(res).to.have.status(200);
@ -145,10 +147,11 @@ describe('POST /query with several songs and filters', () => {
}, },
'ordering': { 'ordering': {
'orderBy': { 'orderBy': {
'type': 0, 'type': 'name',
}, },
'ascending': true 'ascending': true
} },
'responseType': 'details',
}) })
.then((res) => { .then((res) => {
expect(res).to.have.status(200); expect(res).to.have.status(200);
@ -176,10 +179,11 @@ describe('POST /query with several songs and filters', () => {
}, },
'ordering': { 'ordering': {
'orderBy': { 'orderBy': {
'type': 0, 'type': 'name',
}, },
'ascending': true 'ascending': true
} },
'responseType': 'details',
}) })
.then((res) => { .then((res) => {
expect(res).to.have.status(200); expect(res).to.have.status(200);
@ -207,10 +211,11 @@ describe('POST /query with several songs and filters', () => {
}, },
'ordering': { 'ordering': {
'orderBy': { 'orderBy': {
'type': 0, 'type': 'name',
}, },
'ascending': true 'ascending': true
} },
'responseType': 'details',
}) })
.then((res) => { .then((res) => {
expect(res).to.have.status(200); expect(res).to.have.status(200);
@ -248,10 +253,11 @@ describe('POST /query with several songs and filters', () => {
}, },
'ordering': { 'ordering': {
'orderBy': { 'orderBy': {
'type': 0, 'type': 'name',
}, },
'ascending': true 'ascending': true
} },
'responseType': 'details',
}) })
.then((res) => { .then((res) => {
expect(res).to.have.status(200); expect(res).to.have.status(200);
@ -279,10 +285,11 @@ describe('POST /query with several songs and filters', () => {
}, },
'ordering': { 'ordering': {
'orderBy': { 'orderBy': {
'type': 0, 'type': 'name',
}, },
'ascending': true 'ascending': true
} },
'responseType': 'details',
}) })
.then((res) => { .then((res) => {
expect(res).to.have.status(200); expect(res).to.have.status(200);
@ -295,6 +302,62 @@ describe('POST /query with several songs and filters', () => {
}); });
} }
async function checkResponseTypeIds(req) {
await req
.post('/query')
.send({
"query": {},
'offsetsLimits': {
'songOffset': 0,
'songLimit': 10,
},
'ordering': {
'orderBy': {
'type': 'name',
},
'ascending': true
},
'responseType': 'ids',
})
.then((res) => {
expect(res).to.have.status(200);
expect(res.body).to.deep.equal({
songs: [song1.songId, song2.songId, song3.songId],
artists: [],
tags: [],
albums: [],
});
});
}
async function checkResponseTypeCount(req) {
await req
.post('/query')
.send({
"query": {},
'offsetsLimits': {
'songOffset': 0,
'songLimit': 10,
},
'ordering': {
'orderBy': {
'type': 'name',
},
'ascending': true
},
'responseType': 'count',
})
.then((res) => {
expect(res).to.have.status(200);
expect(res.body).to.deep.equal({
songs: 3,
artists: 0,
tags: 0,
albums: 0,
});
});
}
let agent = await init(); let agent = await init();
let req = agent.keepOpen(); let req = agent.keepOpen();
try { try {
@ -309,6 +372,8 @@ describe('POST /query with several songs and filters', () => {
await checkArtistIdIn(req); await checkArtistIdIn(req);
await checkOrRelation(req); await checkOrRelation(req);
await checkStoreLinksLike(req); await checkStoreLinksLike(req);
await checkResponseTypeCount(req);
await checkResponseTypeIds(req);
} finally { } finally {
req.close(); req.close();
agent.close(); agent.close();

Loading…
Cancel
Save