package server import ( "encoding/json" "net/http" "strconv" "github.com/felamaslen/gmus-backend/pkg/database" "github.com/felamaslen/gmus-backend/pkg/logger" "github.com/felamaslen/gmus-backend/pkg/read" "github.com/felamaslen/gmus-backend/pkg/repository" "github.com/go-redis/redis" "github.com/jmoiron/sqlx" ) type ArtistsResponse struct { Artists []string `json:"artists"` } func routeFetchArtists(l *logger.Logger, rdb redis.Cmdable, w http.ResponseWriter, r *http.Request) error { db := database.GetConnection() artists, err := repository.SelectAllArtists(db) if err != nil { return err } response, err := json.Marshal(ArtistsResponse{ Artists: *artists, }) if err != nil { return err } w.Write(response) return nil } type AlbumsResponse struct { Artist string `json:"artist"` Albums []string `json:"albums"` } func routeFetchAlbums(l *logger.Logger, rdb redis.Cmdable, w http.ResponseWriter, r *http.Request) error { artist := r.URL.Query().Get("artist") db := database.GetConnection() albums, err := repository.SelectAlbumsByArtist(db, artist) if err != nil { return err } response, err := json.Marshal(AlbumsResponse{ Artist: artist, Albums: *albums, }) if err != nil { return err } w.Write(response) return nil } type SongsResponse struct { Artist string `json:"artist"` Songs *[]*read.SongExternal `json:"songs"` } func routeFetchSongs(l *logger.Logger, rdb redis.Cmdable, w http.ResponseWriter, r *http.Request) error { artist := r.URL.Query().Get("artist") db := database.GetConnection() songs, err := repository.SelectSongsByArtist(db, artist) if err != nil { return err } response, err := json.Marshal(SongsResponse{ Artist: artist, Songs: songs, }) if err != nil { return err } w.Write(response) return nil } func validateSongId(w http.ResponseWriter, r *http.Request) (id int, err error) { idRaw := r.URL.Query().Get("id") id, err = strconv.Atoi(idRaw) if err != nil { http.Error(w, "Must provide a valid id", http.StatusBadRequest) } else if id < 1 { http.Error(w, "id must be non-negative", http.StatusBadRequest) } return } func routeFetchSongInfo(l *logger.Logger, rdb redis.Cmdable, w http.ResponseWriter, r *http.Request) error { id, err := validateSongId(w, r) if err != nil { return nil } db := database.GetConnection() songs, err := repository.SelectSong(db, []int{id}) if err != nil { return err } if len(*songs) == 0 { http.Error(w, "Song not found", http.StatusNotFound) return nil } song := (*songs)[0] response, err := json.Marshal(read.SongExternal{ Id: id, TrackNumber: song.TrackNumber, Title: song.Title, Artist: song.Artist, Album: song.Album, Duration: song.Duration, }) if err != nil { return err } w.Write(response) return nil } func routeFetchMultiSongInfo(l *logger.Logger, rdb redis.Cmdable, w http.ResponseWriter, r *http.Request) error { idsArray := r.URL.Query()["ids"] if len(idsArray) == 0 { http.Error(w, "Must provide valid list of IDs", http.StatusBadRequest) return nil } var ids []int for _, id := range idsArray { idInt, err := strconv.Atoi(id) if err != nil { http.Error(w, "All IDs must be numeric", http.StatusBadRequest) return nil } if idInt < 1 { http.Error(w, "All IDs must be positive integers", http.StatusBadRequest) return nil } ids = append(ids, idInt) } songs, err := repository.SelectSong(database.GetConnection(), ids) if err != nil { return err } songsArray := []read.SongExternal{} for _, song := range *songs { songsArray = append(songsArray, read.SongExternal{ Id: song.Id, TrackNumber: song.TrackNumber, Title: song.Title, Artist: song.Artist, Album: song.Album, Duration: song.Duration, }) } response, err := json.Marshal(songsArray) if err != nil { return err } w.Write(response) return nil } type NullResponse struct { Id int `json:"id"` } func respondWithSongOrNull(db *sqlx.DB, w http.ResponseWriter, song *read.Song) error { if song.Id == 0 { response, _ := json.Marshal(NullResponse{}) w.Write(response) return nil } response, err := json.Marshal(read.SongExternal{ Id: song.Id, TrackNumber: song.TrackNumber, Title: song.Title, Artist: song.Artist, Album: song.Album, Duration: song.Duration, }) if err != nil { return err } w.Write(response) return nil } func routeFetchNextSong(l *logger.Logger, rdb redis.Cmdable, w http.ResponseWriter, r *http.Request) error { id, err := validateSongId(w, r) if err != nil { return nil } db := database.GetConnection() nextSong, err := repository.GetNextSong(db, id) if err != nil { return err } if err := respondWithSongOrNull(db, w, nextSong); err != nil { return err } return nil } func routeFetchPrevSong(l *logger.Logger, rdb redis.Cmdable, w http.ResponseWriter, r *http.Request) error { id, err := validateSongId(w, r) if err != nil { return nil } db := database.GetConnection() prevSong, err := repository.GetPrevSong(db, id) if err != nil { return err } if err := respondWithSongOrNull(db, w, prevSong); err != nil { return err } return nil }