2120 lines
66 KiB
Go
2120 lines
66 KiB
Go
package enetpulse
|
||
|
||
import (
|
||
"context"
|
||
"encoding/json"
|
||
"fmt"
|
||
"io"
|
||
"net/http"
|
||
"strconv"
|
||
"strings"
|
||
"time"
|
||
|
||
"github.com/SamuelTariku/FortuneBet-Backend/internal/config"
|
||
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
|
||
"github.com/SamuelTariku/FortuneBet-Backend/internal/repository"
|
||
)
|
||
|
||
type Service struct {
|
||
// username string
|
||
// token string
|
||
cfg config.Config
|
||
store *repository.Store
|
||
// mongoLogger *zap.Logger
|
||
httpClient *http.Client
|
||
}
|
||
|
||
func New(cfg config.Config, store *repository.Store) *Service {
|
||
return &Service{
|
||
cfg: cfg,
|
||
store: store,
|
||
// mongoLogger: mongoLogger,
|
||
httpClient: &http.Client{
|
||
Timeout: 15 * time.Second,
|
||
},
|
||
}
|
||
}
|
||
|
||
func (s *Service) FetchAndStoreSports(ctx context.Context) error {
|
||
// 1️⃣ Compose URL with credentials
|
||
url := fmt.Sprintf(
|
||
"http://eapi.enetpulse.com/sport/list/?username=%s&token=%s",
|
||
s.cfg.EnetPulseConfig.UserName, s.cfg.EnetPulseConfig.Token,
|
||
)
|
||
|
||
// 2️⃣ Create HTTP request with context
|
||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
||
if err != nil {
|
||
return fmt.Errorf("creating sport request: %w", err)
|
||
}
|
||
|
||
// 3️⃣ Execute request
|
||
resp, err := s.httpClient.Do(req)
|
||
if err != nil {
|
||
return fmt.Errorf("requesting sports: %w", err)
|
||
}
|
||
defer resp.Body.Close()
|
||
|
||
// 4️⃣ Check response status
|
||
if resp.StatusCode != http.StatusOK {
|
||
body, _ := io.ReadAll(resp.Body)
|
||
return fmt.Errorf("failed to fetch sports (status %d): %s", resp.StatusCode, string(body))
|
||
}
|
||
|
||
// 5️⃣ Decode JSON response
|
||
var sportsResp struct {
|
||
Sports map[string]struct {
|
||
ID string `json:"id"`
|
||
N string `json:"n"` // updates count
|
||
Name string `json:"name"`
|
||
UT string `json:"ut"` // timestamp string
|
||
} `json:"sports"`
|
||
}
|
||
if err := json.NewDecoder(resp.Body).Decode(&sportsResp); err != nil {
|
||
return fmt.Errorf("decoding sports response: %w", err)
|
||
}
|
||
|
||
// 6️⃣ Iterate and store each sport
|
||
for _, sport := range sportsResp.Sports {
|
||
// Parse updates count
|
||
updatesCount := 0
|
||
if sport.N != "" {
|
||
if n, err := strconv.Atoi(sport.N); err == nil {
|
||
updatesCount = n
|
||
}
|
||
}
|
||
|
||
// Parse timestamp
|
||
lastUpdatedAt, err := time.Parse(time.RFC3339, sport.UT)
|
||
if err != nil {
|
||
// Fallback to zero time if parsing fails
|
||
lastUpdatedAt = time.Time{}
|
||
}
|
||
|
||
// Build domain object
|
||
createSport := domain.CreateEnetpulseSport{
|
||
SportID: sport.ID,
|
||
Name: sport.Name,
|
||
UpdatesCount: updatesCount,
|
||
LastUpdatedAt: lastUpdatedAt,
|
||
Status: 1, // default active
|
||
}
|
||
|
||
// Insert or update in DB
|
||
if _, err := s.store.CreateEnetpulseSport(ctx, createSport); err != nil {
|
||
// Log error but continue
|
||
// s.logger.Error("failed to store sport", zap.String("sport_id", sport.ID), zap.Error(err))
|
||
continue
|
||
}
|
||
}
|
||
|
||
// s.logger.Info("Successfully fetched and stored sports", zap.Int("count", len(sportsResp.Sports)))
|
||
return nil
|
||
}
|
||
|
||
func (s *Service) GetAllSports(ctx context.Context) ([]domain.EnetpulseSport, error) {
|
||
// 1️⃣ Fetch all sports from the store (database)
|
||
sports, err := s.store.GetAllEnetpulseSports(ctx)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("failed to fetch sports from DB: %w", err)
|
||
}
|
||
|
||
// 2️⃣ Optionally, you can log the count or perform other transformations
|
||
// s.logger.Info("Fetched sports from DB", zap.Int("count", len(sports)))
|
||
|
||
return sports, nil
|
||
}
|
||
|
||
func (s *Service) FetchAndStoreTournamentTemplates(ctx context.Context) error {
|
||
// 1️⃣ Fetch all sports from the database
|
||
sports, err := s.store.GetAllEnetpulseSports(ctx)
|
||
if err != nil {
|
||
return fmt.Errorf("failed to fetch sports from DB: %w", err)
|
||
}
|
||
|
||
// Template struct
|
||
type TournamentTemplate struct {
|
||
ID string `json:"id"`
|
||
Name string `json:"name"`
|
||
SportFK string `json:"sportFK"`
|
||
Gender string `json:"gender"`
|
||
N string `json:"n"`
|
||
UT string `json:"ut"`
|
||
}
|
||
|
||
for _, sport := range sports {
|
||
|
||
if sport.SportID != "1" {
|
||
continue
|
||
} else {
|
||
// 2️⃣ Compose URL for each sport using its Enetpulse sportFK
|
||
url := fmt.Sprintf(
|
||
"http://eapi.enetpulse.com/tournament_template/list/?sportFK=%s&username=%s&token=%s",
|
||
sport.SportID, // must be Enetpulse sportFK
|
||
s.cfg.EnetPulseConfig.UserName,
|
||
s.cfg.EnetPulseConfig.Token,
|
||
)
|
||
|
||
fmt.Println("Fetching tournament templates:", url)
|
||
|
||
// 3️⃣ Create HTTP request
|
||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
||
if err != nil {
|
||
return fmt.Errorf("creating tournament template request for sport %s: %w", sport.SportID, err)
|
||
}
|
||
|
||
// 4️⃣ Execute request
|
||
resp, err := s.httpClient.Do(req)
|
||
if err != nil {
|
||
return fmt.Errorf("requesting tournament templates for sport %s: %w", sport.SportID, err)
|
||
}
|
||
defer resp.Body.Close()
|
||
|
||
if resp.StatusCode != http.StatusOK {
|
||
body, _ := io.ReadAll(resp.Body)
|
||
return fmt.Errorf("failed to fetch tournament templates for sport %s (status %d): %s",
|
||
sport.SportID, resp.StatusCode, string(body))
|
||
}
|
||
|
||
// 5️⃣ Decode JSON response flexibly
|
||
var raw struct {
|
||
TournamentTemplates json.RawMessage `json:"tournament_templates"`
|
||
}
|
||
bodyBytes, err := io.ReadAll(resp.Body)
|
||
if err != nil {
|
||
return fmt.Errorf("reading tournament templates response for sport %s: %w", sport.SportID, err)
|
||
}
|
||
if err := json.Unmarshal(bodyBytes, &raw); err != nil {
|
||
return fmt.Errorf("unmarshalling raw tournament templates for sport %s: %w", sport.SportID, err)
|
||
}
|
||
|
||
// 6️⃣ Parse depending on object or array
|
||
templates := map[string]TournamentTemplate{}
|
||
if len(raw.TournamentTemplates) > 0 && raw.TournamentTemplates[0] == '{' {
|
||
// Object (normal case)
|
||
if err := json.Unmarshal(raw.TournamentTemplates, &templates); err != nil {
|
||
return fmt.Errorf("decoding tournament templates (object) for sport %s: %w", sport.SportID, err)
|
||
}
|
||
} else {
|
||
// Array or empty → skip safely
|
||
fmt.Printf("No tournament templates found for sport %s\n", sport.SportID)
|
||
continue
|
||
}
|
||
|
||
// 7️⃣ Iterate and store each tournament template
|
||
for _, tmpl := range templates {
|
||
updatesCount := 0
|
||
if tmpl.N != "" {
|
||
if n, err := strconv.Atoi(tmpl.N); err == nil {
|
||
updatesCount = n
|
||
}
|
||
}
|
||
|
||
lastUpdatedAt, err := time.Parse(time.RFC3339, tmpl.UT)
|
||
if err != nil {
|
||
lastUpdatedAt = time.Time{}
|
||
}
|
||
|
||
// Convert sport.SportID from string to int64
|
||
sportFK, err := strconv.ParseInt(sport.SportID, 10, 64)
|
||
if err != nil {
|
||
fmt.Printf("failed to convert sport.SportID '%s' to int64: %v\n", sport.SportID, err)
|
||
continue
|
||
}
|
||
|
||
createTemplate := domain.CreateEnetpulseTournamentTemplate{
|
||
TemplateID: tmpl.ID,
|
||
Name: tmpl.Name,
|
||
SportFK: sportFK, // use DB sport ID internally
|
||
Gender: tmpl.Gender,
|
||
UpdatesCount: updatesCount,
|
||
LastUpdatedAt: lastUpdatedAt,
|
||
Status: 1, // default active
|
||
}
|
||
|
||
if _, err := s.store.CreateEnetpulseTournamentTemplate(ctx, createTemplate); err != nil {
|
||
fmt.Printf("failed to store tournament template %s: %v\n", tmpl.ID, err)
|
||
continue
|
||
}
|
||
}
|
||
break
|
||
}
|
||
}
|
||
|
||
// fmt.Println("✅ Successfully fetched and stored all tournament templates")
|
||
return nil
|
||
}
|
||
|
||
func (s *Service) GetAllTournamentTemplates(ctx context.Context) ([]domain.EnetpulseTournamentTemplate, error) {
|
||
// 1️⃣ Fetch all tournament templates from the store (database)
|
||
templates, err := s.store.GetAllEnetpulseTournamentTemplates(ctx)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("failed to fetch tournament templates from DB: %w", err)
|
||
}
|
||
|
||
// 2️⃣ Optionally, you can log the count or perform other transformations
|
||
// s.logger.Info("Fetched tournament templates from DB", zap.Int("count", len(templates)))
|
||
|
||
return templates, nil
|
||
}
|
||
|
||
func (s *Service) FetchAndStoreTournaments(ctx context.Context) error {
|
||
// 1️⃣ Fetch all tournament templates from the database
|
||
templates, err := s.store.GetAllEnetpulseTournamentTemplates(ctx)
|
||
if err != nil {
|
||
return fmt.Errorf("failed to fetch tournament templates from DB: %w", err)
|
||
}
|
||
|
||
for _, tmpl := range templates {
|
||
// 2️⃣ Compose URL for each tournament template
|
||
url := fmt.Sprintf(
|
||
"http://eapi.enetpulse.com/tournament/list/?tournament_templateFK=%s&username=%s&token=%s",
|
||
tmpl.TemplateID,
|
||
s.cfg.EnetPulseConfig.UserName,
|
||
s.cfg.EnetPulseConfig.Token,
|
||
)
|
||
|
||
// 3️⃣ Create HTTP request
|
||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
||
if err != nil {
|
||
return fmt.Errorf("creating tournament request for template %s: %w", tmpl.TemplateID, err)
|
||
}
|
||
|
||
// 4️⃣ Execute request
|
||
resp, err := s.httpClient.Do(req)
|
||
if err != nil {
|
||
return fmt.Errorf("requesting tournaments for template %s: %w", tmpl.TemplateID, err)
|
||
}
|
||
defer resp.Body.Close()
|
||
|
||
if resp.StatusCode != http.StatusOK {
|
||
body, _ := io.ReadAll(resp.Body)
|
||
return fmt.Errorf("failed to fetch tournaments for template %s (status %d): %s",
|
||
tmpl.TemplateID, resp.StatusCode, string(body))
|
||
}
|
||
|
||
// 5️⃣ Decode JSON response
|
||
var tournamentsResp struct {
|
||
Tournaments map[string]struct {
|
||
ID string `json:"id"`
|
||
Name string `json:"name"`
|
||
TournamentTemplateFK string `json:"tournament_templateFK"`
|
||
N string `json:"n"` // updates count
|
||
UT string `json:"ut"` // timestamp
|
||
} `json:"tournaments"`
|
||
}
|
||
if err := json.NewDecoder(resp.Body).Decode(&tournamentsResp); err != nil {
|
||
return fmt.Errorf("decoding tournaments for template %s: %w", tmpl.TemplateID, err)
|
||
}
|
||
|
||
// 6️⃣ Iterate and store each tournament
|
||
for _, t := range tournamentsResp.Tournaments {
|
||
updatesCount := 0
|
||
if t.N != "" {
|
||
if n, err := strconv.Atoi(t.N); err == nil {
|
||
updatesCount = n
|
||
}
|
||
}
|
||
|
||
lastUpdatedAt, err := time.Parse(time.RFC3339, t.UT)
|
||
if err != nil {
|
||
lastUpdatedAt = time.Time{}
|
||
}
|
||
|
||
createTournament := domain.CreateEnetpulseTournament{
|
||
TournamentID: t.ID,
|
||
Name: t.Name,
|
||
TournamentTemplateFK: tmpl.TemplateID, // DB ID of template
|
||
UpdatesCount: updatesCount,
|
||
LastUpdatedAt: lastUpdatedAt,
|
||
Status: 1, // default active
|
||
}
|
||
|
||
// Insert into DB
|
||
if _, err := s.store.CreateEnetpulseTournament(ctx, createTournament); err != nil {
|
||
// Log error but continue
|
||
// s.logger.Error("failed to store tournament", zap.String("tournament_id", t.ID), zap.Error(err))
|
||
continue
|
||
}
|
||
}
|
||
}
|
||
|
||
// s.logger.Info("Successfully fetched and stored all tournaments")
|
||
return nil
|
||
}
|
||
|
||
func (s *Service) GetAllTournaments(ctx context.Context) ([]domain.EnetpulseTournament, error) {
|
||
// 1️⃣ Fetch all tournaments from the store (database)
|
||
tournaments, err := s.store.GetAllEnetpulseTournaments(ctx)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("failed to fetch tournaments from DB: %w", err)
|
||
}
|
||
|
||
// 2️⃣ Optionally log count or do transformations
|
||
// s.logger.Info("Fetched tournaments from DB", zap.Int("count", len(tournaments)))
|
||
|
||
return tournaments, nil
|
||
}
|
||
|
||
func (s *Service) FetchAndStoreTournamentStages(ctx context.Context) error {
|
||
// 1️⃣ Get all tournaments from DB
|
||
tournaments, err := s.store.GetAllEnetpulseTournaments(ctx)
|
||
if err != nil {
|
||
return fmt.Errorf("failed to fetch tournaments from DB: %w", err)
|
||
}
|
||
|
||
// 2️⃣ Loop through each tournament
|
||
for _, t := range tournaments {
|
||
// Compose URL for each tournament
|
||
url := fmt.Sprintf(
|
||
"https://eapi.enetpulse.com/tournament_stage/list/?language_typeFK=3&tz=Europe/Sofia&tournamentFK=%s&username=%s&token=%s",
|
||
t.TournamentID,
|
||
s.cfg.EnetPulseConfig.UserName,
|
||
s.cfg.EnetPulseConfig.Token,
|
||
)
|
||
|
||
// 3️⃣ Create HTTP request
|
||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
||
if err != nil {
|
||
// log and skip
|
||
// s.logger.Error("creating tournament stages request", zap.String("tournament_id", t.TournamentID), zap.Error(err))
|
||
continue
|
||
}
|
||
|
||
// 4️⃣ Execute request
|
||
resp, err := s.httpClient.Do(req)
|
||
if err != nil {
|
||
// s.logger.Error("requesting tournament stages", zap.String("tournament_id", t.TournamentID), zap.Error(err))
|
||
continue
|
||
}
|
||
defer resp.Body.Close()
|
||
|
||
if resp.StatusCode != http.StatusOK {
|
||
body, _ := io.ReadAll(resp.Body)
|
||
// s.logger.Error("unexpected status code fetching stages", zap.String("tournament_id", t.TournamentID), zap.Int("status", resp.StatusCode))
|
||
_ = body
|
||
continue
|
||
}
|
||
|
||
// 5️⃣ Decode JSON response (based on EnetPulse response format)
|
||
var stagesResp struct {
|
||
Stages map[string]struct {
|
||
ID string `json:"id"`
|
||
Name string `json:"name"`
|
||
TournamentFK string `json:"tournamentFK"`
|
||
N string `json:"n"` // updates count
|
||
UT string `json:"ut"` // timestamp
|
||
} `json:"tournament_stages"`
|
||
}
|
||
if err := json.NewDecoder(resp.Body).Decode(&stagesResp); err != nil {
|
||
// s.logger.Error("decoding tournament stages", zap.String("tournament_id", t.TournamentID), zap.Error(err))
|
||
continue
|
||
}
|
||
|
||
// 6️⃣ Iterate and store each stage
|
||
for _, st := range stagesResp.Stages {
|
||
updatesCount := 0
|
||
if st.N != "" {
|
||
if n, err := strconv.Atoi(st.N); err == nil {
|
||
updatesCount = n
|
||
}
|
||
}
|
||
|
||
lastUpdatedAt, err := time.Parse(time.RFC3339, st.UT)
|
||
if err != nil {
|
||
lastUpdatedAt = time.Time{}
|
||
}
|
||
|
||
createStage := domain.CreateEnetpulseTournamentStage{
|
||
StageID: st.ID,
|
||
Name: st.Name,
|
||
TournamentFK: t.TournamentID, // DB ID of the tournament
|
||
UpdatesCount: updatesCount,
|
||
LastUpdatedAt: lastUpdatedAt,
|
||
Status: 1,
|
||
}
|
||
|
||
// Insert into DB
|
||
if _, err := s.store.CreateEnetpulseTournamentStage(ctx, createStage); err != nil {
|
||
// s.logger.Error("failed to store tournament stage", zap.String("stage_id", st.ID), zap.Error(err))
|
||
continue
|
||
}
|
||
}
|
||
}
|
||
|
||
// s.logger.Info("Successfully fetched and stored all tournament stages for all tournaments")
|
||
return nil
|
||
}
|
||
|
||
func (s *Service) GetAllTournamentStages(ctx context.Context) ([]domain.EnetpulseTournamentStage, error) {
|
||
// 1️⃣ Fetch all tournament stages from the store (database)
|
||
stages, err := s.store.GetAllEnetpulseTournamentStages(ctx)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("failed to fetch tournament stages from DB: %w", err)
|
||
}
|
||
|
||
// 2️⃣ Optionally log count or perform transformations
|
||
// s.logger.Info("Fetched tournament stages from DB", zap.Int("count", len(stages)))
|
||
|
||
return stages, nil
|
||
}
|
||
|
||
func (s *Service) FetchAndStoreFixtures(ctx context.Context, date string) error {
|
||
// 1️⃣ Fetch all sports from DB
|
||
sports, err := s.store.GetAllEnetpulseSports(ctx)
|
||
if err != nil {
|
||
return fmt.Errorf("failed to fetch sports from DB: %w", err)
|
||
}
|
||
|
||
// Define API fixture struct
|
||
type Fixture struct {
|
||
FixtureID string `json:"id"`
|
||
Name string `json:"name"`
|
||
SportFK string `json:"sportFK"`
|
||
TournamentFK string `json:"tournamentFK"`
|
||
TournamentTemplateFK string `json:"tournament_templateFK"`
|
||
TournamentStageFK string `json:"tournament_stageFK"`
|
||
TournamentStageName string `json:"tournament_stage_name"`
|
||
TournamentName string `json:"tournament_name"`
|
||
TournamentTemplateName string `json:"tournament_template_name"`
|
||
SportName string `json:"sport_name"`
|
||
Gender string `json:"gender"`
|
||
StartDate string `json:"startdate"` // ISO 8601
|
||
StatusType string `json:"status_type"`
|
||
StatusDescFK string `json:"status_descFK"`
|
||
RoundTypeFK string `json:"round_typeFK"`
|
||
UpdatesCount string `json:"n"` // convert to int
|
||
LastUpdatedAt string `json:"ut"` // parse to time.Time
|
||
}
|
||
|
||
// 2️⃣ Loop through each sport
|
||
for _, sport := range sports {
|
||
if sport.SportID != "1" {
|
||
continue
|
||
}
|
||
|
||
url := fmt.Sprintf(
|
||
"https://eapi.enetpulse.com/event/fixtures/?username=%s&token=%s&sportFK=%s&language_typeFK=3&date=%s",
|
||
s.cfg.EnetPulseConfig.UserName,
|
||
s.cfg.EnetPulseConfig.Token,
|
||
sport.SportID,
|
||
date,
|
||
)
|
||
|
||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
||
if err != nil {
|
||
fmt.Printf("creating request for sport %s: %v\n", sport.SportID, err)
|
||
continue
|
||
}
|
||
|
||
resp, err := s.httpClient.Do(req)
|
||
if err != nil {
|
||
fmt.Printf("requesting fixtures for sport %s: %v\n", sport.SportID, err)
|
||
continue
|
||
}
|
||
defer resp.Body.Close()
|
||
|
||
if resp.StatusCode != http.StatusOK {
|
||
body, _ := io.ReadAll(resp.Body)
|
||
fmt.Printf("failed to fetch fixtures for sport %s (status %d): %s\n",
|
||
sport.SportID, resp.StatusCode, string(body))
|
||
continue
|
||
}
|
||
|
||
// 3️⃣ Decode API response
|
||
var fixturesResp struct {
|
||
Events map[string]Fixture `json:"events"`
|
||
}
|
||
if err := json.NewDecoder(resp.Body).Decode(&fixturesResp); err != nil {
|
||
fmt.Printf("decoding fixtures for sport %s: %v\n", sport.SportID, err)
|
||
continue
|
||
}
|
||
|
||
// 4️⃣ Iterate and upsert fixtures
|
||
for _, fx := range fixturesResp.Events {
|
||
// Parse StartDate and LastUpdatedAt
|
||
startDate, err := time.Parse(time.RFC3339, fx.StartDate)
|
||
if err != nil {
|
||
fmt.Printf("invalid startDate for fixture %s: %v\n", fx.FixtureID, err)
|
||
continue
|
||
}
|
||
lastUpdated, err := time.Parse(time.RFC3339, fx.LastUpdatedAt)
|
||
if err != nil {
|
||
fmt.Printf("invalid lastUpdatedAt for fixture %s: %v\n", fx.FixtureID, err)
|
||
continue
|
||
}
|
||
|
||
// Convert UpdatesCount
|
||
updatesCount, err := strconv.Atoi(fx.UpdatesCount)
|
||
if err != nil {
|
||
fmt.Printf("invalid updatesCount for fixture %s: %v\n", fx.FixtureID, err)
|
||
updatesCount = 0
|
||
}
|
||
|
||
fixture := domain.CreateEnetpulseFixture{
|
||
FixtureID: fx.FixtureID,
|
||
Name: fx.Name,
|
||
SportFK: fx.SportFK,
|
||
TournamentFK: fx.TournamentFK,
|
||
TournamentTemplateFK: fx.TournamentTemplateFK,
|
||
TournamentStageFK: fx.TournamentStageFK,
|
||
TournamentStageName: fx.TournamentStageName,
|
||
TournamentName: fx.TournamentName,
|
||
TournamentTemplateName: fx.TournamentTemplateName,
|
||
SportName: fx.SportName,
|
||
Gender: fx.Gender,
|
||
StartDate: startDate,
|
||
StatusType: fx.StatusType,
|
||
StatusDescFK: fx.StatusDescFK,
|
||
RoundTypeFK: fx.RoundTypeFK,
|
||
UpdatesCount: updatesCount,
|
||
LastUpdatedAt: lastUpdated,
|
||
}
|
||
|
||
// 5️⃣ Save fixture using UPSERT repository method
|
||
if _, err := s.store.CreateEnetpulseFixture(ctx, fixture); err != nil {
|
||
fmt.Printf("failed upserting fixture %s: %v\n", fx.FixtureID, err)
|
||
continue
|
||
}
|
||
}
|
||
|
||
fmt.Printf("✅ Successfully fetched and stored fixtures for sport %s\n", sport.SportID)
|
||
break
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
func (s *Service) GetAllFixtures(ctx context.Context) ([]domain.EnetpulseFixture, error) {
|
||
// 1️⃣ Fetch all from store
|
||
fixtures, err := s.store.GetAllEnetpulseFixtures(ctx)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("failed to fetch fixtures from DB: %w", err)
|
||
}
|
||
return fixtures, nil
|
||
}
|
||
|
||
func (s *Service) FetchAndStoreResults(ctx context.Context) error {
|
||
sports, err := s.store.GetAllEnetpulseSports(ctx)
|
||
if err != nil {
|
||
return fmt.Errorf("failed to fetch sports from DB: %w", err)
|
||
}
|
||
|
||
for _, sport := range sports {
|
||
if sport.SportID != "1" {
|
||
continue
|
||
}
|
||
|
||
today := time.Now().Format("2006-01-02")
|
||
url := fmt.Sprintf(
|
||
"http://eapi.enetpulse.com/event/results/?sportFK=%s&date=%s&username=%s&token=%s",
|
||
sport.SportID,
|
||
today,
|
||
s.cfg.EnetPulseConfig.UserName,
|
||
s.cfg.EnetPulseConfig.Token,
|
||
)
|
||
fmt.Println("Fetching results:", url)
|
||
|
||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
||
if err != nil {
|
||
return fmt.Errorf("creating results request for sport %s: %w", sport.SportID, err)
|
||
}
|
||
|
||
resp, err := s.httpClient.Do(req)
|
||
if err != nil {
|
||
return fmt.Errorf("requesting results for sport %s: %w", sport.SportID, err)
|
||
}
|
||
defer resp.Body.Close()
|
||
|
||
if resp.StatusCode != http.StatusOK {
|
||
body, _ := io.ReadAll(resp.Body)
|
||
return fmt.Errorf("failed to fetch results for sport %s (status %d): %s",
|
||
sport.SportID, resp.StatusCode, string(body))
|
||
}
|
||
|
||
var data struct {
|
||
Events []struct {
|
||
ID string `json:"id"`
|
||
Name string `json:"name"`
|
||
SportFK string `json:"sportFK"`
|
||
TournamentFK string `json:"tournamentFK"`
|
||
TournamentTemplateFK string `json:"tournament_templateFK"`
|
||
TournamentStageFK string `json:"tournament_stageFK"`
|
||
TournamentStageName string `json:"tournament_stage_name"`
|
||
TournamentName string `json:"tournament_name"`
|
||
TournamentTemplateName string `json:"tournament_template_name"`
|
||
SportName string `json:"sport_name"`
|
||
StartDate string `json:"startdate"`
|
||
StatusType string `json:"status_type"`
|
||
StatusDescFK string `json:"status_descFK"`
|
||
RoundTypeFK string `json:"round_typeFK"`
|
||
N string `json:"n"`
|
||
UT string `json:"ut"`
|
||
|
||
Property map[string]struct {
|
||
ID string `json:"id"`
|
||
Type string `json:"type"`
|
||
Name string `json:"name"`
|
||
Value string `json:"value"`
|
||
N string `json:"n"`
|
||
UT string `json:"ut"`
|
||
} `json:"property"`
|
||
|
||
EventParticipants map[string]struct {
|
||
ID string `json:"id"`
|
||
Number string `json:"number"`
|
||
ParticipantFK string `json:"participantFK"`
|
||
EventFK string `json:"eventFK"`
|
||
|
||
Result map[string]struct {
|
||
ID string `json:"id"`
|
||
EventParticipantsFK string `json:"event_participantsFK"`
|
||
ResultTypeFK string `json:"result_typeFK"`
|
||
ResultCode string `json:"result_code"`
|
||
Value string `json:"value"`
|
||
N string `json:"n"`
|
||
UT string `json:"ut"`
|
||
} `json:"result"`
|
||
|
||
Participant struct {
|
||
ID string `json:"id"`
|
||
Name string `json:"name"`
|
||
Gender string `json:"gender"`
|
||
Type string `json:"type"`
|
||
CountryFK string `json:"countryFK"`
|
||
CountryName string `json:"country_name"`
|
||
} `json:"participant"`
|
||
} `json:"event_participants"`
|
||
} `json:"events"`
|
||
}
|
||
|
||
bodyBytes, _ := io.ReadAll(resp.Body)
|
||
if err := json.Unmarshal(bodyBytes, &data); err != nil {
|
||
return fmt.Errorf("decoding results failed: %w", err)
|
||
}
|
||
|
||
for _, event := range data.Events {
|
||
// 1️⃣ Create result record
|
||
lastUpdatedAt, _ := time.Parse(time.RFC3339, event.UT)
|
||
startDate, _ := time.Parse(time.RFC3339, event.StartDate)
|
||
|
||
createResult := domain.CreateEnetpulseResult{
|
||
ResultID: event.ID,
|
||
Name: event.Name,
|
||
SportFK: event.SportFK,
|
||
TournamentFK: event.TournamentFK,
|
||
TournamentTemplateFK: event.TournamentTemplateFK,
|
||
TournamentStageName: event.TournamentStageName,
|
||
TournamentName: event.TournamentName,
|
||
TournamentTemplateName: event.TournamentTemplateName,
|
||
SportName: event.SportName,
|
||
StartDate: startDate,
|
||
StatusType: event.StatusType,
|
||
StatusDescFK: event.StatusDescFK,
|
||
RoundTypeFK: event.RoundTypeFK,
|
||
LastUpdatedAt: lastUpdatedAt,
|
||
}
|
||
|
||
if _, err := s.store.CreateEnetpulseResult(ctx, createResult); err != nil {
|
||
fmt.Printf("❌ failed to store result %s: %v\n", event.ID, err)
|
||
continue
|
||
}
|
||
|
||
// 2️⃣ Create referees (type == "ref:participant")
|
||
for _, prop := range event.Property {
|
||
if strings.HasPrefix(prop.Type, "ref:participant") {
|
||
refCreatedAt, _ := time.Parse(time.RFC3339, prop.UT)
|
||
ref := domain.CreateEnetpulseResultReferee{
|
||
ResultFk: event.ID,
|
||
RefereeFk: prop.Value,
|
||
LastUpdatedAt: refCreatedAt,
|
||
}
|
||
if _, err := s.store.CreateEnetpulseResultReferee(ctx, ref); err != nil {
|
||
fmt.Printf("⚠️ failed to create referee %s: %v\n", prop.Name, err)
|
||
}
|
||
}
|
||
}
|
||
|
||
// 3️⃣ Create participants + their results
|
||
for _, ep := range event.EventParticipants {
|
||
p := domain.CreateEnetpulseResultParticipant{
|
||
ParticipantMapID: ep.ID,
|
||
ResultFk: ep.EventFK,
|
||
ParticipantFk: ep.ParticipantFK,
|
||
Name: ep.Participant.Name,
|
||
CountryFk: ep.Participant.CountryFK,
|
||
CountryName: ep.Participant.CountryName,
|
||
}
|
||
if _, err := s.store.CreateEnetpulseResultParticipant(ctx, p); err != nil {
|
||
fmt.Printf("⚠️ failed to create participant %s: %v\n", ep.Participant.Name, err)
|
||
continue
|
||
}
|
||
}
|
||
}
|
||
|
||
break // stop after the first sport (football)
|
||
}
|
||
|
||
fmt.Println("✅ Successfully fetched and stored EnetPulse results + participants + referees")
|
||
return nil
|
||
}
|
||
|
||
func (s *Service) GetAllResults(ctx context.Context) ([]domain.EnetpulseResult, error) {
|
||
results, err := s.store.GetAllEnetpulseResults(ctx)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("failed to fetch results from DB: %w", err)
|
||
}
|
||
|
||
fmt.Printf("✅ Retrieved %d results from DB\n", len(results))
|
||
return results, nil
|
||
}
|
||
|
||
// FetchAndStoreOutcomeTypes fetches outcome types from EnetPulse API and stores them in the DB.
|
||
func (s *Service) FetchAndStoreOutcomeTypes(ctx context.Context) error {
|
||
// 1️⃣ Compose EnetPulse API URL
|
||
url := fmt.Sprintf(
|
||
"http://eapi.enetpulse.com/static/outcome_type/?language_typeFK=3&username=%s&token=%s",
|
||
s.cfg.EnetPulseConfig.UserName,
|
||
s.cfg.EnetPulseConfig.Token,
|
||
)
|
||
|
||
// 2️⃣ Create HTTP request
|
||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
||
if err != nil {
|
||
return fmt.Errorf("failed to create outcome types request: %w", err)
|
||
}
|
||
|
||
// 3️⃣ Execute request
|
||
resp, err := s.httpClient.Do(req)
|
||
if err != nil {
|
||
return fmt.Errorf("failed to call EnetPulse outcome_type API: %w", err)
|
||
}
|
||
defer resp.Body.Close()
|
||
|
||
if resp.StatusCode != http.StatusOK {
|
||
body, _ := io.ReadAll(resp.Body)
|
||
return fmt.Errorf("unexpected status %d fetching outcome types: %s", resp.StatusCode, string(body))
|
||
}
|
||
|
||
// 4️⃣ Decode JSON response
|
||
var outcomeResp struct {
|
||
OutcomeTypes map[string]struct {
|
||
ID string `json:"id"`
|
||
Name string `json:"name"`
|
||
Description string `json:"description"`
|
||
N string `json:"n"`
|
||
UT string `json:"ut"`
|
||
} `json:"outcome_type"`
|
||
}
|
||
|
||
if err := json.NewDecoder(resp.Body).Decode(&outcomeResp); err != nil {
|
||
return fmt.Errorf("failed to decode outcome types JSON: %w", err)
|
||
}
|
||
|
||
// 5️⃣ Iterate and store each outcome type
|
||
for _, ot := range outcomeResp.OutcomeTypes {
|
||
updatesCount := 0
|
||
if ot.N != "" {
|
||
if n, err := strconv.Atoi(ot.N); err == nil {
|
||
updatesCount = n
|
||
}
|
||
}
|
||
|
||
lastUpdatedAt, err := time.Parse(time.RFC3339, ot.UT)
|
||
if err != nil {
|
||
lastUpdatedAt = time.Time{}
|
||
}
|
||
|
||
createOutcome := domain.CreateEnetpulseOutcomeType{
|
||
OutcomeTypeID: ot.ID,
|
||
Name: ot.Name,
|
||
Description: ot.Description,
|
||
UpdatesCount: int32(updatesCount),
|
||
LastUpdatedAt: lastUpdatedAt,
|
||
}
|
||
|
||
// 6️⃣ Save to DB (upsert)
|
||
if _, err := s.store.CreateEnetpulseOutcomeType(ctx, createOutcome); err != nil {
|
||
// Optionally log the failure, continue to next
|
||
continue
|
||
}
|
||
}
|
||
|
||
// s.logger.Info("✅ Successfully fetched and stored all EnetPulse outcome types")
|
||
return nil
|
||
}
|
||
|
||
// GetAllOutcomeTypes retrieves all stored outcome types from the DB.
|
||
func (s *Service) GetAllOutcomeTypes(ctx context.Context) ([]domain.EnetpulseOutcomeType, error) {
|
||
outcomes, err := s.store.GetAllEnetpulseOutcomeTypes(ctx)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("failed to fetch outcome types from DB: %w", err)
|
||
}
|
||
|
||
// s.logger.Info("✅ Fetched outcome types from DB", zap.Int("count", len(outcomes)))
|
||
return outcomes, nil
|
||
}
|
||
|
||
func (s *Service) FetchAndStorePreodds(ctx context.Context) error {
|
||
// 1️⃣ Fetch all fixtures
|
||
fixtures, err := s.store.GetAllEnetpulseFixtures(ctx)
|
||
if err != nil {
|
||
return fmt.Errorf("failed to fetch fixtures: %w", err)
|
||
}
|
||
|
||
// 2️⃣ Fetch all outcome types
|
||
outcomeTypes, err := s.store.GetAllEnetpulseOutcomeTypes(ctx)
|
||
if err != nil {
|
||
return fmt.Errorf("failed to fetch outcome types: %w", err)
|
||
}
|
||
|
||
// 3️⃣ Loop through each fixture
|
||
for _, fixture := range fixtures {
|
||
// 4️⃣ Loop through each outcome type
|
||
for _, outcome := range outcomeTypes {
|
||
|
||
url := fmt.Sprintf(
|
||
"http://eapi.enetpulse.com/preodds/event/?objectFK=%s&odds_providerFK=%s&outcome_typeFK=%s&username=%s&token=%s",
|
||
fixture.FixtureID,
|
||
s.cfg.EnetPulseConfig.ProviderID,
|
||
outcome.OutcomeTypeID,
|
||
s.cfg.EnetPulseConfig.UserName,
|
||
s.cfg.EnetPulseConfig.Token,
|
||
)
|
||
|
||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
||
if err != nil {
|
||
continue
|
||
}
|
||
|
||
resp, err := s.httpClient.Do(req)
|
||
if err != nil {
|
||
continue
|
||
}
|
||
defer resp.Body.Close()
|
||
|
||
if resp.StatusCode != http.StatusOK {
|
||
continue
|
||
}
|
||
|
||
// Struct adjusted exactly to match JSON structure
|
||
var preoddsResp struct {
|
||
Preodds map[string]struct {
|
||
ID string `json:"id"`
|
||
OutcomeTypeFK string `json:"outcome_typeFK"`
|
||
OutcomeScopeFK string `json:"outcome_scopeFK"`
|
||
OutcomeSubtypeFK string `json:"outcome_subtypeFK"`
|
||
EventParticipantNumber string `json:"event_participant_number"`
|
||
Iparam string `json:"iparam"`
|
||
Iparam2 string `json:"iparam2"`
|
||
Dparam string `json:"dparam"`
|
||
Dparam2 string `json:"dparam2"`
|
||
Sparam string `json:"sparam"`
|
||
N string `json:"n"`
|
||
UT string `json:"ut"`
|
||
|
||
PreoddsBettingOffers map[string]struct {
|
||
ID string `json:"id"`
|
||
BettingOfferStatusFK string `json:"bettingoffer_statusFK"`
|
||
OddsProviderFK string `json:"odds_providerFK"`
|
||
Odds string `json:"odds"`
|
||
OddsOld string `json:"odds_old"`
|
||
Active string `json:"active"`
|
||
CouponKey string `json:"couponKey"`
|
||
N string `json:"n"`
|
||
UT string `json:"ut"`
|
||
} `json:"preodds_bettingoffers"`
|
||
} `json:"preodds"`
|
||
}
|
||
|
||
if err := json.NewDecoder(resp.Body).Decode(&preoddsResp); err != nil {
|
||
continue
|
||
}
|
||
|
||
for _, p := range preoddsResp.Preodds {
|
||
// Convert numeric/string fields safely
|
||
updatesCount, _ := strconv.Atoi(defaultIfEmpty(p.N, "0"))
|
||
eventParticipantNumber, _ := strconv.Atoi(defaultIfEmpty(p.EventParticipantNumber, "0"))
|
||
lastUpdatedAt := parseTimeOrNow(p.UT)
|
||
|
||
createPreodds := domain.CreateEnetpulsePreodds{
|
||
PreoddsID: p.ID,
|
||
EventFK: fixture.FixtureID,
|
||
OutcomeTypeFK: p.OutcomeTypeFK,
|
||
OutcomeScopeFK: p.OutcomeScopeFK,
|
||
OutcomeSubtypeFK: p.OutcomeSubtypeFK,
|
||
EventParticipantNumber: eventParticipantNumber,
|
||
IParam: p.Iparam,
|
||
IParam2: p.Iparam2,
|
||
DParam: p.Dparam,
|
||
DParam2: p.Dparam2,
|
||
SParam: p.Sparam,
|
||
UpdatesCount: updatesCount,
|
||
LastUpdatedAt: lastUpdatedAt,
|
||
}
|
||
|
||
// Store preodds in DB
|
||
_, err := s.store.CreateEnetpulsePreodds(ctx, createPreodds)
|
||
if err != nil {
|
||
continue
|
||
}
|
||
|
||
// 5️⃣ Loop through betting offers map
|
||
for _, o := range p.PreoddsBettingOffers {
|
||
bettingUpdates, _ := strconv.Atoi(defaultIfEmpty(o.N, "0"))
|
||
bettingLastUpdatedAt := parseTimeOrNow(o.UT)
|
||
|
||
odds, _ := strconv.ParseFloat(defaultIfEmpty(o.Odds, "0"), 64)
|
||
oddsOld, _ := strconv.ParseFloat(defaultIfEmpty(o.OddsOld, "0"), 64)
|
||
bettingOfferStatusFK, _ := strconv.Atoi(defaultIfEmpty(o.BettingOfferStatusFK, "0"))
|
||
oddsProviderFK, _ := strconv.Atoi(defaultIfEmpty(o.OddsProviderFK, "0"))
|
||
|
||
createOffer := domain.CreateEnetpulsePreoddsBettingOffer{
|
||
BettingOfferID: o.ID,
|
||
PreoddsFK: createPreodds.PreoddsID,
|
||
BettingOfferStatusFK: int32(bettingOfferStatusFK),
|
||
OddsProviderFK: int32(oddsProviderFK),
|
||
Odds: odds,
|
||
OddsOld: oddsOld,
|
||
Active: o.Active,
|
||
CouponKey: o.CouponKey,
|
||
UpdatesCount: bettingUpdates,
|
||
LastUpdatedAt: bettingLastUpdatedAt,
|
||
}
|
||
|
||
_, _ = s.store.CreateEnetpulsePreoddsBettingOffer(ctx, createOffer)
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
// Utility helpers
|
||
func defaultIfEmpty(val, def string) string {
|
||
if val == "" {
|
||
return def
|
||
}
|
||
return val
|
||
}
|
||
|
||
func parseTimeOrNow(t string) time.Time {
|
||
parsed, err := time.Parse(time.RFC3339, t)
|
||
if err != nil {
|
||
return time.Now().UTC()
|
||
}
|
||
return parsed
|
||
}
|
||
|
||
|
||
// helper function to parse string to int32 safely
|
||
func ParseStringToInt32(s string) int32 {
|
||
if s == "" {
|
||
return 0
|
||
}
|
||
i, _ := strconv.Atoi(s)
|
||
return int32(i)
|
||
}
|
||
|
||
func (s *Service) GetAllPreodds(ctx context.Context) ([]domain.EnetpulsePreodds, error) {
|
||
preodds, err := s.store.GetAllEnetpulsePreodds(ctx)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("failed to fetch preodds from DB: %w", err)
|
||
}
|
||
fmt.Printf("\n\nFetched Preodds are:%v\n\n", preodds)
|
||
return preodds, nil
|
||
}
|
||
|
||
// FetchAndStoreBettingOffers fetches betting offers from EnetPulse API and stores them in the DB.
|
||
func (s *Service) StoreBettingOffers(ctx context.Context, preoddsID string, oddsProviderIDs []int32) error {
|
||
// 1️⃣ Compose API URL
|
||
providers := make([]string, len(oddsProviderIDs))
|
||
for i, p := range oddsProviderIDs {
|
||
providers[i] = strconv.Itoa(int(p))
|
||
}
|
||
url := fmt.Sprintf(
|
||
"http://eapi.enetpulse.com/preodds_bettingoffer/?preoddsFK=%s&odds_providerFK=%s&username=%s&token=%s",
|
||
preoddsID,
|
||
strings.Join(providers, ","),
|
||
s.cfg.EnetPulseConfig.UserName,
|
||
s.cfg.EnetPulseConfig.Token,
|
||
)
|
||
|
||
// 2️⃣ Create HTTP request
|
||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
||
if err != nil {
|
||
return fmt.Errorf("failed to create betting offer request: %w", err)
|
||
}
|
||
|
||
// 3️⃣ Execute request
|
||
resp, err := s.httpClient.Do(req)
|
||
if err != nil {
|
||
return fmt.Errorf("failed to call EnetPulse betting offer API: %w", err)
|
||
}
|
||
defer resp.Body.Close()
|
||
|
||
if resp.StatusCode != http.StatusOK {
|
||
body, _ := io.ReadAll(resp.Body)
|
||
return fmt.Errorf("unexpected status %d fetching betting offers: %s", resp.StatusCode, string(body))
|
||
}
|
||
|
||
// 4️⃣ Decode JSON response
|
||
var offerResp struct {
|
||
BettingOffers map[string]struct {
|
||
ID string `json:"id"`
|
||
PreoddsFK string `json:"preodds_fk"`
|
||
BettingOfferStatusFK int32 `json:"bettingoffer_status_fk"`
|
||
OddsProviderFK int32 `json:"odds_provider_fk"`
|
||
Odds float64 `json:"odds"`
|
||
OddsOld float64 `json:"odds_old"`
|
||
Active string `json:"active"`
|
||
CouponKey string `json:"coupon_key"`
|
||
N string `json:"n"`
|
||
UT string `json:"ut"`
|
||
} `json:"bettingoffer"`
|
||
}
|
||
|
||
if err := json.NewDecoder(resp.Body).Decode(&offerResp); err != nil {
|
||
return fmt.Errorf("failed to decode betting offers JSON: %w", err)
|
||
}
|
||
|
||
// 5️⃣ Iterate and store each betting offer
|
||
for _, o := range offerResp.BettingOffers {
|
||
updatesCount := 0
|
||
if o.N != "" {
|
||
if n, err := strconv.Atoi(o.N); err == nil {
|
||
updatesCount = n
|
||
}
|
||
}
|
||
|
||
lastUpdatedAt, err := time.Parse(time.RFC3339, o.UT)
|
||
if err != nil {
|
||
lastUpdatedAt = time.Time{}
|
||
}
|
||
|
||
createOffer := domain.CreateEnetpulsePreoddsBettingOffer{
|
||
BettingOfferID: o.ID,
|
||
PreoddsFK: preoddsID,
|
||
BettingOfferStatusFK: o.BettingOfferStatusFK,
|
||
OddsProviderFK: o.OddsProviderFK,
|
||
Odds: o.Odds,
|
||
OddsOld: o.OddsOld,
|
||
Active: o.Active,
|
||
CouponKey: o.CouponKey,
|
||
UpdatesCount: int(updatesCount),
|
||
LastUpdatedAt: lastUpdatedAt,
|
||
}
|
||
|
||
// 6️⃣ Save to DB
|
||
if _, err := s.store.CreateEnetpulsePreoddsBettingOffer(ctx, createOffer); err != nil {
|
||
// optionally log the failure and continue
|
||
continue
|
||
}
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
// GetAllBettingOffers retrieves all stored betting offers from the DB.
|
||
func (s *Service) GetAllBettingOffers(ctx context.Context) ([]domain.EnetpulsePreoddsBettingOffer, error) {
|
||
offers, err := s.store.GetAllEnetpulsePreoddsBettingOffers(ctx)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("failed to fetch betting offers from DB: %w", err)
|
||
}
|
||
return offers, nil
|
||
}
|
||
|
||
func (s *Service) GetAllPreoddsWithBettingOffers(ctx context.Context) ([]domain.EnetpulsePreodds, error) {
|
||
preodds, err := s.store.GetAllEnetpulsePreoddsWithBettingOffers(ctx)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("failed to fetch preodds with betting offers from DB: %w", err)
|
||
}
|
||
|
||
return preodds, nil
|
||
}
|
||
|
||
func (s *Service) GetFixturesWithPreodds(ctx context.Context) ([]domain.EnetpulseFixtureWithPreodds, error) {
|
||
// 1️⃣ Fetch fixtures and their associated preodds from the repository
|
||
fixtures, err := s.store.GetFixturesWithPreodds(ctx)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("failed to fetch fixtures with preodds from DB: %w", err)
|
||
}
|
||
|
||
return fixtures, nil
|
||
}
|
||
|
||
func (s *Service) FetchTournamentTemplates(ctx context.Context) (*domain.TournamentTemplatesResponse, error) {
|
||
url := fmt.Sprintf(
|
||
"http://eapi.enetpulse.com/tournamenttemplate/list/?username=%s&token=%s",
|
||
s.cfg.EnetPulseConfig.UserName, s.cfg.EnetPulseConfig.Token,
|
||
)
|
||
|
||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("creating tournament template request: %w", err)
|
||
}
|
||
|
||
resp, err := s.httpClient.Do(req)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("requesting tournament templates: %w", err)
|
||
}
|
||
defer resp.Body.Close()
|
||
|
||
if resp.StatusCode != http.StatusOK {
|
||
body, _ := io.ReadAll(resp.Body)
|
||
return nil, fmt.Errorf("failed to fetch tournament templates: %s, body: %s", resp.Status, string(body))
|
||
}
|
||
|
||
var templatesResp domain.TournamentTemplatesResponse
|
||
if err := json.NewDecoder(resp.Body).Decode(&templatesResp); err != nil {
|
||
return nil, fmt.Errorf("decoding tournament templates response: %w", err)
|
||
}
|
||
|
||
// Optionally save to DB or cache
|
||
return &templatesResp, nil
|
||
}
|
||
|
||
func (s *Service) FetchTournamentTemplateParticipants(ctx context.Context, templateID string, opts domain.ParticipantsOptions) (*domain.TournamentTemplateParticipantsResponse, error) {
|
||
// Build URL with optional parameters
|
||
url := fmt.Sprintf(
|
||
"http://eapi.enetpulse.com/tournamenttemplate/participants/?username=%s&token=%s&id=%s",
|
||
s.cfg.EnetPulseConfig.UserName, s.cfg.EnetPulseConfig.Token, templateID,
|
||
)
|
||
|
||
// Append optional params if set
|
||
if opts.IncludeProperties {
|
||
url += "&includeProperties=yes"
|
||
}
|
||
if opts.IncludeParticipantProperties {
|
||
url += "&includeParticipantProperties=yes"
|
||
}
|
||
if opts.IncludeParticipantSports {
|
||
url += "&includeParticipantSports=yes"
|
||
}
|
||
if opts.IncludeCountries {
|
||
url += "&includeCountries=yes"
|
||
}
|
||
if opts.IncludeCountryCodes {
|
||
url += "&includeCountryCodes=yes"
|
||
}
|
||
if opts.ParticipantType != "" {
|
||
url += "&participantType=" + opts.ParticipantType
|
||
}
|
||
|
||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("creating tournament participants request: %w", err)
|
||
}
|
||
|
||
resp, err := s.httpClient.Do(req)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("requesting tournament participants: %w", err)
|
||
}
|
||
defer resp.Body.Close()
|
||
|
||
if resp.StatusCode != http.StatusOK {
|
||
body, _ := io.ReadAll(resp.Body)
|
||
return nil, fmt.Errorf("failed to fetch tournament participants: %s, body: %s", resp.Status, string(body))
|
||
}
|
||
|
||
var participantsResp domain.TournamentTemplateParticipantsResponse
|
||
if err := json.NewDecoder(resp.Body).Decode(&participantsResp); err != nil {
|
||
return nil, fmt.Errorf("decoding tournament participants response: %w", err)
|
||
}
|
||
|
||
// Optionally save to DB or cache
|
||
return &participantsResp, nil
|
||
}
|
||
|
||
func (s *Service) FetchTournaments(ctx context.Context, templateID string) error {
|
||
url := fmt.Sprintf(
|
||
"http://eapi.enetpulse.com/tournament/list/?tournament_templateFK=%s&username=%s&token=%s",
|
||
templateID, s.cfg.EnetPulseConfig.UserName, s.cfg.EnetPulseConfig.Token,
|
||
)
|
||
|
||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
||
if err != nil {
|
||
return fmt.Errorf("creating tournament request: %w", err)
|
||
}
|
||
|
||
resp, err := s.httpClient.Do(req)
|
||
if err != nil {
|
||
return fmt.Errorf("requesting tournaments: %w", err)
|
||
}
|
||
defer resp.Body.Close()
|
||
|
||
if resp.StatusCode != http.StatusOK {
|
||
body, _ := io.ReadAll(resp.Body)
|
||
return fmt.Errorf("failed to fetch tournaments (status %d): %s",
|
||
resp.StatusCode, string(body))
|
||
}
|
||
|
||
var tournamentsResp domain.TournamentsResponse
|
||
if err := json.NewDecoder(resp.Body).Decode(&tournamentsResp); err != nil {
|
||
return fmt.Errorf("decoding tournaments response: %w", err)
|
||
}
|
||
|
||
// Example: save tournaments to store or log them
|
||
for _, tournament := range tournamentsResp.Tournaments {
|
||
fmt.Printf("Tournament ID=%s Name=%s TemplateFK=%s UpdatedAt=%s\n",
|
||
tournament.ID, tournament.Name, tournament.TournamentTemplateFK, tournament.UT)
|
||
// e.g. s.store.SaveTournament(ctx, tournament)
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
func (s *Service) FetchTournamentParticipants(ctx context.Context, tournamentID string) error {
|
||
url := fmt.Sprintf(
|
||
"http://eapi.enetpulse.com/tournament_stage/participants/?id=%s&participantType=team&username=%s&token=%s",
|
||
tournamentID, s.cfg.EnetPulseConfig.UserName, s.cfg.EnetPulseConfig.Token,
|
||
)
|
||
|
||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
||
if err != nil {
|
||
return fmt.Errorf("creating tournament participants request: %w", err)
|
||
}
|
||
|
||
resp, err := s.httpClient.Do(req)
|
||
if err != nil {
|
||
return fmt.Errorf("requesting tournament participants: %w", err)
|
||
}
|
||
defer resp.Body.Close()
|
||
|
||
if resp.StatusCode != http.StatusOK {
|
||
body, _ := io.ReadAll(resp.Body)
|
||
return fmt.Errorf("failed to fetch tournament participants (status %d): %s",
|
||
resp.StatusCode, string(body))
|
||
}
|
||
|
||
var participantsResp domain.TournamentParticipantsResponse
|
||
if err := json.NewDecoder(resp.Body).Decode(&participantsResp); err != nil {
|
||
return fmt.Errorf("decoding tournament participants response: %w", err)
|
||
}
|
||
|
||
// Example: loop participants
|
||
for tid, t := range participantsResp.Tournaments {
|
||
fmt.Printf("Tournament ID=%s Name=%s has %d participants\n",
|
||
tid, t.Name, len(t.Participants))
|
||
// You can loop deeper into t.Participants and store
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
func (s *Service) FetchTournamentStages(ctx context.Context, tournamentFK string) (*domain.TournamentStagesResponse, error) {
|
||
url := fmt.Sprintf(
|
||
"http://eapi.enetpulse.com/tournament_stage/list/?language_typeFK=3&tz=Europe/Sofia&tournamentFK=%s&username=%s&token=%s",
|
||
tournamentFK, s.cfg.EnetPulseConfig.UserName, s.cfg.EnetPulseConfig.Token,
|
||
)
|
||
|
||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("creating tournament stages request: %w", err)
|
||
}
|
||
|
||
resp, err := s.httpClient.Do(req)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("requesting tournament stages: %w", err)
|
||
}
|
||
defer resp.Body.Close()
|
||
|
||
if resp.StatusCode != http.StatusOK {
|
||
body, _ := io.ReadAll(resp.Body)
|
||
return nil, fmt.Errorf("unexpected status code %d: %s", resp.StatusCode, string(body))
|
||
}
|
||
|
||
var stagesResp domain.TournamentStagesResponse
|
||
if err := json.NewDecoder(resp.Body).Decode(&stagesResp); err != nil {
|
||
return nil, fmt.Errorf("decoding tournament stages response: %w", err)
|
||
}
|
||
|
||
// optionally save to DB or cache here
|
||
return &stagesResp, nil
|
||
}
|
||
|
||
func (s *Service) FetchTournamentStageParticipants(
|
||
ctx context.Context,
|
||
stageID string,
|
||
includeProps, includeParticipantProps, includeCountries, includeCountryCodes bool,
|
||
) (*domain.TournamentStageParticipantsResponse, error) {
|
||
url := fmt.Sprintf(
|
||
"http://eapi.enetpulse.com/tournament_stage/participants/?id=%s&includeProperties=%s&includeParticipantProperties=%s&includeCountries=%s&includeCountryCodes=%s&username=%s&token=%s",
|
||
stageID,
|
||
boolToYesNo(includeProps),
|
||
boolToYesNo(includeParticipantProps),
|
||
boolToYesNo(includeCountries),
|
||
boolToYesNo(includeCountryCodes),
|
||
s.cfg.EnetPulseConfig.UserName, s.cfg.EnetPulseConfig.Token,
|
||
)
|
||
|
||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("creating participants request: %w", err)
|
||
}
|
||
|
||
resp, err := s.httpClient.Do(req)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("requesting participants: %w", err)
|
||
}
|
||
defer resp.Body.Close()
|
||
|
||
if resp.StatusCode != http.StatusOK {
|
||
body, _ := io.ReadAll(resp.Body)
|
||
return nil, fmt.Errorf("unexpected status code %d: %s", resp.StatusCode, string(body))
|
||
}
|
||
|
||
var participantsResp domain.TournamentStageParticipantsResponse
|
||
if err := json.NewDecoder(resp.Body).Decode(&participantsResp); err != nil {
|
||
return nil, fmt.Errorf("decoding participants response: %w", err)
|
||
}
|
||
|
||
// optionally save to DB or cache here
|
||
return &participantsResp, nil
|
||
}
|
||
|
||
// helper function to convert bool to yes/no
|
||
func boolToYesNo(v bool) string {
|
||
if v {
|
||
return "yes"
|
||
}
|
||
return "no"
|
||
}
|
||
|
||
func (s *Service) FetchDailyEvents(ctx context.Context, req domain.DailyEventsRequest) (*domain.DailyEventsResponse, error) {
|
||
baseURL := "http://eapi.enetpulse.com/event/daily/"
|
||
query := fmt.Sprintf("?username=%s&token=%s&language_typeFK=3", s.cfg.EnetPulseConfig.UserName, s.cfg.EnetPulseConfig.Token)
|
||
|
||
// Required at least one of sportFK / tournament_templateFK / tournament_stageFK
|
||
if req.SportFK != 0 {
|
||
query += fmt.Sprintf("&sportFK=%d", req.SportFK)
|
||
}
|
||
if req.TournamentTemplateFK != 0 {
|
||
query += fmt.Sprintf("&tournament_templateFK=%d", req.TournamentTemplateFK)
|
||
}
|
||
// if req.TournamentStageFK != 0 {
|
||
// query += fmt.Sprintf("&tournament_stageFK=%d", req.TournamentStageFK)
|
||
// }
|
||
|
||
// Optionals
|
||
if req.Date != "" {
|
||
query += fmt.Sprintf("&date=%s", req.Date)
|
||
}
|
||
if req.Live != "" {
|
||
query += fmt.Sprintf("&live=%s", req.Live)
|
||
}
|
||
if req.IncludeVenue != "" {
|
||
query += fmt.Sprintf("&includeVenue=%s", req.IncludeVenue)
|
||
}
|
||
if req.StatusType != "" {
|
||
query += fmt.Sprintf("&status_type=%s", req.StatusType)
|
||
}
|
||
if req.IncludeEventProperties != "" {
|
||
query += fmt.Sprintf("&includeEventProperties=%s", req.IncludeEventProperties)
|
||
}
|
||
if req.IncludeCountryCodes != "" {
|
||
query += fmt.Sprintf("&includeCountryCodes=%s", req.IncludeCountryCodes)
|
||
}
|
||
if req.IncludeFirstLastName != "" {
|
||
query += fmt.Sprintf("&includeFirstLastName=%s", req.IncludeFirstLastName)
|
||
}
|
||
if req.IncludeDeleted != "" {
|
||
query += fmt.Sprintf("&includeDeleted=%s", req.IncludeDeleted)
|
||
}
|
||
|
||
fullURL := baseURL + query
|
||
|
||
httpReq, err := http.NewRequestWithContext(ctx, http.MethodGet, fullURL, nil)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
resp, err := s.httpClient.Do(httpReq)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
defer resp.Body.Close()
|
||
|
||
if resp.StatusCode != http.StatusOK {
|
||
body, _ := io.ReadAll(resp.Body)
|
||
return nil, fmt.Errorf("failed to fetch daily events: status %d body: %s", resp.StatusCode, string(body))
|
||
}
|
||
|
||
var dailyResp domain.DailyEventsResponse
|
||
if err := json.NewDecoder(resp.Body).Decode(&dailyResp); err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
return &dailyResp, nil
|
||
}
|
||
|
||
func (s *Service) FetchResults(ctx context.Context, params domain.ResultsRequest) (*domain.ResultsResponse, error) {
|
||
// Build base URL
|
||
url := fmt.Sprintf("http://eapi.enetpulse.com/event/results/?username=%s&token=%s",
|
||
s.cfg.EnetPulseConfig.UserName, s.cfg.EnetPulseConfig.Token)
|
||
|
||
// Required filters (at least one of them)
|
||
if params.SportFK != 0 {
|
||
url += fmt.Sprintf("&sportFK=%d", params.SportFK)
|
||
}
|
||
if params.TournamentTemplateFK != 0 {
|
||
url += fmt.Sprintf("&tournament_templateFK=%d", params.TournamentTemplateFK)
|
||
}
|
||
// if params.TournamentStageFK != 0 {
|
||
// url += fmt.Sprintf("&tournament_stageFK=%d", params.TournamentStageFK)
|
||
// }
|
||
|
||
// Optional filters
|
||
if params.LanguageTypeFK != 0 {
|
||
url += fmt.Sprintf("&language_typeFK=%d", params.LanguageTypeFK)
|
||
} else {
|
||
url += "&language_typeFK=3" // default English
|
||
}
|
||
if params.Date != "" {
|
||
url += fmt.Sprintf("&date=%s", params.Date)
|
||
}
|
||
if params.Live != "" {
|
||
url += fmt.Sprintf("&live=%s", params.Live)
|
||
}
|
||
if params.IncludeVenue {
|
||
url += "&includeVenue=yes"
|
||
}
|
||
if !params.IncludeEventProperties {
|
||
url += "&includeEventProperties=no"
|
||
}
|
||
if params.IncludeCountryCodes {
|
||
url += "&includeCountryCodes=yes"
|
||
}
|
||
if params.IncludeFirstLastName {
|
||
url += "&includeFirstLastName=yes"
|
||
}
|
||
if params.Limit > 0 {
|
||
url += fmt.Sprintf("&limit=%d", params.Limit)
|
||
}
|
||
if params.Offset > 0 {
|
||
url += fmt.Sprintf("&offset=%d", params.Offset)
|
||
}
|
||
|
||
// Make request
|
||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("creating results request: %w", err)
|
||
}
|
||
|
||
resp, err := s.httpClient.Do(req)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("requesting results: %w", err)
|
||
}
|
||
defer resp.Body.Close()
|
||
|
||
if resp.StatusCode != http.StatusOK {
|
||
body, _ := io.ReadAll(resp.Body)
|
||
return nil, fmt.Errorf("unexpected status %d: %s", resp.StatusCode, string(body))
|
||
}
|
||
|
||
// Decode response
|
||
var resultsResp domain.ResultsResponse
|
||
if err := json.NewDecoder(resp.Body).Decode(&resultsResp); err != nil {
|
||
return nil, fmt.Errorf("decoding results response: %w", err)
|
||
}
|
||
|
||
return &resultsResp, nil
|
||
}
|
||
|
||
func (s *Service) FetchEventDetails(ctx context.Context, params domain.EventDetailsRequest) (*domain.EventDetailsResponse, error) {
|
||
// Base URL
|
||
url := fmt.Sprintf("http://eapi.enetpulse.com/event/details/?username=%s&token=%s&id=%d",
|
||
s.cfg.EnetPulseConfig.UserName, s.cfg.EnetPulseConfig.Token, params.ID)
|
||
|
||
// Optional flags
|
||
if params.IncludeLineups {
|
||
url += "&includeLineups=yes"
|
||
}
|
||
if params.IncludeIncidents {
|
||
url += "&includeIncidents=yes"
|
||
}
|
||
if !params.IncludeExtendedResults {
|
||
url += "&includeExtendedResults=no"
|
||
}
|
||
if params.IncludeProperties {
|
||
url += "&includeProperties=yes"
|
||
}
|
||
if params.IncludeLivestats {
|
||
url += "&includeLivestats=yes"
|
||
}
|
||
if params.IncludeVenue {
|
||
url += "&includeVenue=yes"
|
||
}
|
||
if params.IncludeCountryCodes {
|
||
url += "&includeCountryCodes=yes"
|
||
}
|
||
if params.IncludeFirstLastName {
|
||
url += "&includeFirstLastName=yes"
|
||
}
|
||
if params.IncludeDeleted {
|
||
url += "&includeDeleted=yes"
|
||
}
|
||
if params.IncludeReference {
|
||
url += "&includeReference=yes"
|
||
}
|
||
if params.IncludeObjectParticipants {
|
||
url += "&includeObjectParticipants=yes"
|
||
}
|
||
if params.IncludeEventIncidentRelation {
|
||
url += "&includeEventIncidentRelation=yes"
|
||
}
|
||
if params.IncludeTeamProperties {
|
||
url += "&includeTeamProperties=yes"
|
||
}
|
||
if params.IncludeObjectRound {
|
||
url += "&includeObjectRound=yes"
|
||
}
|
||
|
||
// Make request
|
||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("creating event details request: %w", err)
|
||
}
|
||
|
||
resp, err := s.httpClient.Do(req)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("requesting event details: %w", err)
|
||
}
|
||
defer resp.Body.Close()
|
||
|
||
if resp.StatusCode != http.StatusOK {
|
||
body, _ := io.ReadAll(resp.Body)
|
||
return nil, fmt.Errorf("unexpected status %d: %s", resp.StatusCode, string(body))
|
||
}
|
||
|
||
// Decode response
|
||
var detailsResp domain.EventDetailsResponse
|
||
if err := json.NewDecoder(resp.Body).Decode(&detailsResp); err != nil {
|
||
return nil, fmt.Errorf("decoding event details response: %w", err)
|
||
}
|
||
|
||
return &detailsResp, nil
|
||
}
|
||
|
||
func (s *Service) FetchEventList(ctx context.Context, params domain.EventListRequest) (*domain.EventListResponse, error) {
|
||
// You must provide either TournamentFK or TournamentStageFK
|
||
if params.TournamentFK == 0 {
|
||
return nil, fmt.Errorf("either TournamentFK or TournamentStageFK is required")
|
||
}
|
||
|
||
// Base URL
|
||
url := fmt.Sprintf("http://eapi.enetpulse.com/event/list/?username=%s&token=%s",
|
||
s.cfg.EnetPulseConfig.UserName, s.cfg.EnetPulseConfig.Token)
|
||
|
||
// Mandatory one of
|
||
if params.TournamentFK != 0 {
|
||
url += fmt.Sprintf("&tournamentFK=%d", params.TournamentFK)
|
||
}
|
||
// if params.TournamentStageFK != 0 {
|
||
// url += fmt.Sprintf("&tournament_stageFK=%d", params.TournamentStageFK)
|
||
// }
|
||
|
||
// Optional parameters
|
||
if !params.IncludeEventProperties {
|
||
url += "&includeEventProperties=no"
|
||
}
|
||
if params.StatusType != "" {
|
||
url += "&status_type=" + params.StatusType
|
||
}
|
||
if params.IncludeVenue {
|
||
url += "&includeVenue=yes"
|
||
}
|
||
if params.IncludeDeleted {
|
||
url += "&includeDeleted=yes"
|
||
}
|
||
if params.Limit > 0 {
|
||
url += fmt.Sprintf("&limit=%d", params.Limit)
|
||
}
|
||
if params.Offset > 0 {
|
||
url += fmt.Sprintf("&offset=%d", params.Offset)
|
||
}
|
||
|
||
// Make request
|
||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("creating event list request: %w", err)
|
||
}
|
||
|
||
resp, err := s.httpClient.Do(req)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("requesting event list: %w", err)
|
||
}
|
||
defer resp.Body.Close()
|
||
|
||
if resp.StatusCode != http.StatusOK {
|
||
body, _ := io.ReadAll(resp.Body)
|
||
return nil, fmt.Errorf("unexpected status %d: %s", resp.StatusCode, string(body))
|
||
}
|
||
|
||
// Decode response
|
||
var eventListResp domain.EventListResponse
|
||
if err := json.NewDecoder(resp.Body).Decode(&eventListResp); err != nil {
|
||
return nil, fmt.Errorf("decoding event list response: %w", err)
|
||
}
|
||
|
||
return &eventListResp, nil
|
||
}
|
||
|
||
func (s *Service) FetchParticipantFixtures(ctx context.Context, params domain.ParticipantFixturesRequest) (*domain.ParticipantFixturesResponse, error) {
|
||
// You must provide ParticipantFK
|
||
if params.ParticipantFK == 0 {
|
||
return nil, fmt.Errorf("ParticipantFK is required")
|
||
}
|
||
|
||
// Base URL
|
||
url := fmt.Sprintf("http://eapi.enetpulse.com/event/participant_fixtures/?username=%s&token=%s",
|
||
s.cfg.EnetPulseConfig.UserName, s.cfg.EnetPulseConfig.Token)
|
||
|
||
// Mandatory param
|
||
url += fmt.Sprintf("&participantFK=%d", params.ParticipantFK)
|
||
|
||
// Optionals
|
||
if params.SportFK != 0 {
|
||
url += fmt.Sprintf("&sportFK=%d", params.SportFK)
|
||
}
|
||
if params.TournamentFK != 0 {
|
||
url += fmt.Sprintf("&tournamentFK=%d", params.TournamentFK)
|
||
}
|
||
if params.TournamentTemplateFK != 0 {
|
||
url += fmt.Sprintf("&tournament_templateFK=%d", params.TournamentTemplateFK)
|
||
}
|
||
// if params.TournamentStageFK != 0 {
|
||
// url += fmt.Sprintf("&tournament_stageFK=%d", params.TournamentStageFK)
|
||
// }
|
||
if params.Date != "" {
|
||
url += "&date=" + params.Date
|
||
}
|
||
if params.Live != "" {
|
||
url += "&live=" + params.Live
|
||
}
|
||
if params.Limit > 0 {
|
||
url += fmt.Sprintf("&limit=%d", params.Limit)
|
||
}
|
||
if params.Offset > 0 {
|
||
url += fmt.Sprintf("&offset=%d", params.Offset)
|
||
}
|
||
|
||
if params.IncludeVenue {
|
||
url += "&includeVenue=yes"
|
||
}
|
||
if params.IncludeCountryCodes {
|
||
url += "&includeCountryCodes=yes"
|
||
}
|
||
if params.IncludeFirstLastName {
|
||
url += "&includeFirstLastName=yes"
|
||
}
|
||
if !params.IncludeEventProperties {
|
||
// default yes → only append when false
|
||
url += "&includeEventProperties=no"
|
||
}
|
||
if params.LanguageTypeFK != 0 {
|
||
url += fmt.Sprintf("&language_typeFK=%d", params.LanguageTypeFK)
|
||
}
|
||
|
||
// Make request
|
||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("creating participant fixtures request: %w", err)
|
||
}
|
||
|
||
resp, err := s.httpClient.Do(req)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("requesting participant fixtures: %w", err)
|
||
}
|
||
defer resp.Body.Close()
|
||
|
||
if resp.StatusCode != http.StatusOK {
|
||
body, _ := io.ReadAll(resp.Body)
|
||
return nil, fmt.Errorf("unexpected status %d: %s", resp.StatusCode, string(body))
|
||
}
|
||
|
||
// Decode response
|
||
var fixturesResp domain.ParticipantFixturesResponse
|
||
if err := json.NewDecoder(resp.Body).Decode(&fixturesResp); err != nil {
|
||
return nil, fmt.Errorf("decoding participant fixtures response: %w", err)
|
||
}
|
||
|
||
return &fixturesResp, nil
|
||
}
|
||
|
||
func (s *Service) FetchParticipantResults(ctx context.Context, params domain.ParticipantResultsRequest) (*domain.ParticipantResultsResponse, error) {
|
||
// Required param
|
||
if params.ParticipantFK == 0 {
|
||
return nil, fmt.Errorf("participantFK is required")
|
||
}
|
||
|
||
// Base URL
|
||
url := fmt.Sprintf("http://eapi.enetpulse.com/event/participant_results/?username=%s&token=%s&participantFK=%d",
|
||
s.cfg.EnetPulseConfig.UserName, s.cfg.EnetPulseConfig.Token, params.ParticipantFK)
|
||
|
||
// Optional parameters
|
||
if params.Limit > 0 {
|
||
url += fmt.Sprintf("&limit=%d", params.Limit)
|
||
}
|
||
if params.Offset > 0 {
|
||
url += fmt.Sprintf("&offset=%d", params.Offset)
|
||
}
|
||
if params.IncludeDeleted {
|
||
url += "&includeDeleted=yes"
|
||
}
|
||
if params.IncludeVenue {
|
||
url += "&includeVenue=yes"
|
||
}
|
||
|
||
// Make request
|
||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("creating participant_results request: %w", err)
|
||
}
|
||
|
||
resp, err := s.httpClient.Do(req)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("requesting participant_results: %w", err)
|
||
}
|
||
defer resp.Body.Close()
|
||
|
||
if resp.StatusCode != http.StatusOK {
|
||
body, _ := io.ReadAll(resp.Body)
|
||
return nil, fmt.Errorf("unexpected status %d: %s", resp.StatusCode, string(body))
|
||
}
|
||
|
||
// Decode response
|
||
var prResp domain.ParticipantResultsResponse
|
||
if err := json.NewDecoder(resp.Body).Decode(&prResp); err != nil {
|
||
return nil, fmt.Errorf("decoding participant_results response: %w", err)
|
||
}
|
||
|
||
return &prResp, nil
|
||
}
|
||
|
||
func (s *Service) FetchTeamLogo(ctx context.Context, teamFK int64) (*domain.TeamLogoResponse, error) {
|
||
if teamFK == 0 {
|
||
return nil, fmt.Errorf("teamFK is required")
|
||
}
|
||
|
||
// Build URL
|
||
url := fmt.Sprintf("http://eapi.enetpulse.com/image/team_logo/?teamFK=%d&username=%s&token=%s",
|
||
teamFK, s.cfg.EnetPulseConfig.UserName, s.cfg.EnetPulseConfig.Token)
|
||
|
||
// Make request
|
||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("creating team logo request: %w", err)
|
||
}
|
||
|
||
resp, err := s.httpClient.Do(req)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("requesting team logo: %w", err)
|
||
}
|
||
defer resp.Body.Close()
|
||
|
||
if resp.StatusCode != http.StatusOK {
|
||
body, _ := io.ReadAll(resp.Body)
|
||
return nil, fmt.Errorf("unexpected status %d: %s", resp.StatusCode, string(body))
|
||
}
|
||
|
||
// Read image bytes
|
||
data, err := io.ReadAll(resp.Body)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("reading team logo body: %w", err)
|
||
}
|
||
|
||
// Build response
|
||
return &domain.TeamLogoResponse{
|
||
ContentType: resp.Header.Get("Content-Type"),
|
||
Data: data,
|
||
URL: url, // optional: you can also return the URL for reference
|
||
}, nil
|
||
}
|
||
|
||
func (s *Service) FetchTeamShirts(ctx context.Context, teamFK int64) (*domain.TeamShirtsResponse, error) {
|
||
if teamFK == 0 {
|
||
return nil, fmt.Errorf("teamFK is required")
|
||
}
|
||
|
||
// Build URL
|
||
url := fmt.Sprintf("http://eapi.enetpulse.com/image/team_shirt/?teamFK=%d&username=%s&token=%s",
|
||
teamFK, s.cfg.EnetPulseConfig.UserName, s.cfg.EnetPulseConfig.Token)
|
||
|
||
// Make request
|
||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("creating team shirts request: %w", err)
|
||
}
|
||
|
||
resp, err := s.httpClient.Do(req)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("requesting team shirts: %w", err)
|
||
}
|
||
defer resp.Body.Close()
|
||
|
||
if resp.StatusCode != http.StatusOK {
|
||
body, _ := io.ReadAll(resp.Body)
|
||
return nil, fmt.Errorf("unexpected status %d: %s", resp.StatusCode, string(body))
|
||
}
|
||
|
||
// Read response bytes — because shirts may come back as JSON list of URLs *or* image bytes
|
||
data, err := io.ReadAll(resp.Body)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("reading team shirts body: %w", err)
|
||
}
|
||
|
||
// Build response — since Enetpulse typically returns a list of image URLs,
|
||
// we’ll return raw bytes if only one image, or JSON if multiple images.
|
||
return &domain.TeamShirtsResponse{
|
||
ContentType: resp.Header.Get("Content-Type"),
|
||
RawData: data,
|
||
URL: url,
|
||
}, nil
|
||
}
|
||
|
||
func (s *Service) FetchCountryFlag(ctx context.Context, countryFK int64) (*domain.ImageResponse, error) {
|
||
if countryFK == 0 {
|
||
return nil, fmt.Errorf("countryFK is required")
|
||
}
|
||
|
||
// Build URL
|
||
url := fmt.Sprintf("http://eapi.enetpulse.com/image/country_flag/?countryFK=%d&username=%s&token=%s",
|
||
countryFK, s.cfg.EnetPulseConfig.UserName, s.cfg.EnetPulseConfig.Token)
|
||
|
||
// Create HTTP request
|
||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("creating country flag request: %w", err)
|
||
}
|
||
|
||
// Execute request
|
||
resp, err := s.httpClient.Do(req)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("requesting country flag: %w", err)
|
||
}
|
||
defer resp.Body.Close()
|
||
|
||
if resp.StatusCode != http.StatusOK {
|
||
body, _ := io.ReadAll(resp.Body)
|
||
return nil, fmt.Errorf("unexpected status %d: %s", resp.StatusCode, string(body))
|
||
}
|
||
|
||
// Read image bytes
|
||
data, err := io.ReadAll(resp.Body)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("reading country flag body: %w", err)
|
||
}
|
||
|
||
// Return response
|
||
return &domain.ImageResponse{
|
||
ContentType: resp.Header.Get("Content-Type"),
|
||
RawData: data,
|
||
URL: url,
|
||
}, nil
|
||
}
|
||
|
||
func (s *Service) FetchPreMatchOdds(ctx context.Context, params domain.PreMatchOddsRequest) (*domain.PreMatchOddsResponse, error) {
|
||
// Mandatory parameter check
|
||
if params.ObjectFK == 0 {
|
||
return nil, fmt.Errorf("objectFK (event ID) is required")
|
||
}
|
||
|
||
// Base URL
|
||
url := fmt.Sprintf("http://eapi.enetpulse.com/preodds/event/?username=%s&token=%s",
|
||
s.cfg.EnetPulseConfig.UserName, s.cfg.EnetPulseConfig.Token)
|
||
|
||
// Mandatory parameters
|
||
url += fmt.Sprintf("&objectFK=%d", params.ObjectFK)
|
||
if len(params.OddsProviderFK) > 0 {
|
||
url += "&odds_providerFK=" + joinIntSlice(params.OddsProviderFK)
|
||
}
|
||
if params.OutcomeTypeFK != 0 {
|
||
url += fmt.Sprintf("&outcome_typeFK=%d", params.OutcomeTypeFK)
|
||
}
|
||
if params.OutcomeScopeFK != 0 {
|
||
url += fmt.Sprintf("&outcome_scopeFK=%d", params.OutcomeScopeFK)
|
||
}
|
||
if params.OutcomeSubtypeFK != 0 {
|
||
url += fmt.Sprintf("&outcome_subtypeFK=%d", params.OutcomeSubtypeFK)
|
||
}
|
||
|
||
// Optional parameters
|
||
if params.Limit > 0 {
|
||
url += fmt.Sprintf("&limit=%d", params.Limit)
|
||
}
|
||
if params.Offset > 0 {
|
||
url += fmt.Sprintf("&offset=%d", params.Offset)
|
||
}
|
||
if params.LanguageTypeFK != 0 {
|
||
url += fmt.Sprintf("&language_typeFK=%d", params.LanguageTypeFK)
|
||
}
|
||
|
||
// Create HTTP request
|
||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("creating pre-match odds request: %w", err)
|
||
}
|
||
|
||
// Execute request
|
||
resp, err := s.httpClient.Do(req)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("requesting pre-match odds: %w", err)
|
||
}
|
||
defer resp.Body.Close()
|
||
|
||
if resp.StatusCode != http.StatusOK {
|
||
body, _ := io.ReadAll(resp.Body)
|
||
return nil, fmt.Errorf("unexpected status %d: %s", resp.StatusCode, string(body))
|
||
}
|
||
|
||
// Decode response
|
||
var oddsResp domain.PreMatchOddsResponse
|
||
if err := json.NewDecoder(resp.Body).Decode(&oddsResp); err != nil {
|
||
return nil, fmt.Errorf("decoding pre-match odds response: %w", err)
|
||
}
|
||
|
||
return &oddsResp, nil
|
||
}
|
||
|
||
// Helper function to join int slice with comma
|
||
func joinIntSlice(slice []int64) string {
|
||
result := ""
|
||
for i, v := range slice {
|
||
if i > 0 {
|
||
result += ","
|
||
}
|
||
result += fmt.Sprintf("%d", v)
|
||
}
|
||
return result
|
||
}
|
||
|
||
func (s *Service) FetchTournamentStageOdds(ctx context.Context, params domain.TournamentStageOddsRequest) (*domain.TournamentStageOddsResponse, error) {
|
||
// Mandatory parameter check
|
||
if params.ObjectFK == 0 {
|
||
return nil, fmt.Errorf("objectFK (tournament stage ID) is required")
|
||
}
|
||
if params.OutcomeTypeFK == 0 {
|
||
return nil, fmt.Errorf("outcomeTypeFK is required")
|
||
}
|
||
|
||
// Base URL
|
||
url := fmt.Sprintf("http://eapi.enetpulse.com/preodds/tournament_stage/?username=%s&token=%s",
|
||
s.cfg.EnetPulseConfig.UserName, s.cfg.EnetPulseConfig.Token)
|
||
|
||
// Mandatory parameters
|
||
url += fmt.Sprintf("&objectFK=%d", params.ObjectFK)
|
||
url += fmt.Sprintf("&outcome_typeFK=%d", params.OutcomeTypeFK)
|
||
if params.OddsProviderFK != 0 {
|
||
url += fmt.Sprintf("&odds_providerFK=%d", params.OddsProviderFK)
|
||
}
|
||
if params.OutcomeScopeFK != 0 {
|
||
url += fmt.Sprintf("&outcome_scopeFK=%d", params.OutcomeScopeFK)
|
||
}
|
||
if params.OutcomeSubtypeFK != 0 {
|
||
url += fmt.Sprintf("&outcome_subtypeFK=%d", params.OutcomeSubtypeFK)
|
||
}
|
||
|
||
// Optional parameters
|
||
if params.Limit > 0 {
|
||
url += fmt.Sprintf("&limit=%d", params.Limit)
|
||
}
|
||
if params.Offset > 0 {
|
||
url += fmt.Sprintf("&offset=%d", params.Offset)
|
||
}
|
||
if params.LanguageTypeFK != 0 {
|
||
url += fmt.Sprintf("&language_typeFK=%d", params.LanguageTypeFK)
|
||
}
|
||
|
||
// Create HTTP request
|
||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("creating tournament stage odds request: %w", err)
|
||
}
|
||
|
||
// Execute request
|
||
resp, err := s.httpClient.Do(req)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("requesting tournament stage odds: %w", err)
|
||
}
|
||
defer resp.Body.Close()
|
||
|
||
if resp.StatusCode != http.StatusOK {
|
||
body, _ := io.ReadAll(resp.Body)
|
||
return nil, fmt.Errorf("unexpected status %d: %s", resp.StatusCode, string(body))
|
||
}
|
||
|
||
// Decode response
|
||
var oddsResp domain.TournamentStageOddsResponse
|
||
if err := json.NewDecoder(resp.Body).Decode(&oddsResp); err != nil {
|
||
return nil, fmt.Errorf("decoding tournament stage odds response: %w", err)
|
||
}
|
||
|
||
return &oddsResp, nil
|
||
}
|
||
|
||
func (s *Service) FetchTournamentOdds(ctx context.Context, params domain.TournamentOddsRequest) (*domain.TournamentOddsResponse, error) {
|
||
// Mandatory parameter check
|
||
if params.ObjectFK == 0 {
|
||
return nil, fmt.Errorf("objectFK (tournament ID) is required")
|
||
}
|
||
if params.OutcomeTypeFK == 0 {
|
||
return nil, fmt.Errorf("outcomeTypeFK is required")
|
||
}
|
||
|
||
// Base URL
|
||
url := fmt.Sprintf("http://eapi.enetpulse.com/preodds/tournament/?username=%s&token=%s",
|
||
s.cfg.EnetPulseConfig.UserName, s.cfg.EnetPulseConfig.Token)
|
||
|
||
// Mandatory parameters
|
||
url += fmt.Sprintf("&objectFK=%d", params.ObjectFK)
|
||
url += fmt.Sprintf("&outcome_typeFK=%d", params.OutcomeTypeFK)
|
||
if params.OddsProviderFK != 0 {
|
||
url += fmt.Sprintf("&odds_providerFK=%d", params.OddsProviderFK)
|
||
}
|
||
if params.OutcomeScopeFK != 0 {
|
||
url += fmt.Sprintf("&outcome_scopeFK=%d", params.OutcomeScopeFK)
|
||
}
|
||
if params.OutcomeSubtypeFK != 0 {
|
||
url += fmt.Sprintf("&outcome_subtypeFK=%d", params.OutcomeSubtypeFK)
|
||
}
|
||
|
||
// Optional parameters
|
||
if params.Limit > 0 {
|
||
url += fmt.Sprintf("&limit=%d", params.Limit)
|
||
}
|
||
if params.Offset > 0 {
|
||
url += fmt.Sprintf("&offset=%d", params.Offset)
|
||
}
|
||
if params.LanguageTypeFK != 0 {
|
||
url += fmt.Sprintf("&language_typeFK=%d", params.LanguageTypeFK)
|
||
}
|
||
|
||
// Create HTTP request
|
||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("creating tournament odds request: %w", err)
|
||
}
|
||
|
||
// Execute request
|
||
resp, err := s.httpClient.Do(req)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("requesting tournament odds: %w", err)
|
||
}
|
||
defer resp.Body.Close()
|
||
|
||
if resp.StatusCode != http.StatusOK {
|
||
body, _ := io.ReadAll(resp.Body)
|
||
return nil, fmt.Errorf("unexpected status %d: %s", resp.StatusCode, string(body))
|
||
}
|
||
|
||
// Decode response
|
||
var oddsResp domain.TournamentOddsResponse
|
||
if err := json.NewDecoder(resp.Body).Decode(&oddsResp); err != nil {
|
||
return nil, fmt.Errorf("decoding tournament odds response: %w", err)
|
||
}
|
||
|
||
return &oddsResp, nil
|
||
}
|