184 lines
5.2 KiB
Go
184 lines
5.2 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"github.com/gin-gonic/gin"
|
|
"gorm.io/gorm/clause"
|
|
"log/slog"
|
|
"net/http"
|
|
"strconv"
|
|
)
|
|
|
|
// Pagination helper middleware
|
|
// This just checks and inserts some default pagination values into the request context
|
|
func paginator() gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
queryPage := c.DefaultQuery("page", "1")
|
|
queryPageSize := c.DefaultQuery("page_size", "10")
|
|
|
|
// add some defaults here if user gives us empty values
|
|
if queryPage == "" {
|
|
queryPage = "1"
|
|
}
|
|
if queryPageSize == "" {
|
|
queryPageSize = "10"
|
|
}
|
|
|
|
page, err := strconv.Atoi(queryPage)
|
|
if err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"Error": "Failed to convert page parameter to number"})
|
|
c.Abort()
|
|
return
|
|
}
|
|
|
|
pageSize, err := strconv.Atoi(queryPageSize)
|
|
if err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"Error": "Failed to convert page_size parameter to number"})
|
|
c.Abort()
|
|
return
|
|
}
|
|
|
|
if page < 1 {
|
|
page = 1
|
|
}
|
|
|
|
if pageSize < 1 {
|
|
pageSize = 1
|
|
}
|
|
|
|
// Calculate the correct SQL offset for this page
|
|
offset := (page - 1) * pageSize
|
|
c.Set("page", page)
|
|
c.Set("page_size", pageSize)
|
|
c.Set("page_offset", offset)
|
|
|
|
// Process request
|
|
c.Next()
|
|
}
|
|
}
|
|
|
|
func (env *Env) alive(c *gin.Context) {
|
|
c.String(http.StatusOK, "yes!")
|
|
}
|
|
|
|
func (env *Env) getArtistByID(c *gin.Context) {
|
|
artistID := c.Params.ByName("artistID")
|
|
spotifyAuthToken := c.GetString("spotifyAuthToken")
|
|
|
|
if artistID == "" || spotifyAuthToken == "Bearer " {
|
|
c.JSON(http.StatusBadRequest, gin.H{"Error": "Could not find required parameters and/or required authentication tokens"})
|
|
return
|
|
}
|
|
|
|
spotifyResponse, err := getSpotifyArtistData(artistID, spotifyAuthToken)
|
|
if err != nil {
|
|
// cast the error back into our custom type
|
|
// so we can get context on why things failed
|
|
statusCode := err.(*ResponseError).StatusCode
|
|
switch statusCode {
|
|
case 404:
|
|
c.JSON(http.StatusNotFound, gin.H{"Error": "This artist does not exist"})
|
|
return
|
|
case 400:
|
|
c.JSON(http.StatusBadRequest, gin.H{"Error": "Bad request"})
|
|
return
|
|
}
|
|
slog.Error("[GOMUSIC] Failed to request latest spotify data from API", "Error", err)
|
|
c.JSON(http.StatusInternalServerError, gin.H{"Error": "Failed to request the latest data from spotify API"})
|
|
return
|
|
}
|
|
|
|
// Update DB here
|
|
var genreList []Genre
|
|
for _, val := range spotifyResponse.Genres {
|
|
genreList = append(genreList, Genre{Name: val})
|
|
}
|
|
|
|
artistProfile := &ArtistProfile{
|
|
SpotifyID: spotifyResponse.ID,
|
|
Name: spotifyResponse.Name,
|
|
Popularity: spotifyResponse.Popularity,
|
|
Genres: genreList,
|
|
}
|
|
|
|
dbResult := env.db.Clauses(clause.OnConflict{
|
|
Columns: []clause.Column{{Name: "spotify_id"}},
|
|
UpdateAll: true,
|
|
}).Create(&artistProfile)
|
|
|
|
if dbResult.Error != nil {
|
|
slog.Error("[GOMUSIC] Failed to store response in local database", "Error", err)
|
|
}
|
|
|
|
c.JSON(http.StatusOK, artistProfile)
|
|
}
|
|
|
|
func (env *Env) getArtistByName(c *gin.Context) {
|
|
artistName, exists := c.GetQuery("name")
|
|
if !exists {
|
|
c.JSON(http.StatusBadRequest, gin.H{"Error": "name parameter was not supplied"})
|
|
return
|
|
}
|
|
pageSize := c.GetInt("page_size")
|
|
pageOffset := c.GetInt("page_offset")
|
|
|
|
// Lookup this name in the DB and return any ArtistProfile objects
|
|
// NOTE: This is case insensitive for ascii on sqlite
|
|
// NOTE: However unicode is treated case sensitive due to very difficult conversions for some languages/characters
|
|
// NOTE: In future better DBs with unicode support such as postgres should be used
|
|
var artistProfiles []ArtistProfile
|
|
searchString := fmt.Sprintf("%%%s%%", artistName)
|
|
dbResult := env.db.Where("name LIKE ?", searchString).Preload("Genres").Offset(pageOffset).Limit(pageSize).Find(&artistProfiles)
|
|
if dbResult.Error != nil {
|
|
slog.Error("[GOMUSIC] Failed to query local database for artist name", "Name", artistName)
|
|
c.JSON(http.StatusInternalServerError, gin.H{"Error": "Failed to lookup name"})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, artistProfiles)
|
|
}
|
|
|
|
func (env *Env) getGenres(c *gin.Context) {
|
|
var genres []Genre
|
|
pageSize := c.GetInt("page_size")
|
|
pageOffset := c.GetInt("page_offset")
|
|
|
|
dbResult := env.db.Offset(pageOffset).Limit(pageSize).Find(&genres)
|
|
if dbResult.Error != nil {
|
|
slog.Error("[GOMUSIC] Failed to query local database for genre data")
|
|
c.JSON(http.StatusInternalServerError, gin.H{"Error": "Failed to query database for genre data"})
|
|
return
|
|
}
|
|
recordNum := dbResult.RowsAffected
|
|
|
|
// Construct simple slice for response
|
|
genreList := make([]string, 0, recordNum)
|
|
if recordNum > 0 {
|
|
for _, g := range genres {
|
|
genreList = append(genreList, g.Name)
|
|
}
|
|
}
|
|
c.JSON(http.StatusOK, genreList)
|
|
}
|
|
|
|
func (env *Env) createGenre(c *gin.Context) {
|
|
var userGenre Genre
|
|
if c.ShouldBind(&userGenre) == nil {
|
|
// If we didn't get the correct data abort early
|
|
if userGenre.Name == "" {
|
|
c.JSON(http.StatusBadRequest, gin.H{"Error": "Could not find genre name"})
|
|
return
|
|
}
|
|
dbResult := env.db.Clauses(clause.OnConflict{
|
|
DoNothing: true,
|
|
}).Create(&userGenre)
|
|
if dbResult.Error != nil {
|
|
slog.Error("[GOMUSIC] Failed to create new genre record in DB")
|
|
c.JSON(http.StatusInternalServerError, gin.H{"Error": "Failed to create new genre record"})
|
|
return
|
|
}
|
|
c.String(http.StatusOK, "Success")
|
|
return
|
|
}
|
|
c.JSON(http.StatusInternalServerError, gin.H{"Error": "Failed to parse input data"})
|
|
}
|