// TODO: this file is located in the client src folder because // otherwise, Create React App will refuse to compile it. // Putting it in the server folder or in its own folder makes more sense. // This file represents the API interface for Mudbase's back-end. // Each endpoint is described by its endpoint address, // a request structure, a response structure and // a checking function which determines request validity. export enum ItemType { Song = 0, Artist, Album, Tag } export interface ArtistDetails { artistId: Number, name: String, storeLinks?: String[], } export function isArtistDetails(q: any): q is ArtistDetails { return 'artistId' in q; } export interface TagDetails { tagId: Number, name: String, parent?: TagDetails, storeLinks?: String[], } export function isTagDetails(q: any): q is TagDetails { return 'tagId' in q; } export interface RankingDetails { rankingId: Number, type: ItemType, // The item type being ranked rankedId: Number, // The item being ranked context: ArtistDetails | TagDetails, value: Number, // The ranking (higher = better) } export function isRankingDetails(q: any): q is RankingDetails { return 'rankingId' in q; } export interface SongDetails { songId: Number, title: String, artists?: ArtistDetails[], tags?: TagDetails[], storeLinks?: String[], rankings?: RankingDetails[], } export function isSongDetails(q: any): q is SongDetails { return 'songId' in q; } // Query for items (POST). export const QueryEndpoint = '/query'; export enum QueryElemOp { And = "AND", Or = "OR", } export enum QueryFilterOp { Eq = "EQ", Ne = "NE", In = "IN", NotIn = "NOTIN", Like = "LIKE", } export enum QueryElemProperty { songTitle = "songTitle", songId = "songId", artistName = "artistName", albumName = "albumName", } export enum OrderBy { Name = 0 } export interface QueryElem { prop?: QueryElemProperty, propOperand?: any, propOperator?: QueryFilterOp, children?: QueryElem[] childrenOperator?: QueryElemOp, } export interface Ordering { orderBy: OrderBy, ascending: boolean, } export interface Query extends QueryElem { } export interface QueryRequest { query: Query, songOffset: number, songLimit: number, artistOffset: number, artistLimit: number, tagOffset: number, tagLimit: number, ordering: Ordering, } export interface QueryResponse { songs: SongDetails[], artists: ArtistDetails[], tags: TagDetails[], } export function checkQueryElem(elem: any): boolean { if (elem.childrenOperator && elem.children) { elem.children.forEach((child: any) => { if (!checkQueryElem(child)) { return false; } }); } return (elem.childrenOperator && elem.children) || (elem.prop && elem.propOperand && elem.propOperator) || Object.keys(elem).length == 0; } export function checkQueryRequest(req: any): boolean { return 'query' in req && 'songOffset' in req && 'songLimit' in req && 'artistOffset' in req && 'artistLimit' in req && 'tagOffset' in req && 'tagLimit' in req && checkQueryElem(req.query); } // Get song details (GET). export const SongDetailsEndpoint = '/song/:id'; export interface SongDetailsRequest { } export interface SongDetailsResponse { title: String, storeLinks: String[], artistIds: Number[], albumIds: Number[], tagIds: Number[], } export function checkSongDetailsRequest(req: any): boolean { return true; } // Get artist details (GET). export const ArtistDetailsEndpoint = '/artist/:id'; export interface ArtistDetailsRequest { } export interface ArtistDetailsResponse { name: String, tagIds: Number[], storeLinks: String[], } export function checkArtistDetailsRequest(req: any): boolean { return true; } // Create a new song (POST). export const CreateSongEndpoint = '/song'; export interface CreateSongRequest { title: String; artistIds?: Number[]; albumIds?: Number[]; tagIds?: Number[]; storeLinks?: String[]; } export interface CreateSongResponse { id: Number; } export function checkCreateSongRequest(req: any): boolean { return "body" in req && "title" in req.body; } // Modify an existing song (PUT). export const ModifySongEndpoint = '/song/:id'; export interface ModifySongRequest { title?: String; artistIds?: Number[]; albumIds?: Number[]; tagIds?: Number[]; storeLinks?: String[]; } export interface ModifySongResponse { } export function checkModifySongRequest(req: any): boolean { return true; } // Create a new artist (POST). export const CreateArtistEndpoint = '/artist'; export interface CreateArtistRequest { name: String; tagIds?: Number[]; storeLinks?: String[]; } export interface CreateArtistResponse { id: Number; } export function checkCreateArtistRequest(req: any): boolean { return "body" in req && "name" in req.body; } // Modify an existing artist (PUT). export const ModifyArtistEndpoint = '/artist/:id'; export interface ModifyArtistRequest { name?: String, tagIds?: Number[]; storeLinks?: String[], } export interface ModifyArtistResponse { } export function checkModifyArtistRequest(req: any): boolean { return true; } // Create a new tag (POST). export const CreateTagEndpoint = '/tag'; export interface CreateTagRequest { name: String; parentId?: Number; } export interface CreateTagResponse { id: Number; } export function checkCreateTagRequest(req: any): boolean { return "body" in req && "name" in req.body; } // Modify an existing tag (PUT). export const ModifyTagEndpoint = '/tag/:id'; export interface ModifyTagRequest { name?: String, parentId?: Number; } export interface ModifyTagResponse { } export function checkModifyTagRequest(req: any): boolean { return true; } // Get tag details (GET). export const TagDetailsEndpoint = '/tag/:id'; export interface TagDetailsRequest { } export interface TagDetailsResponse { name: String, parentId?: Number, } export function checkTagDetailsRequest(req: any): boolean { return true; }