Skip to content

Commit

Permalink
manager implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
AbdoAnss committed Dec 11, 2024
1 parent a7b7419 commit 93fcb35
Show file tree
Hide file tree
Showing 10 changed files with 451 additions and 17 deletions.
2 changes: 2 additions & 0 deletions client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ type Client struct {
Players *endpoints.PlayerService
Fixtures *endpoints.FixtureService
Teams *endpoints.TeamService
Managers *endpoints.ManagerService
}

func NewClient(opts ...Option) *Client {
Expand Down Expand Up @@ -54,6 +55,7 @@ func NewClient(opts ...Option) *Client {
// services dependant on bootstrap:
c.Players = endpoints.NewPlayerService(c, c.Bootstrap)
c.Teams = endpoints.NewTeamService(c, c.Bootstrap)
c.Managers = endpoints.NewManagerService(c, c.Bootstrap)
// standalone services
c.Fixtures = endpoints.NewFixtureService(c)

Expand Down
27 changes: 27 additions & 0 deletions endpoints/bootstrap.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,13 @@ var (
fixturesCacheTTL = 10 * time.Minute
gameweeksCacheTTL = 3 * time.Minute // Gameweeks status might change more often
settingsCacheTTL = 24 * time.Hour // Game settings rarely change
managerCacheTTL = 5 * time.Minute // Managers data updates frequently
)

func init() {
sharedCache.StartCleanupTask(5 * time.Minute)
}

type Response struct {
Teams []models.Team `json:"teams"`
Elements []models.Player `json:"elements"`
Expand Down Expand Up @@ -91,6 +96,28 @@ func (bs *BootstrapService) GetGameWeeks() ([]models.GameWeek, error) {
return data.Events, nil
}

func (bs *BootstrapService) GetCurrentGameWeek() (int, error) {
const cacheKey = "current_gameweek"
if cached, found := sharedCache.Get(cacheKey); found {
if gw, ok := cached.(int); ok {
return gw, nil
}
}
gameweeks, err := bs.GetGameWeeks()
if err != nil {
return 0, fmt.Errorf("failed to get gameweeks: %w", err)
}

for _, gw := range gameweeks {
if gw.IsCurrent {
sharedCache.Set(cacheKey, gw.ID, gameweeksCacheTTL)
return gw.ID, nil
}
}

return 0, fmt.Errorf("failed to find current gameweek")
}

func (bs *BootstrapService) GetSettings() (*models.GameSettings, error) {
const cacheKey = "settings"
if cached, found := sharedCache.Get(cacheKey); found {
Expand Down
5 changes: 0 additions & 5 deletions endpoints/fixtures.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package endpoints
import (
"encoding/json"
"fmt"
"time"

"github.com/AbdoAnss/go-fantasy-pl/api"
"github.com/AbdoAnss/go-fantasy-pl/models"
Expand Down Expand Up @@ -31,10 +30,6 @@ func NewFixtureService(client api.Client) *FixtureService {
}
}

func init() {
sharedCache.StartCleanupTask(5 * time.Minute)
}

func (fs *FixtureService) GetAllFixtures() ([]models.Fixture, error) {
if cached, found := sharedCache.Get("fixtures"); found {
if fixtures, ok := cached.([]models.Fixture); ok {
Expand Down
153 changes: 153 additions & 0 deletions endpoints/manager.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
// Package endpoints provides access to the Fantasy Premier League API
package endpoints

import (
"encoding/json"
"fmt"
"io"
"net/http"

"github.com/AbdoAnss/go-fantasy-pl/api"
"github.com/AbdoAnss/go-fantasy-pl/models"
)

const (
managerDetailsEndpoint = "/entry/%d/"
managerHistoryEndpoint = "/entry/%d/history"
managerGameWeekPicksEndpoint = "/entry/%d/event/%d/picks/"
)

type ManagerService struct {
client api.Client
bootstrapService *BootstrapService
}

func NewManagerService(client api.Client, bootstrap *BootstrapService) *ManagerService {
return &ManagerService{
client: client,
bootstrapService: bootstrap,
}
}

func (ms *ManagerService) validateManager(manager *models.Manager) error {
if manager == nil {
return fmt.Errorf("received nil manager data")
}
if manager.ID == nil {
return fmt.Errorf("manager ID is missing")
}
return nil
}

func (ms *ManagerService) GetManager(id int) (*models.Manager, error) {
cacheKey := fmt.Sprintf("manager_%d", id)
if cached, found := sharedCache.Get(cacheKey); found {
if manager, ok := cached.(*models.Manager); ok {
return manager, nil
}
}

endpoint := fmt.Sprintf(managerDetailsEndpoint, id)
resp, err := ms.client.Get(endpoint)
if err != nil {
return nil, fmt.Errorf("failed to get manager data: %w", err)
}
defer resp.Body.Close()

switch resp.StatusCode {
case http.StatusOK:
case http.StatusNotFound:
return nil, fmt.Errorf("manager with ID %d not found", id)
default:
return nil, fmt.Errorf("unexpected status code: %d", resp.StatusCode)
}

body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("failed to read response body: %w", err)
}

var manager models.Manager
if err := json.Unmarshal(body, &manager); err != nil {
return nil, fmt.Errorf("failed to decode manager data: %w", err)
}

if err := ms.validateManager(&manager); err != nil {
return nil, err
}

sharedCache.Set(cacheKey, &manager, managerCacheTTL)

return &manager, nil
}

func (ms *ManagerService) GetCurrentTeam(managerID int) (*models.ManagerTeam, error) {
cacheKey := fmt.Sprintf("manager_team_%d", managerID)
if cached, found := sharedCache.Get(cacheKey); found {
if team, ok := cached.(*models.ManagerTeam); ok {
return team, nil
}
}

currentGameWeekID, err := ms.bootstrapService.GetCurrentGameWeek()
if err != nil {
return nil, fmt.Errorf("failed to get current game week: %w", err)
}

endpoint := fmt.Sprintf(managerGameWeekPicksEndpoint, managerID, currentGameWeekID)
resp, err := ms.client.Get(endpoint)
if err != nil {
return nil, fmt.Errorf("failed to get manager team: %w", err)
}
defer resp.Body.Close()

if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("failed to get manager team: status %d", resp.StatusCode)
}

var team models.ManagerTeam
if err := json.NewDecoder(resp.Body).Decode(&team); err != nil {
return nil, fmt.Errorf("failed to decode manager team: %w", err)
}

sharedCache.Set(cacheKey, &team, managerCacheTTL)
return &team, nil
}

func (ms *ManagerService) GetManagerHistory(id int) (*models.ManagerHistory, error) {
cacheKey := fmt.Sprintf("manager_history_%d", id)
if cached, found := sharedCache.Get(cacheKey); found {
if ManagerHistory, ok := cached.(*models.ManagerHistory); ok {
return ManagerHistory, nil
}
}

endpoint := fmt.Sprintf(managerHistoryEndpoint, id)
resp, err := ms.client.Get(endpoint)
if err != nil {
return nil, fmt.Errorf("failed to get manager history data: %w", err)
}
defer resp.Body.Close()

switch resp.StatusCode {
case http.StatusOK:
case http.StatusNotFound:
return nil, fmt.Errorf("manager with ID %d not found", id)
default:
return nil, fmt.Errorf("unexpected status code: %d", resp.StatusCode)
}

body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("failed to read response body: %w", err)
}

var managerHistory models.ManagerHistory
if err := json.Unmarshal(body, &managerHistory); err != nil {
return nil, fmt.Errorf("failed to decode manager data: %w", err)
}

sharedCache.Set(cacheKey, &managerHistory, managerCacheTTL)

return &managerHistory, nil
}
121 changes: 121 additions & 0 deletions endpoints/manager_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
package endpoints_test

import (
"testing"

"github.com/AbdoAnss/go-fantasy-pl/client"
"github.com/stretchr/testify/assert"
)

var testManagerClient *client.Client

func init() {
testManagerClient = client.NewClient()
}

func TestManagerEndpoints(t *testing.T) {
t.Run("GetManager", func(t *testing.T) {
// Using a known manager ID (you can replace with a valid one)
managerID := 1387812

manager, err := testManagerClient.Managers.GetManager(managerID)
assert.NoError(t, err, "expected no error when getting manager")
assert.NotNil(t, manager, "expected manager to be returned")

// Log manager details
t.Logf("Manager Details:")
t.Logf("ID: %d", *manager.ID)
t.Logf("Name: %s", manager.GetFullName())
t.Logf("Team Name: %s", manager.Name)
t.Logf("Overall Points: %d", manager.SummaryOverallPoints)
t.Logf("Overall Rank: %d", manager.SummaryOverallRank)
})

t.Run("GetNonExistentManager", func(t *testing.T) {
manager, err := testManagerClient.Managers.GetManager(99999999)
assert.Error(t, err, "expected error when getting non-existent manager")
assert.Nil(t, manager, "expected nil manager for non-existent ID")
assert.Contains(t, err.Error(), "not found", "expected 'not found' error message")
})

t.Run("GetCurrentTeam", func(t *testing.T) {
// Using same manager ID
managerID := 1387812

team, err := testManagerClient.Managers.GetCurrentTeam(managerID)
assert.NoError(t, err, "expected no error when getting current team")
assert.NotNil(t, team, "expected team to be returned")

// Log team details
t.Logf("Current Team Details:")
t.Logf("Number of Picks: %d", len(team.Picks))

// Log starting XI
t.Log("Starting XI:")
for _, pick := range team.GetStartingXI() {
t.Logf("Position %d: Player ID %d (Captain: %v)",
pick.Position,
pick.Element,
pick.IsCaptain)
}

// Log bench
t.Log("Bench:")
for _, pick := range team.GetBench() {
t.Logf("Position %d: Player ID %d",
pick.Position,
pick.Element)
}

t.Logf("Team Value: £%.1fm", team.GetTeamValueInMillions())
t.Logf("Bank: £%.1fm", team.GetBankValueInMillions())
})

t.Run("GetManagerHistory", func(t *testing.T) {
// Using same manager ID
managerID := 1387812

history, err := testManagerClient.Managers.GetManagerHistory(managerID)
assert.NoError(t, err, "expected no error when getting manager history")
assert.NotNil(t, history, "expected history to be returned")

// Log history details
t.Logf("Manager History Details:")

if len(history.Current) > 0 {
t.Log("Current Season Performance:")
for _, gw := range history.Current[:3] { // Show first 3 gameweeks
t.Logf("GW%d: Points: %d, Overall Rank: %d",
gw.Event,
gw.Points,
gw.OverallRank)
}
}

if len(history.Past) > 0 {
t.Log("Past Seasons:")
for _, season := range history.Past {
t.Logf("Season %s: Points: %d, Overall Rank: %d",
season.SeasonName,
season.TotalPoints,
season.Rank)
}
}
})

t.Run("CacheConsistency", func(t *testing.T) {
managerID := 1387812

// First call
manager1, err := testManagerClient.Managers.GetManager(managerID)
assert.NoError(t, err)

// Second call (should be from cache)
manager2, err := testManagerClient.Managers.GetManager(managerID)
assert.NoError(t, err)

// Compare results
assert.Equal(t, manager1.ID, manager2.ID, "cached manager should match original")
assert.Equal(t, manager1.Name, manager2.Name, "cached manager name should match original")
})
}
8 changes: 0 additions & 8 deletions endpoints/players.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (
"fmt"
"io"
"net/http"
"time"

"github.com/AbdoAnss/go-fantasy-pl/api"
"github.com/AbdoAnss/go-fantasy-pl/models"
Expand All @@ -27,16 +26,10 @@ func NewPlayerService(client api.Client, bootstrap *BootstrapService) *PlayerSer
}
}

func init() {
sharedCache.StartCleanupTask(5 * time.Minute)
}

// GetAllPlayers now uses the bootstrap service to fetch players
func (ps *PlayerService) GetAllPlayers() ([]models.Player, error) {
return ps.bootstrapService.GetPlayers()
}

// GetPlayer finds a specific player by ID
func (ps *PlayerService) GetPlayer(id int) (*models.Player, error) {
players, err := ps.GetAllPlayers()
if err != nil {
Expand All @@ -51,7 +44,6 @@ func (ps *PlayerService) GetPlayer(id int) (*models.Player, error) {
return nil, fmt.Errorf("player with ID %d not found", id)
}

// GetPlayerHistory fetches detailed history for a specific player
func (ps *PlayerService) GetPlayerHistory(id int) (*models.PlayerHistory, error) {
cacheKey := fmt.Sprintf("player_history_%d", id)
if cached, found := sharedCache.Get(cacheKey); found {
Expand Down
Loading

0 comments on commit 93fcb35

Please sign in to comment.