diff --git a/db/migrations/000001_fortune.down.sql b/db/migrations/000001_fortune.down.sql index 82d488d..2724f06 100644 --- a/db/migrations/000001_fortune.down.sql +++ b/db/migrations/000001_fortune.down.sql @@ -75,4 +75,5 @@ DROP TABLE IF EXISTS supported_operations; DROP TABLE IF EXISTS refresh_tokens; DROP TABLE IF EXISTS otps; DROP TABLE IF EXISTS odds; -DROP TABLE IF EXISTS events; \ No newline at end of file +DROP TABLE IF EXISTS events; +DROP TABLE IF EXISTS leagues; \ No newline at end of file diff --git a/db/migrations/000001_fortune.up.sql b/db/migrations/000001_fortune.up.sql index 50a4b75..25f23f3 100644 --- a/db/migrations/000001_fortune.up.sql +++ b/db/migrations/000001_fortune.up.sql @@ -232,6 +232,13 @@ CREATE TABLE companies ( admin_id BIGINT NOT NULL, wallet_id BIGINT NOT NULL ); +CREATE TABLE leagues ( + id BIGINT PRIMARY KEY, + name TEXT NOT NULL, + country_code TEXT, + bet365_id INT, + is_active BOOLEAN DEFAULT true +); -- Views CREATE VIEW companies_details AS SELECT companies.*, diff --git a/db/query/leagues.sql b/db/query/leagues.sql new file mode 100644 index 0000000..b4905c8 --- /dev/null +++ b/db/query/leagues.sql @@ -0,0 +1,37 @@ +-- name: InsertLeague :exec +INSERT INTO leagues ( + id, + name, + country_code, + bet365_id, + is_active +) VALUES ( + $1, $2, $3, $4, $5 +) +ON CONFLICT (id) DO UPDATE +SET name = EXCLUDED.name, + country_code = EXCLUDED.country_code, + bet365_id = EXCLUDED.bet365_id, + is_active = EXCLUDED.is_active; +-- name: GetSupportedLeagues :many +SELECT id, + name, + country_code, + bet365_id, + is_active +FROM leagues +WHERE is_active = true; +-- name: CheckLeagueSupport :one +SELECT EXISTS( + SELECT 1 + FROM leagues + WHERE id = $1 + AND is_active = true +); +-- name: UpdateLeague :exec +UPDATE leagues +SET name = $1, + country_code = $2, + bet365_id = $3, + is_active = $4 +WHERE id = $5; \ No newline at end of file diff --git a/gen/db/leagues.sql.go b/gen/db/leagues.sql.go new file mode 100644 index 0000000..e8589dd --- /dev/null +++ b/gen/db/leagues.sql.go @@ -0,0 +1,128 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.29.0 +// source: leagues.sql + +package dbgen + +import ( + "context" + + "github.com/jackc/pgx/v5/pgtype" +) + +const CheckLeagueSupport = `-- name: CheckLeagueSupport :one +SELECT EXISTS( + SELECT 1 + FROM leagues + WHERE id = $1 + AND is_active = true +) +` + +func (q *Queries) CheckLeagueSupport(ctx context.Context, id int64) (bool, error) { + row := q.db.QueryRow(ctx, CheckLeagueSupport, id) + var exists bool + err := row.Scan(&exists) + return exists, err +} + +const GetSupportedLeagues = `-- name: GetSupportedLeagues :many +SELECT id, + name, + country_code, + bet365_id, + is_active +FROM leagues +WHERE is_active = true +` + +func (q *Queries) GetSupportedLeagues(ctx context.Context) ([]League, error) { + rows, err := q.db.Query(ctx, GetSupportedLeagues) + if err != nil { + return nil, err + } + defer rows.Close() + var items []League + for rows.Next() { + var i League + if err := rows.Scan( + &i.ID, + &i.Name, + &i.CountryCode, + &i.Bet365ID, + &i.IsActive, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const InsertLeague = `-- name: InsertLeague :exec +INSERT INTO leagues ( + id, + name, + country_code, + bet365_id, + is_active +) VALUES ( + $1, $2, $3, $4, $5 +) +ON CONFLICT (id) DO UPDATE +SET name = EXCLUDED.name, + country_code = EXCLUDED.country_code, + bet365_id = EXCLUDED.bet365_id, + is_active = EXCLUDED.is_active +` + +type InsertLeagueParams struct { + ID int64 `json:"id"` + Name string `json:"name"` + CountryCode pgtype.Text `json:"country_code"` + Bet365ID pgtype.Int4 `json:"bet365_id"` + IsActive pgtype.Bool `json:"is_active"` +} + +func (q *Queries) InsertLeague(ctx context.Context, arg InsertLeagueParams) error { + _, err := q.db.Exec(ctx, InsertLeague, + arg.ID, + arg.Name, + arg.CountryCode, + arg.Bet365ID, + arg.IsActive, + ) + return err +} + +const UpdateLeague = `-- name: UpdateLeague :exec +UPDATE leagues +SET name = $1, + country_code = $2, + bet365_id = $3, + is_active = $4 +WHERE id = $5 +` + +type UpdateLeagueParams struct { + Name string `json:"name"` + CountryCode pgtype.Text `json:"country_code"` + Bet365ID pgtype.Int4 `json:"bet365_id"` + IsActive pgtype.Bool `json:"is_active"` + ID int64 `json:"id"` +} + +func (q *Queries) UpdateLeague(ctx context.Context, arg UpdateLeagueParams) error { + _, err := q.db.Exec(ctx, UpdateLeague, + arg.Name, + arg.CountryCode, + arg.Bet365ID, + arg.IsActive, + arg.ID, + ) + return err +} diff --git a/gen/db/models.go b/gen/db/models.go index 75c6af8..d45d0a3 100644 --- a/gen/db/models.go +++ b/gen/db/models.go @@ -199,6 +199,14 @@ type Event struct { Source pgtype.Text `json:"source"` } +type League struct { + ID int64 `json:"id"` + Name string `json:"name"` + CountryCode pgtype.Text `json:"country_code"` + Bet365ID pgtype.Int4 `json:"bet365_id"` + IsActive pgtype.Bool `json:"is_active"` +} + type Notification struct { ID string `json:"id"` RecipientID int64 `json:"recipient_id"` diff --git a/internal/domain/league.go b/internal/domain/league.go index a4a9cc2..f5ac35e 100644 --- a/internal/domain/league.go +++ b/internal/domain/league.go @@ -1,39 +1,9 @@ package domain -// TODO Will make this dynamic by moving into the database - -var SupportedLeagues = []int64{ - // Football - 10041282, //Premier League - 10083364, //La Liga - 10041095, //German Bundesliga - 10041100, //Ligue 1 - 10041809, //UEFA Champions League - 10041957, //UEFA Europa League - 10079560, //UEFA Conference League - 10047168, // US MLS - 10044469, // Ethiopian Premier League - 10050282, //UEFA Nations League - - 10043156, //England FA Cup - 10042103, //France Cup - 10041088, //Premier League 2 - 10084250, //Turkiye Super League - 10041187, //Kenya Super League - 10041315, //Italian Serie A - 10041391, //Netherlands Eredivisie - - // Basketball - 173998768, //NBA - 10041830, //NBA - 10049984, //WNBA - 10037165, //German Bundesliga - 10036608, //Italian Lega 1 - 10040795, //EuroLeague - - // Ice Hockey - 10037477, //NHL - 10037447, //AHL - 10069385, //IIHF World Championship - +type League struct { + ID int64 + Name string + CountryCode string + Bet365ID int32 + IsActive bool } diff --git a/internal/domain/resultres.go b/internal/domain/resultres.go index 493c0c9..3e53ce6 100644 --- a/internal/domain/resultres.go +++ b/internal/domain/resultres.go @@ -9,7 +9,7 @@ type BaseResultResponse struct { Results []json.RawMessage `json:"results"` } -type League struct { +type LeagueRes struct { ID string `json:"id"` Name string `json:"name"` CC string `json:"cc"` @@ -28,14 +28,14 @@ type Score struct { } type FootballResultResponse struct { - ID string `json:"id"` - SportID string `json:"sport_id"` - Time string `json:"time"` - TimeStatus string `json:"time_status"` - League League `json:"league"` - Home Team `json:"home"` - Away Team `json:"away"` - SS string `json:"ss"` + ID string `json:"id"` + SportID string `json:"sport_id"` + Time string `json:"time"` + TimeStatus string `json:"time_status"` + League LeagueRes `json:"league"` + Home Team `json:"home"` + Away Team `json:"away"` + SS string `json:"ss"` Scores struct { FirstHalf Score `json:"1"` SecondHalf Score `json:"2"` @@ -67,14 +67,14 @@ type FootballResultResponse struct { } type BasketballResultResponse struct { - ID string `json:"id"` - SportID string `json:"sport_id"` - Time string `json:"time"` - TimeStatus string `json:"time_status"` - League League `json:"league"` - Home Team `json:"home"` - Away Team `json:"away"` - SS string `json:"ss"` + ID string `json:"id"` + SportID string `json:"sport_id"` + Time string `json:"time"` + TimeStatus string `json:"time_status"` + League LeagueRes `json:"league"` + Home Team `json:"home"` + Away Team `json:"away"` + SS string `json:"ss"` Scores struct { FirstQuarter Score `json:"1"` SecondQuarter Score `json:"2"` @@ -114,14 +114,14 @@ type BasketballResultResponse struct { Bet365ID string `json:"bet365_id"` } type IceHockeyResultResponse struct { - ID string `json:"id"` - SportID string `json:"sport_id"` - Time string `json:"time"` - TimeStatus string `json:"time_status"` - League League `json:"league"` - Home Team `json:"home"` - Away Team `json:"away"` - SS string `json:"ss"` + ID string `json:"id"` + SportID string `json:"sport_id"` + Time string `json:"time"` + TimeStatus string `json:"time_status"` + League LeagueRes `json:"league"` + Home Team `json:"home"` + Away Team `json:"away"` + SS string `json:"ss"` Scores struct { FirstPeriod Score `json:"1"` SecondPeriod Score `json:"2"` diff --git a/internal/repository/league.go b/internal/repository/league.go new file mode 100644 index 0000000..1bbec9c --- /dev/null +++ b/internal/repository/league.go @@ -0,0 +1,61 @@ +package repository + +import ( + "context" + + dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db" + "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" + "github.com/jackc/pgx/v5/pgtype" +) + +func (s *Store) SaveLeague(ctx context.Context, l domain.League) error { + return s.queries.InsertLeague(ctx, dbgen.InsertLeagueParams{ + ID: l.ID, + Name: l.Name, + CountryCode: pgtype.Text{String: l.CountryCode, Valid: true}, + Bet365ID: pgtype.Int4{Int32: l.Bet365ID, Valid: true}, + IsActive: pgtype.Bool{Bool: l.IsActive, Valid: true}, + }) +} + +func (s *Store) GetSupportedLeagues(ctx context.Context) ([]domain.League, error) { + leagues, err := s.queries.GetSupportedLeagues(ctx) + if err != nil { + return nil, err + } + + supportedLeagues := make([]domain.League, len(leagues)) + for i, league := range leagues { + supportedLeagues[i] = domain.League{ + ID: league.ID, + Name: league.Name, + CountryCode: league.CountryCode.String, + Bet365ID: league.Bet365ID.Int32, + IsActive: league.IsActive.Bool, + } + } + return supportedLeagues, nil +} + +func (s *Store) CheckLeagueSupport(ctx context.Context, leagueID int64) (bool, error) { + return s.queries.CheckLeagueSupport(ctx, leagueID) +} + +// TODO: change to only take league id instad of the whole league +func (s *Store) SetLeagueActive(ctx context.Context, l domain.League) error { + return s.queries.UpdateLeague(ctx, dbgen.UpdateLeagueParams{ + Name: l.Name, + CountryCode: pgtype.Text{String: l.CountryCode, Valid: true}, + Bet365ID: pgtype.Int4{Int32: l.Bet365ID, Valid: true}, + IsActive: pgtype.Bool{Bool: true, Valid: true}, + }) +} + +func (s *Store) SetLeagueInActive(ctx context.Context, l domain.League) error { + return s.queries.UpdateLeague(ctx, dbgen.UpdateLeagueParams{ + Name: l.Name, + CountryCode: pgtype.Text{String: l.CountryCode, Valid: true}, + Bet365ID: pgtype.Int4{Int32: l.Bet365ID, Valid: true}, + IsActive: pgtype.Bool{Bool: false, Valid: true}, + }) +} diff --git a/internal/services/event/service.go b/internal/services/event/service.go index 2c6bc52..2382091 100644 --- a/internal/services/event/service.go +++ b/internal/services/event/service.go @@ -7,7 +7,6 @@ import ( "io" "log" "net/http" - "slices" "strconv" "sync" "time" @@ -242,11 +241,16 @@ func (s *service) fetchUpcomingEventsFromProvider(ctx context.Context, url, sour continue } - if !slices.Contains(domain.SupportedLeagues, leagueID) { - // fmt.Printf("⚠️ Skipping league %s (%d) as it is not supported\n", ev.League.Name, leagueID) + // doesn't make sense to save and check back to back, but for now it can be here + s.store.SaveLeague(ctx, domain.League{ + ID: leagueID, + Name: ev.League.Name, + IsActive: true, + }) + + if supported, err := s.store.CheckLeagueSupport(ctx, leagueID); !supported || err != nil { skippedLeague = append(skippedLeague, ev.League.Name) - // ! for now - // continue + continue } event := domain.UpcomingEvent{