// 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 AlbumDetails { albumId: number, name: string, storeLinks?: string[], } export function isAlbumDetails(q: any): q is ArtistDetails { return 'albumId' 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[], albums?: AlbumDetails[], 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", artistId = "artistId", albumName = "albumName", albumId = "albumId", tagId = "tagId", } export enum OrderByType { Name = 0, } export interface QueryElem { prop?: QueryElemProperty, propOperand?: any, propOperator?: QueryFilterOp, children?: QueryElem[] childrenOperator?: QueryElemOp, } export interface Ordering { orderBy: { type: OrderByType, } ascending: boolean, } export interface Query extends QueryElem { } export interface QueryRequest { query: Query, offsetsLimits: OffsetsLimits, ordering: Ordering, } export interface QueryResponse { songs: SongDetails[], artists: ArtistDetails[], tags: TagDetails[], albums: AlbumDetails[], } export interface OffsetsLimits { songOffset?: number, songLimit?: number, artistOffset?: number, artistLimit?: number, tagOffset?: number, tagLimit?: number, albumOffset?: number, albumLimit?: number, } 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) || ("prop" in elem && "propOperand" in elem && "propOperator" in elem) || Object.keys(elem).length === 0; } export function checkQueryRequest(req: any): boolean { return 'query' in req && 'offsetsLimits' in req && 'ordering' 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 album (POST). export const CreateAlbumEndpoint = '/album'; export interface CreateAlbumRequest { name: string; tagIds?: number[]; artistIds?: number[]; storeLinks?: string[]; } export interface CreateAlbumResponse { id: number; } export function checkCreateAlbumRequest(req: any): boolean { return "body" in req && "name" in req.body; } // Modify an existing album (PUT). export const ModifyAlbumEndpoint = '/album/:id'; export interface ModifyAlbumRequest { name?: string; tagIds?: number[]; artistIds?: number[]; storeLinks?: string[]; } export interface ModifyAlbumResponse { } export function checkModifyAlbumRequest(req: any): boolean { return true; } // Get album details (GET). export const AlbumDetailsEndpoint = '/album/:id'; export interface AlbumDetailsRequest { } export interface AlbumDetailsResponse { name: string; tagIds: number[]; artistIds: number[]; songIds: number[]; storeLinks: string[]; } export function checkAlbumDetailsRequest(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; } // Delete tag (DELETE). export const DeleteTagEndpoint = '/tag/:id'; export interface DeleteTagRequest { } export interface DeleteTagResponse { } export function checkDeleteTagRequest(req: any): boolean { return true; }