Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 60 additions & 2 deletions internal/service/player.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ func NewPlayerService(codClient codclient.CodClient, playerRepo *repository.Play
}

// SearchPlayer verifies a player exists via the CoD API, persists them, and returns search results.
// Falls back to database if the API is unavailable.
func (s *PlayerService) SearchPlayer(ctx context.Context, platform, gamertag, title, mode string) (*PlayerSearchResult, error) {
if title == "" {
title = "mw"
Expand All @@ -39,7 +40,8 @@ func (s *PlayerService) SearchPlayer(ctx context.Context, platform, gamertag, ti

stats, err := s.codClient.GetPlayerStats(ctx, platform, gamertag, title, mode)
if err != nil {
return nil, err
slog.Warn("cod api unavailable, falling back to database", "error", err)
return s.searchFromDB(ctx, platform, gamertag, mode)
}

player, err := s.playerRepo.Upsert(ctx, platform, gamertag)
Expand All @@ -64,6 +66,7 @@ func (s *PlayerService) SearchPlayer(ctx context.Context, platform, gamertag, ti
}

// GetPlayerStats fetches player stats from the CoD API, upserts the player, and saves a snapshot.
// Falls back to database if the API is unavailable.
func (s *PlayerService) GetPlayerStats(ctx context.Context, platform, gamertag, title, mode string) (*codclient.PlayerStats, error) {
if title == "" {
title = "mw"
Expand All @@ -74,7 +77,8 @@ func (s *PlayerService) GetPlayerStats(ctx context.Context, platform, gamertag,

stats, err := s.codClient.GetPlayerStats(ctx, platform, gamertag, title, mode)
if err != nil {
return nil, err
slog.Warn("cod api unavailable for stats, falling back to database", "error", err)
return s.getStatsFromDB(ctx, platform, gamertag, mode)
}

player, err := s.playerRepo.Upsert(ctx, platform, gamertag)
Expand All @@ -92,3 +96,57 @@ func (s *PlayerService) GetPlayerStats(ctx context.Context, platform, gamertag,

return stats, nil
}

// searchFromDB looks up a player and their latest stats from the database.
func (s *PlayerService) searchFromDB(ctx context.Context, platform, gamertag, mode string) (*PlayerSearchResult, error) {
player, err := s.playerRepo.GetByPlatformAndTag(ctx, platform, gamertag)
if err != nil {
return nil, err
}
if player == nil {
return nil, codclient.ErrPlayerNotFound
}

stats, err := s.getStatsFromDB(ctx, platform, gamertag, mode)
if err != nil {
return nil, err
}

return &PlayerSearchResult{
PlayerID: player.ID,
Platform: platform,
Gamertag: gamertag,
Stats: stats,
}, nil
}

// getStatsFromDB loads the latest stats snapshot for a player from the database.
func (s *PlayerService) getStatsFromDB(ctx context.Context, platform, gamertag, mode string) (*codclient.PlayerStats, error) {
player, err := s.playerRepo.GetByPlatformAndTag(ctx, platform, gamertag)
if err != nil {
return nil, err
}
if player == nil {
return nil, codclient.ErrPlayerNotFound
}

statsData, _, err := s.playerRepo.GetLatestStats(ctx, player.ID, mode)
if err != nil {
return nil, err
}
if statsData == nil {
return nil, codclient.ErrPlayerNotFound
}

// statsData is any (from pgx JSONB scan) — round-trip through JSON to decode
jsonBytes, err := json.Marshal(statsData)
if err != nil {
return nil, err
}
var stats codclient.PlayerStats
if err := json.Unmarshal(jsonBytes, &stats); err != nil {
return nil, err
}

return &stats, nil
}
Loading