Run go fmt

This commit is contained in:
froge 2025-02-12 14:31:19 +10:00
parent 47d45625bc
commit 72fef2e3b2
Signed by: froge
GPG key ID: A825E09930271BFA
7 changed files with 122 additions and 122 deletions

48
auth.go
View file

@ -1,27 +1,27 @@
package main
import (
"github.com/gin-gonic/gin"
"encoding/base64"
"encoding/json"
"net/http"
"log/slog"
"strings"
"time"
"fmt"
"io"
"encoding/base64"
"encoding/json"
"fmt"
"github.com/gin-gonic/gin"
"io"
"log/slog"
"net/http"
"strings"
"time"
)
// Used to unmarshal response data into type safe struct
type SpotifyToken struct {
AccessToken string `json:"access_token"`
TokenType string `json:"token_type"`
ExpiresIn int `json:"expires_in"`
TokenType string `json:"token_type"`
ExpiresIn int `json:"expires_in"`
}
// Makes a HTTP request to grab a short lived access token from Spotify
func getSpotifyToken(clientID string, clientSecret string) (SpotifyToken, error) {
// See spotify API docs for details on parameters/urls required here
// https://developer.spotify.com/documentation/web-api/tutorials/client-credentials-flow
spotifyAuthURL := "https://accounts.spotify.com/api/token"
@ -43,7 +43,7 @@ func getSpotifyToken(clientID string, clientSecret string) (SpotifyToken, error)
if err != nil {
slog.Error("[GOMUSIC] Failed to send authentication request to spotify", "Error", err)
return SpotifyToken{}, fmt.Errorf("Failed to authenticate with spotify: %w", err)
} else if resp.StatusCode != 200 {
slog.Error("[GOMUSIC] Failed to authenticate with spotify", "Error", resp.Status)
return SpotifyToken{}, fmt.Errorf("Failed to authenticate with spotify: %s", resp.Status)
@ -56,13 +56,13 @@ func getSpotifyToken(clientID string, clientSecret string) (SpotifyToken, error)
// Close this immediately since it's unused now
resp.Body.Close()
var spotifyToken SpotifyToken
var spotifyToken SpotifyToken
err = json.Unmarshal(respData, &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)
}
return spotifyToken, nil
}
@ -72,7 +72,7 @@ func spotifyAuth(clientID string, clientSecret string) gin.HandlerFunc {
// Short lived spotify access tokens during execution
spotifyToken, err := getSpotifyToken(clientID, clientSecret)
if err != nil {
slog.Error("[GOMUSIC] Failed to get spotify access token", "Error", err)
slog.Error("[GOMUSIC] Failed to get spotify access token", "Error", err)
}
// Calculate the future expiry time
@ -81,14 +81,14 @@ func spotifyAuth(clientID string, clientSecret string) gin.HandlerFunc {
return func(c *gin.Context) {
if time.Now().After(expireTime) {
// Token expired so we grab a new one
slog.Info("[GOMUSIC] Replacing expired Spotify access token")
spotifyToken, err = getSpotifyToken(clientID, clientSecret)
if err != nil {
slog.Error("[GOMUSIC] Failed to get new spotify access token", "Error", err)
}
expDuration := time.Duration(spotifyToken.ExpiresIn) * time.Second
expireTime = time.Now().Add(expDuration)
// Token expired so we grab a new one
slog.Info("[GOMUSIC] Replacing expired Spotify access token")
spotifyToken, err = getSpotifyToken(clientID, clientSecret)
if err != nil {
slog.Error("[GOMUSIC] Failed to get new spotify access token", "Error", err)
}
expDuration := time.Duration(spotifyToken.ExpiresIn) * time.Second
expireTime = time.Now().Add(expDuration)
}
// Pass the authorization header into context

View file

@ -1,8 +1,8 @@
package main
import (
"testing"
"github.com/stretchr/testify/assert"
"testing"
)
func TestgetSpotifyAuthURL(t *testing.T) {

View file

@ -1,26 +1,26 @@
package main
import (
"gorm.io/driver/sqlite"
"gorm.io/gorm"
"log/slog"
"fmt"
)
"gorm.io/driver/sqlite"
"gorm.io/gorm"
"log/slog"
)
type ArtistProfile struct {
gorm.Model
SpotifyID string `gorm:"unique"`
Name string
Popularity int
Genres []Genre `gorm:"many2many:artist_genres;"`
gorm.Model
SpotifyID string `gorm:"unique"`
Name string
Popularity int
Genres []Genre `gorm:"many2many:artist_genres;"`
}
type Genre struct {
gorm.Model
Name string `gorm:"unique"`
gorm.Model
Name string `gorm:"unique"`
}
func setupTestDatabase(name string) *gorm.DB {
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
dbName := fmt.Sprintf("file:%s?mode=memory&cache=shared", name)
@ -30,18 +30,18 @@ func setupTestDatabase(name string) *gorm.DB {
}
// Run model migrations here to keep DB consistent
db.AutoMigrate(&ArtistProfile{}, &Genre{})
db.AutoMigrate(&ArtistProfile{}, &Genre{})
return db
}
func setupDatabase() *gorm.DB {
func setupDatabase() *gorm.DB {
slog.Info("[GOMUSIC] Setting up database and running auto migrations")
db, err := gorm.Open(sqlite.Open("gomusic.db"), &gorm.Config{})
if err != nil {
panic("Failed to open database")
}
if err != nil {
panic("Failed to open database")
}
// Run model migrations here to keep DB consistent
db.AutoMigrate(&ArtistProfile{}, &Genre{})
return db
// Run model migrations here to keep DB consistent
db.AutoMigrate(&ArtistProfile{}, &Genre{})
return db
}

40
main.go
View file

@ -1,11 +1,11 @@
package main
import (
"github.com/gin-gonic/gin"
"gorm.io/gorm"
"log/slog"
"os"
)
"github.com/gin-gonic/gin"
"gorm.io/gorm"
"log/slog"
"os"
)
// Environment type is used with route methods
// So that we can easily inject/contain context data like DB connections
@ -18,27 +18,27 @@ var spotifyClientID = os.Getenv("SPOTIFY_ID")
var spotifyClientSecret = os.Getenv("SPOTIFY_TOKEN")
func setupRouter(env *Env, spotifyID string, spotifySecret string) *gin.Engine {
var r *gin.Engine = gin.Default()
var r *gin.Engine = gin.Default()
r.Use(spotifyAuth(spotifyID, spotifySecret))
r.GET("/alive", env.alive)
r.GET("/artists/:artistID", env.getArtistByID)
r.GET("/artists", env.getArtistByName)
return r
r.GET("/alive", env.alive)
r.GET("/artists/:artistID", env.getArtistByID)
r.GET("/artists", env.getArtistByName)
return r
}
func main() {
// If the auth/ID variables are empty something is probably misconfigured
if spotifyClientID == "" {
slog.Warn("[GOMUSIC] No Spotify ID configured in 'SPOTIFY_ID' environment variable")
}
if spotifyClientSecret == "" {
slog.Warn("[GOMUSIC] No Spotify secret configured in 'SPOTIFY_SECRET' environment variable")
}
// If the auth/ID variables are empty something is probably misconfigured
if spotifyClientID == "" {
slog.Warn("[GOMUSIC] No Spotify ID configured in 'SPOTIFY_ID' environment variable")
}
if spotifyClientSecret == "" {
slog.Warn("[GOMUSIC] No Spotify secret configured in 'SPOTIFY_SECRET' environment variable")
}
db := setupDatabase()
env := &Env{db: db}
// Router/middleware and server setup
r := setupRouter(env, spotifyClientID, spotifyClientSecret)
r.Run(":8000")
r := setupRouter(env, spotifyClientID, spotifyClientSecret)
r.Run(":8000")
}

View file

@ -1,60 +1,60 @@
package main
import (
"github.com/gin-gonic/gin"
"gorm.io/gorm/clause"
"net/http"
"log/slog"
import (
"fmt"
"github.com/gin-gonic/gin"
"gorm.io/gorm/clause"
"log/slog"
"net/http"
)
func (env *Env) alive(c *gin.Context) {
c.String(http.StatusOK, "yes!")
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 " {
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 {
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
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})
genreList = append(genreList, Genre{Name: val})
}
artistProfile := ArtistProfile{
SpotifyID: spotifyResponse.ID,
Name: spotifyResponse.Name,
SpotifyID: spotifyResponse.ID,
Name: spotifyResponse.Name,
Popularity: spotifyResponse.Popularity,
Genres: genreList,
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"}},
Columns: []clause.Column{{Name: "spotify_id"}},
UpdateAll: true,
}).Create(&artistProfile)
@ -62,8 +62,8 @@ 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)
// Send back our response data
c.JSON(http.StatusOK, spotifyResponse)
}
func (env *Env) getArtistByName(c *gin.Context) {

View file

@ -1,12 +1,12 @@
package main
import (
"net/http"
"encoding/json"
"net/http/httptest"
"github.com/stretchr/testify/assert"
"testing"
"fmt"
"github.com/stretchr/testify/assert"
"net/http"
"net/http/httptest"
"testing"
)
func TestAliveRoute(t *testing.T) {
@ -49,7 +49,7 @@ func TestGetArtistByIDRoute(t *testing.T) {
if dbResult.Error != nil {
assert.Fail(t, "Failed to retrieve new info from test database")
}
assert.Equal(t, "Pitbull", artist.Name)
assert.Equal(t, "0TnOYISbd1XYRBk9myaseg", artist.SpotifyID)
}

View file

@ -1,22 +1,22 @@
package main
import (
"fmt"
"errors"
"net/http"
"encoding/json"
"errors"
"fmt"
"net/http"
)
type SpotifyResponse struct {
ID string
Name string
Popularity int
Genres []string
ID string
Name string
Popularity int
Genres []string
}
type ResponseError struct {
StatusCode int
Err error
Err error
}
func (r ResponseError) Error() string {
@ -28,44 +28,44 @@ func (r ResponseError) Error() string {
func getSpotifyArtistData(artistID string, spotifyAuthToken string) (SpotifyResponse, error) {
artistEndpoint := fmt.Sprintf("https://api.spotify.com/v1/artists/%s", artistID)
req, err := http.NewRequest("GET", artistEndpoint, nil)
if err != nil {
req, err := http.NewRequest("GET", artistEndpoint, nil)
if err != nil {
respErr := &ResponseError{
StatusCode: 500,
Err: errors.New("Failed to build request"),
Err: errors.New("Failed to build request"),
}
return SpotifyResponse{}, respErr
}
req.Header.Add("Authorization", spotifyAuthToken)
}
req.Header.Add("Authorization", spotifyAuthToken)
// Send off request
resp, err := http.DefaultClient.Do(req)
if err != nil {
// Send off request
resp, err := http.DefaultClient.Do(req)
if err != nil {
respErr := &ResponseError{
StatusCode: 500,
Err: errors.New("Failed to send request to spotify"),
Err: errors.New("Failed to send request to spotify"),
}
return SpotifyResponse{}, respErr
} else if resp.StatusCode != 200 {
respErr := &ResponseError{
StatusCode: resp.StatusCode,
Err: errors.New("Failed to get artist data from spotify API"),
Err: errors.New("Failed to get artist data from spotify API"),
}
return SpotifyResponse{}, respErr
}
}
var spotifyResponse SpotifyResponse
err = json.NewDecoder(resp.Body).Decode(&spotifyResponse)
if err != nil {
if err != nil {
respErr := &ResponseError{
StatusCode: 500,
Err: errors.New("Failed to decode response body"),
Err: errors.New("Failed to decode response body"),
}
return SpotifyResponse{}, respErr
}
// Close this immediately since it's unused now
resp.Body.Close()
return SpotifyResponse{}, respErr
}
// Close this immediately since it's unused now
resp.Body.Close()
// After all the above checks we assume this response is populated
// If errors arise we can do extra validation here