Cleanup API responses and add better comments

This commit is contained in:
froge 2025-02-14 05:54:45 +10:00
parent 05cb03ac52
commit d56b21a3d1
Signed by: froge
GPG key ID: A825E09930271BFA
3 changed files with 51 additions and 22 deletions

14
auth.go
View file

@ -5,7 +5,6 @@ import (
"encoding/json"
"fmt"
"github.com/gin-gonic/gin"
"io"
"log/slog"
"net/http"
"strings"
@ -48,21 +47,14 @@ func getSpotifyToken(clientID string, clientSecret string) (SpotifyToken, error)
slog.Error("[GOMUSIC] Failed to authenticate with spotify", "Error", resp.Status)
return SpotifyToken{}, fmt.Errorf("Failed to authenticate with spotify: %s", resp.Status)
}
respData, err := io.ReadAll(resp.Body)
if err != nil {
slog.Error("[GOMUSIC] Failed to read response body data from spotify", "Error", err)
return SpotifyToken{}, fmt.Errorf("Failed to authenticate with spotify")
}
// Close this immediately since it's unused now
resp.Body.Close()
var spotifyToken SpotifyToken
err = json.Unmarshal(respData, &spotifyToken)
err = json.NewDecoder(resp.Body).Decode(&spotifyToken)
if err != nil {
slog.Error("[GOMUSIC] Failed to parse spotify authentication response", "Error", err)
return SpotifyToken{}, fmt.Errorf("Failed to parse spotify JSON response: %w", err)
}
resp.Body.Close()
return spotifyToken, nil
}
@ -93,8 +85,6 @@ func spotifyAuth(clientID string, clientSecret string) gin.HandlerFunc {
// Pass the authorization header into context
c.Set("spotifyAuthToken", fmt.Sprintf("Bearer %s", spotifyToken.AccessToken))
// Process the request
c.Next()
}
}

View file

@ -3,8 +3,10 @@ package main
import (
"fmt"
"gorm.io/driver/sqlite"
"encoding/json"
"gorm.io/gorm"
"log/slog"
"time"
)
type ArtistProfile struct {
@ -22,14 +24,13 @@ type Genre struct {
func setupTestDatabase(name string) *gorm.DB {
slog.Info("[GOMUSIC] Setting up new test database in memory")
// Open a named DB instance so each test has a clean environment
// Open a named DB instance in memory so each test has a clean environment
dbName := fmt.Sprintf("file:%s?mode=memory&cache=shared", name)
db, err := gorm.Open(sqlite.Open(dbName), &gorm.Config{})
if err != nil {
panic("Failed to open database")
}
// Run model migrations here to keep DB consistent
db.AutoMigrate(&ArtistProfile{}, &Genre{})
return db
}
@ -41,7 +42,45 @@ func setupDatabase() *gorm.DB {
panic("Failed to open database")
}
// Run model migrations here to keep DB consistent
db.AutoMigrate(&ArtistProfile{}, &Genre{})
return db
}
// Some functions to control JSON marshalling of DB structs
func (p *ArtistProfile) MarshalJSON() ([]byte, error) {
type CleanedProfile struct {
UpdatedAt time.Time
SpotifyID string
Name string
Popularity int
Genres []string
}
artistGenres := make([]string, 0, len(p.Genres))
for _, val := range p.Genres {
artistGenres = append(artistGenres, val.Name)
}
t := CleanedProfile{
UpdatedAt: p.UpdatedAt,
SpotifyID: p.SpotifyID,
Name: p.Name,
Popularity: p.Popularity,
Genres: artistGenres,
}
return json.Marshal(&t)
}
func (g *Genre) MarshalJSON() ([]byte, error) {
type CleanedGenre struct {
Name string
CreatedAt time.Time
}
t := CleanedGenre{
Name: g.Name,
CreatedAt: g.CreatedAt,
}
return json.Marshal(&t)
}

View file

@ -23,6 +23,8 @@ func (env *Env) getArtistByID(c *gin.Context) {
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:
@ -43,16 +45,13 @@ func (env *Env) getArtistByID(c *gin.Context) {
genreList = append(genreList, Genre{Name: val})
}
artistProfile := ArtistProfile{
artistProfile := &ArtistProfile{
SpotifyID: spotifyResponse.ID,
Name: spotifyResponse.Name,
Popularity: spotifyResponse.Popularity,
Genres: genreList,
}
// Create new record
// Otherwise update values when artist with this SpotifyID already exists
// Basically upsert
dbResult := env.db.Clauses(clause.OnConflict{
Columns: []clause.Column{{Name: "spotify_id"}},
UpdateAll: true,
@ -62,8 +61,7 @@ func (env *Env) getArtistByID(c *gin.Context) {
slog.Error("[GOMUSIC] Failed to store response in local database", "Error", err)
}
// Send back our response data
c.JSON(http.StatusOK, spotifyResponse)
c.JSON(http.StatusOK, artistProfile)
}
func (env *Env) getArtistByName(c *gin.Context) {
@ -74,12 +72,14 @@ func (env *Env) getArtistByName(c *gin.Context) {
}
// Lookup this name in the DB and return any ArtistProfile objects
// This is case insensitive on sqlite
var artistProfiles []ArtistProfile
dbResult := env.db.Where("name LIKE ?", fmt.Sprintf("%%%s%%", artistName)).Find(&artistProfiles)
dbResult := env.db.Where("name LIKE ?", fmt.Sprintf("%%%s%%", artistName)).Preload("Genres").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)
}