fix market fetching

This commit is contained in:
Samuel Tariku 2025-04-14 19:48:22 +03:00
parent 35b846bcc8
commit d5be4803ae
12 changed files with 587 additions and 577 deletions

View File

@ -1,43 +1,52 @@
-- name: InsertNonLiveOdd :exec -- name: InsertNonLiveOdd :exec
INSERT INTO odds ( INSERT INTO odds (
event_id, event_id,
fi, fi,
market_type, market_type,
market_name, market_name,
market_category, market_category,
market_id, market_id,
name, name,
handicap, handicap,
odds_value, odds_value,
section, section,
category, category,
raw_odds, raw_odds,
is_active, is_active,
source, source,
fetched_at fetched_at
) VALUES ( )
$1, $2, $3, $4, $5, $6, $7, VALUES (
$8, $9, $10, $11, $12, $13, $14, $15 $1,
) $2,
ON CONFLICT (event_id, market_id) DO UPDATE SET $3,
odds_value = EXCLUDED.odds_value, $4,
raw_odds = EXCLUDED.raw_odds, $5,
market_type = EXCLUDED.market_type, $6,
market_name = EXCLUDED.market_name, $7,
$8,
$9,
$10,
$11,
$12,
$13,
$14,
$15
) ON CONFLICT (event_id, market_id) DO
UPDATE
SET odds_value = EXCLUDED.odds_value,
raw_odds = EXCLUDED.raw_odds,
market_type = EXCLUDED.market_type,
market_name = EXCLUDED.market_name,
market_category = EXCLUDED.market_category, market_category = EXCLUDED.market_category,
name = EXCLUDED.name, name = EXCLUDED.name,
handicap = EXCLUDED.handicap, handicap = EXCLUDED.handicap,
fetched_at = EXCLUDED.fetched_at, fetched_at = EXCLUDED.fetched_at,
is_active = EXCLUDED.is_active, is_active = EXCLUDED.is_active,
source = EXCLUDED.source, source = EXCLUDED.source,
fi = EXCLUDED.fi; fi = EXCLUDED.fi;
-- name: GetPrematchOdds :many -- name: GetPrematchOdds :many
SELECT SELECT event_id,
event_id,
fi, fi,
market_type, market_type,
market_name, market_name,
@ -53,11 +62,10 @@ SELECT
source, source,
is_active is_active
FROM odds FROM odds
WHERE is_active = true AND source = 'b365api'; WHERE is_active = true
AND source = 'b365api';
-- name: GetALLPrematchOdds :many -- name: GetALLPrematchOdds :many
SELECT SELECT event_id,
event_id,
fi, fi,
market_type, market_type,
market_name, market_name,
@ -73,23 +81,20 @@ SELECT
source, source,
is_active is_active
FROM odds FROM odds
WHERE is_active = true AND source = 'b365api'; WHERE is_active = true
AND source = 'b365api';
-- name: GetRawOddsByMarketID :many -- name: GetRawOddsByMarketID :many
SELECT SELECT id,
id, raw_odds,
raw_odds,
fetched_at fetched_at
FROM odds FROM odds
WHERE WHERE market_id = $1
market_id = $1 AND AND fi = $2
is_active = true AND AND is_active = true
source = 'b365api' AND source = 'b365api'
LIMIT $2 OFFSET $3; LIMIT $3 OFFSET $4;
-- name: GetPrematchOddsByUpcomingID :many -- name: GetPrematchOddsByUpcomingID :many
SELECT SELECT o.event_id,
o.event_id,
o.fi, o.fi,
o.market_type, o.market_type,
o.market_name, o.market_name,
@ -105,10 +110,10 @@ SELECT
o.source, o.source,
o.is_active o.is_active
FROM odds o FROM odds o
JOIN events e ON o.fi = e.id JOIN events e ON o.fi = e.id
WHERE e.id = $1 WHERE e.id = $1
AND e.is_live = false AND e.is_live = false
AND e.status = 'upcoming' AND e.status = 'upcoming'
AND o.is_active = true AND o.is_active = true
AND o.source = 'b365api' AND o.source = 'b365api'
LIMIT $2 OFFSET $3; LIMIT $2 OFFSET $3;

View File

@ -1354,53 +1354,6 @@ const docTemplate = `{
} }
} }
}, },
"/prematch/odds/raw/{market_id}": {
"get": {
"description": "Retrieve raw odds records using a Market ID",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"prematch"
],
"summary": "Retrieve raw odds by Market ID",
"parameters": [
{
"type": "string",
"description": "Market ID",
"name": "market_id",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/domain.RawOddsByMarketID"
}
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
}
}
}
},
"/prematch/odds/upcoming/{upcoming_id}": { "/prematch/odds/upcoming/{upcoming_id}": {
"get": { "get": {
"description": "Retrieve prematch odds by upcoming event ID (FI from Bet365) with optional pagination", "description": "Retrieve prematch odds by upcoming event ID (FI from Bet365) with optional pagination",
@ -1460,6 +1413,60 @@ const docTemplate = `{
} }
} }
}, },
"/prematch/odds/upcoming/{upcoming_id}/market/{market_id}": {
"get": {
"description": "Retrieve raw odds records using a Market ID",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"prematch"
],
"summary": "Retrieve raw odds by Market ID",
"parameters": [
{
"type": "string",
"description": "Upcoming ID",
"name": "upcoming_id",
"in": "path",
"required": true
},
{
"type": "string",
"description": "Market ID",
"name": "market_id",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/domain.RawOddsByMarketID"
}
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
}
}
}
},
"/prematch/odds/{event_id}": { "/prematch/odds/{event_id}": {
"get": { "get": {
"description": "Retrieve prematch odds for a specific event by event ID", "description": "Retrieve prematch odds for a specific event by event ID",

View File

@ -1346,53 +1346,6 @@
} }
} }
}, },
"/prematch/odds/raw/{market_id}": {
"get": {
"description": "Retrieve raw odds records using a Market ID",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"prematch"
],
"summary": "Retrieve raw odds by Market ID",
"parameters": [
{
"type": "string",
"description": "Market ID",
"name": "market_id",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/domain.RawOddsByMarketID"
}
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
}
}
}
},
"/prematch/odds/upcoming/{upcoming_id}": { "/prematch/odds/upcoming/{upcoming_id}": {
"get": { "get": {
"description": "Retrieve prematch odds by upcoming event ID (FI from Bet365) with optional pagination", "description": "Retrieve prematch odds by upcoming event ID (FI from Bet365) with optional pagination",
@ -1452,6 +1405,60 @@
} }
} }
}, },
"/prematch/odds/upcoming/{upcoming_id}/market/{market_id}": {
"get": {
"description": "Retrieve raw odds records using a Market ID",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"prematch"
],
"summary": "Retrieve raw odds by Market ID",
"parameters": [
{
"type": "string",
"description": "Upcoming ID",
"name": "upcoming_id",
"in": "path",
"required": true
},
{
"type": "string",
"description": "Market ID",
"name": "market_id",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/domain.RawOddsByMarketID"
}
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
}
}
}
},
"/prematch/odds/{event_id}": { "/prematch/odds/{event_id}": {
"get": { "get": {
"description": "Retrieve prematch odds for a specific event by event ID", "description": "Retrieve prematch odds for a specific event by event ID",

View File

@ -1704,37 +1704,6 @@ paths:
summary: Retrieve prematch odds for an event summary: Retrieve prematch odds for an event
tags: tags:
- prematch - prematch
/prematch/odds/raw/{market_id}:
get:
consumes:
- application/json
description: Retrieve raw odds records using a Market ID
parameters:
- description: Market ID
in: path
name: market_id
required: true
type: string
produces:
- application/json
responses:
"200":
description: OK
schema:
items:
$ref: '#/definitions/domain.RawOddsByMarketID'
type: array
"400":
description: Bad Request
schema:
$ref: '#/definitions/response.APIResponse'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/response.APIResponse'
summary: Retrieve raw odds by Market ID
tags:
- prematch
/prematch/odds/upcoming/{upcoming_id}: /prematch/odds/upcoming/{upcoming_id}:
get: get:
consumes: consumes:
@ -1775,6 +1744,42 @@ paths:
summary: Retrieve prematch odds by upcoming ID (FI) summary: Retrieve prematch odds by upcoming ID (FI)
tags: tags:
- prematch - prematch
/prematch/odds/upcoming/{upcoming_id}/market/{market_id}:
get:
consumes:
- application/json
description: Retrieve raw odds records using a Market ID
parameters:
- description: Upcoming ID
in: path
name: upcoming_id
required: true
type: string
- description: Market ID
in: path
name: market_id
required: true
type: string
produces:
- application/json
responses:
"200":
description: OK
schema:
items:
$ref: '#/definitions/domain.RawOddsByMarketID'
type: array
"400":
description: Bad Request
schema:
$ref: '#/definitions/response.APIResponse'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/response.APIResponse'
summary: Retrieve raw odds by Market ID
tags:
- prematch
/search/branch: /search/branch:
get: get:
consumes: consumes:

View File

@ -148,7 +148,6 @@ type Odd struct {
MarketCategory pgtype.Text `json:"market_category"` MarketCategory pgtype.Text `json:"market_category"`
MarketID pgtype.Text `json:"market_id"` MarketID pgtype.Text `json:"market_id"`
Name pgtype.Text `json:"name"` Name pgtype.Text `json:"name"`
Header pgtype.Text `json:"header"`
Handicap pgtype.Text `json:"handicap"` Handicap pgtype.Text `json:"handicap"`
OddsValue pgtype.Float8 `json:"odds_value"` OddsValue pgtype.Float8 `json:"odds_value"`
Section string `json:"section"` Section string `json:"section"`

View File

@ -253,15 +253,17 @@ SELECT id,
fetched_at fetched_at
FROM odds FROM odds
WHERE market_id = $1 WHERE market_id = $1
AND fi = $2
AND is_active = true AND is_active = true
AND source = 'b365api' AND source = 'b365api'
LIMIT $2 OFFSET $3 LIMIT $3 OFFSET $4
` `
type GetRawOddsByMarketIDParams struct { type GetRawOddsByMarketIDParams struct {
MarketID pgtype.Text MarketID pgtype.Text `json:"market_id"`
Limit int32 Fi pgtype.Text `json:"fi"`
Offset int32 Limit int32 `json:"limit"`
Offset int32 `json:"offset"`
} }
type GetRawOddsByMarketIDRow struct { type GetRawOddsByMarketIDRow struct {
@ -271,7 +273,12 @@ type GetRawOddsByMarketIDRow struct {
} }
func (q *Queries) GetRawOddsByMarketID(ctx context.Context, arg GetRawOddsByMarketIDParams) ([]GetRawOddsByMarketIDRow, error) { func (q *Queries) GetRawOddsByMarketID(ctx context.Context, arg GetRawOddsByMarketIDParams) ([]GetRawOddsByMarketIDRow, error) {
rows, err := q.db.Query(ctx, GetRawOddsByMarketID, arg.MarketID, arg.Limit, arg.Offset) rows, err := q.db.Query(ctx, GetRawOddsByMarketID,
arg.MarketID,
arg.Fi,
arg.Limit,
arg.Offset,
)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -324,37 +331,15 @@ VALUES (
$13, $13,
$14, $14,
$15 $15
) ON CONFLICT (market_id, name, handicap) DO ) ON CONFLICT (event_id, market_id) DO
UPDATE UPDATE
SET odds_value = EXCLUDED.odds_value, SET odds_value = EXCLUDED.odds_value,
raw_odds = EXCLUDED.raw_odds, raw_odds = EXCLUDED.raw_odds,
market_type = EXCLUDED.market_type, market_type = EXCLUDED.market_type,
market_name = EXCLUDED.market_name, market_name = EXCLUDED.market_name,
event_id,
fi,
market_type,
market_name,
market_category,
market_id,
name,
handicap,
odds_value,
section,
category,
raw_odds,
is_active,
source,
fetched_at
) VALUES (
$1, $2, $3, $4, $5, $6, $7,
$8, $9, $10, $11, $12, $13, $14, $15
)
ON CONFLICT (event_id, market_id) DO UPDATE SET
odds_value = EXCLUDED.odds_value,
raw_odds = EXCLUDED.raw_odds,
market_type = EXCLUDED.market_type,
market_name = EXCLUDED.market_name,
market_category = EXCLUDED.market_category, market_category = EXCLUDED.market_category,
name = EXCLUDED.name,
handicap = EXCLUDED.handicap,
fetched_at = EXCLUDED.fetched_at, fetched_at = EXCLUDED.fetched_at,
is_active = EXCLUDED.is_active, is_active = EXCLUDED.is_active,
source = EXCLUDED.source, source = EXCLUDED.source,

View File

@ -14,242 +14,243 @@ import (
) )
func (s *Store) SaveNonLiveMarket(ctx context.Context, m domain.Market) error { func (s *Store) SaveNonLiveMarket(ctx context.Context, m domain.Market) error {
if len(m.Odds) == 0 { if len(m.Odds) == 0 {
return nil return nil
} }
for _, raw := range m.Odds { for _, raw := range m.Odds {
var item map[string]interface{} var item map[string]interface{}
if err := json.Unmarshal(raw, &item); err != nil { if err := json.Unmarshal(raw, &item); err != nil {
continue continue
} }
name := getString(item["name"]) name := getString(item["name"])
handicap := getString(item["handicap"]) handicap := getString(item["handicap"])
oddsVal := getFloat(item["odds"]) oddsVal := getFloat(item["odds"])
rawOddsBytes, _ := json.Marshal(m.Odds) rawOddsBytes, _ := json.Marshal(m.Odds)
params := dbgen.InsertNonLiveOddParams{ params := dbgen.InsertNonLiveOddParams{
EventID: pgtype.Text{String: m.EventID, Valid: m.EventID != ""}, EventID: pgtype.Text{String: m.EventID, Valid: m.EventID != ""},
Fi: pgtype.Text{String: m.FI, Valid: m.FI != ""}, Fi: pgtype.Text{String: m.FI, Valid: m.FI != ""},
MarketType: m.MarketType, MarketType: m.MarketType,
MarketName: pgtype.Text{String: m.MarketName, Valid: m.MarketName != ""}, MarketName: pgtype.Text{String: m.MarketName, Valid: m.MarketName != ""},
MarketCategory: pgtype.Text{String: m.MarketCategory, Valid: m.MarketCategory != ""}, MarketCategory: pgtype.Text{String: m.MarketCategory, Valid: m.MarketCategory != ""},
MarketID: pgtype.Text{String: m.MarketID, Valid: m.MarketID != ""}, MarketID: pgtype.Text{String: m.MarketID, Valid: m.MarketID != ""},
Name: pgtype.Text{String: name, Valid: name != ""}, Name: pgtype.Text{String: name, Valid: name != ""},
Handicap: pgtype.Text{String: handicap, Valid: handicap != ""}, Handicap: pgtype.Text{String: handicap, Valid: handicap != ""},
OddsValue: pgtype.Float8{Float64: oddsVal, Valid: oddsVal != 0}, OddsValue: pgtype.Float8{Float64: oddsVal, Valid: oddsVal != 0},
Section: m.MarketCategory, Section: m.MarketCategory,
Category: pgtype.Text{Valid: false}, Category: pgtype.Text{Valid: false},
RawOdds: rawOddsBytes, RawOdds: rawOddsBytes,
IsActive: pgtype.Bool{Bool: true, Valid: true}, IsActive: pgtype.Bool{Bool: true, Valid: true},
Source: pgtype.Text{String: "b365api", Valid: true}, Source: pgtype.Text{String: "b365api", Valid: true},
FetchedAt: pgtype.Timestamp{Time: time.Now(), Valid: true}, FetchedAt: pgtype.Timestamp{Time: time.Now(), Valid: true},
} }
err := s.queries.InsertNonLiveOdd(ctx, params) err := s.queries.InsertNonLiveOdd(ctx, params)
if err != nil { if err != nil {
_ = writeFailedMarketLog(m, err) _ = writeFailedMarketLog(m, err)
continue continue
} }
} }
return nil return nil
} }
func writeFailedMarketLog(m domain.Market, err error) error { func writeFailedMarketLog(m domain.Market, err error) error {
logDir := "logs" logDir := "logs"
logFile := logDir + "/failed_markets.log" logFile := logDir + "/failed_markets.log"
if mkErr := os.MkdirAll(logDir, 0755); mkErr != nil { if mkErr := os.MkdirAll(logDir, 0755); mkErr != nil {
return mkErr return mkErr
} }
f, fileErr := os.OpenFile(logFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) f, fileErr := os.OpenFile(logFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if fileErr != nil { if fileErr != nil {
return fileErr return fileErr
} }
defer f.Close() defer f.Close()
entry := struct { entry := struct {
Time string `json:"time"` Time string `json:"time"`
Error string `json:"error"` Error string `json:"error"`
Record domain.Market `json:"record"` Record domain.Market `json:"record"`
}{ }{
Time: time.Now().Format(time.RFC3339), Time: time.Now().Format(time.RFC3339),
Error: err.Error(), Error: err.Error(),
Record: m, Record: m,
} }
jsonData, _ := json.MarshalIndent(entry, "", " ") jsonData, _ := json.MarshalIndent(entry, "", " ")
_, writeErr := f.WriteString(string(jsonData) + "\n\n") _, writeErr := f.WriteString(string(jsonData) + "\n\n")
return writeErr return writeErr
} }
func getString(v interface{}) string { func getString(v interface{}) string {
if s, ok := v.(string); ok { if s, ok := v.(string); ok {
return s return s
} }
return "" return ""
} }
func getFloat(v interface{}) float64 { func getFloat(v interface{}) float64 {
if s, ok := v.(string); ok { if s, ok := v.(string); ok {
f, err := strconv.ParseFloat(s, 64) f, err := strconv.ParseFloat(s, 64)
if err == nil { if err == nil {
return f return f
} }
} }
return 0 return 0
} }
func (s *Store) GetPrematchOdds(ctx context.Context, eventID string) ([]domain.Odd, error) { func (s *Store) GetPrematchOdds(ctx context.Context, eventID string) ([]domain.Odd, error) {
odds, err := s.queries.GetPrematchOdds(ctx) odds, err := s.queries.GetPrematchOdds(ctx)
if err != nil { if err != nil {
return nil, err return nil, err
} }
domainOdds := make([]domain.Odd, len(odds)) domainOdds := make([]domain.Odd, len(odds))
for i, odd := range odds { for i, odd := range odds {
domainOdds[i] = domain.Odd{ domainOdds[i] = domain.Odd{
EventID: odd.EventID.String, EventID: odd.EventID.String,
Fi: odd.Fi.String, Fi: odd.Fi.String,
MarketType: odd.MarketType, MarketType: odd.MarketType,
MarketName: odd.MarketName.String, MarketName: odd.MarketName.String,
MarketCategory: odd.MarketCategory.String, MarketCategory: odd.MarketCategory.String,
MarketID: odd.MarketID.String, MarketID: odd.MarketID.String,
Name: odd.Name.String, Name: odd.Name.String,
Handicap: odd.Handicap.String, Handicap: odd.Handicap.String,
OddsValue: odd.OddsValue.Float64, OddsValue: odd.OddsValue.Float64,
Section: odd.Section, Section: odd.Section,
Category: odd.Category.String, Category: odd.Category.String,
RawOdds: func() []domain.RawMessage { RawOdds: func() []domain.RawMessage {
var rawOdds []domain.RawMessage var rawOdds []domain.RawMessage
if err := json.Unmarshal(odd.RawOdds, &rawOdds); err != nil { if err := json.Unmarshal(odd.RawOdds, &rawOdds); err != nil {
rawOdds = nil rawOdds = nil
} }
return rawOdds return rawOdds
}(), }(),
FetchedAt: odd.FetchedAt.Time, FetchedAt: odd.FetchedAt.Time,
Source: odd.Source.String, Source: odd.Source.String,
IsActive: odd.IsActive.Bool, IsActive: odd.IsActive.Bool,
} }
} }
return domainOdds, nil return domainOdds, nil
} }
func (s *Store) GetALLPrematchOdds(ctx context.Context) ([]domain.Odd, error) { func (s *Store) GetALLPrematchOdds(ctx context.Context) ([]domain.Odd, error) {
rows, err := s.queries.GetALLPrematchOdds(ctx) rows, err := s.queries.GetALLPrematchOdds(ctx)
if err != nil { if err != nil {
return nil, err return nil, err
} }
domainOdds := make([]domain.Odd, len(rows)) domainOdds := make([]domain.Odd, len(rows))
for i, row := range rows { for i, row := range rows {
domainOdds[i] = domain.Odd{ domainOdds[i] = domain.Odd{
// ID: int64(row.ID), // ID: int64(row.ID),
EventID: row.EventID.String, EventID: row.EventID.String,
Fi: row.Fi.String, Fi: row.Fi.String,
MarketType: row.MarketType, MarketType: row.MarketType,
MarketName: row.MarketName.String, MarketName: row.MarketName.String,
MarketCategory: row.MarketCategory.String, MarketCategory: row.MarketCategory.String,
MarketID: row.MarketID.String, MarketID: row.MarketID.String,
Name: row.Name.String, Name: row.Name.String,
Handicap: row.Handicap.String, Handicap: row.Handicap.String,
OddsValue: row.OddsValue.Float64, OddsValue: row.OddsValue.Float64,
Section: row.Section, Section: row.Section,
Category: row.Category.String, Category: row.Category.String,
RawOdds: func() []domain.RawMessage { RawOdds: func() []domain.RawMessage {
var rawOdds []domain.RawMessage var rawOdds []domain.RawMessage
if err := json.Unmarshal(row.RawOdds, &rawOdds); err != nil { if err := json.Unmarshal(row.RawOdds, &rawOdds); err != nil {
rawOdds = nil rawOdds = nil
} }
return rawOdds return rawOdds
}(), }(),
FetchedAt: row.FetchedAt.Time, FetchedAt: row.FetchedAt.Time,
Source: row.Source.String, Source: row.Source.String,
IsActive: row.IsActive.Bool, IsActive: row.IsActive.Bool,
} }
} }
return domainOdds, nil return domainOdds, nil
} }
func (s *Store) GetRawOddsByMarketID(ctx context.Context, rawOddsID string) (domain.RawOddsByMarketID, error) { func (s *Store) GetRawOddsByMarketID(ctx context.Context, rawOddsID string, upcomingID string) (domain.RawOddsByMarketID, error) {
params := dbgen.GetRawOddsByMarketIDParams{ params := dbgen.GetRawOddsByMarketIDParams{
MarketID: pgtype.Text{String: rawOddsID, Valid: true}, MarketID: pgtype.Text{String: rawOddsID, Valid: true},
Limit: 1, Fi: pgtype.Text{String: upcomingID, Valid: true},
Offset: 0, Limit: 1,
} Offset: 0,
}
rows, err := s.queries.GetRawOddsByMarketID(ctx, params) rows, err := s.queries.GetRawOddsByMarketID(ctx, params)
if err != nil { if err != nil {
return domain.RawOddsByMarketID{}, err return domain.RawOddsByMarketID{}, err
} }
if len(rows) == 0 { if len(rows) == 0 {
return domain.RawOddsByMarketID{}, fmt.Errorf("no raw odds found for market_id: %s", rawOddsID) return domain.RawOddsByMarketID{}, fmt.Errorf("no raw odds found for market_id: %s", rawOddsID)
} }
row := rows[0] row := rows[0]
var rawOdds []json.RawMessage var rawOdds []json.RawMessage
if err := json.Unmarshal(row.RawOdds, &rawOdds); err != nil { if err := json.Unmarshal(row.RawOdds, &rawOdds); err != nil {
return domain.RawOddsByMarketID{}, err return domain.RawOddsByMarketID{}, err
} }
return domain.RawOddsByMarketID{ return domain.RawOddsByMarketID{
ID: int64(row.ID), ID: int64(row.ID),
RawOdds: func() []domain.RawMessage { RawOdds: func() []domain.RawMessage {
converted := make([]domain.RawMessage, len(rawOdds)) converted := make([]domain.RawMessage, len(rawOdds))
for i, r := range rawOdds { for i, r := range rawOdds {
converted[i] = domain.RawMessage(r) converted[i] = domain.RawMessage(r)
} }
return converted return converted
}(), }(),
FetchedAt: row.FetchedAt.Time, FetchedAt: row.FetchedAt.Time,
}, nil }, nil
} }
func (s *Store) GetPrematchOddsByUpcomingID(ctx context.Context, upcomingID string, limit, offset int32) ([]domain.Odd, error) { func (s *Store) GetPrematchOddsByUpcomingID(ctx context.Context, upcomingID string, limit, offset int32) ([]domain.Odd, error) {
params := dbgen.GetPrematchOddsByUpcomingIDParams{ params := dbgen.GetPrematchOddsByUpcomingIDParams{
ID: upcomingID, ID: upcomingID,
Limit: limit, Limit: limit,
Offset: offset, Offset: offset,
} }
odds, err := s.queries.GetPrematchOddsByUpcomingID(ctx, params) odds, err := s.queries.GetPrematchOddsByUpcomingID(ctx, params)
if err != nil { if err != nil {
return nil, err return nil, err
} }
// Map the results to domain.Odd // Map the results to domain.Odd
domainOdds := make([]domain.Odd, len(odds)) domainOdds := make([]domain.Odd, len(odds))
for i, odd := range odds { for i, odd := range odds {
var rawOdds []domain.RawMessage var rawOdds []domain.RawMessage
if err := json.Unmarshal(odd.RawOdds, &rawOdds); err != nil { if err := json.Unmarshal(odd.RawOdds, &rawOdds); err != nil {
rawOdds = nil rawOdds = nil
} }
domainOdds[i] = domain.Odd{ domainOdds[i] = domain.Odd{
EventID: odd.EventID.String, EventID: odd.EventID.String,
Fi: odd.Fi.String, Fi: odd.Fi.String,
MarketType: odd.MarketType, MarketType: odd.MarketType,
MarketName: odd.MarketName.String, MarketName: odd.MarketName.String,
MarketCategory: odd.MarketCategory.String, MarketCategory: odd.MarketCategory.String,
MarketID: odd.MarketID.String, MarketID: odd.MarketID.String,
Name: odd.Name.String, Name: odd.Name.String,
Handicap: odd.Handicap.String, Handicap: odd.Handicap.String,
OddsValue: odd.OddsValue.Float64, OddsValue: odd.OddsValue.Float64,
Section: odd.Section, Section: odd.Section,
Category: odd.Category.String, Category: odd.Category.String,
RawOdds: rawOdds, RawOdds: rawOdds,
FetchedAt: odd.FetchedAt.Time, FetchedAt: odd.FetchedAt.Time,
Source: odd.Source.String, Source: odd.Source.String,
IsActive: odd.IsActive.Bool, IsActive: odd.IsActive.Bool,
} }
} }
return domainOdds, nil return domainOdds, nil
} }

View File

@ -10,7 +10,5 @@ type Service interface {
FetchNonLiveOdds(ctx context.Context) error FetchNonLiveOdds(ctx context.Context) error
GetPrematchOdds(ctx context.Context, eventID string) ([]domain.Odd, error) GetPrematchOdds(ctx context.Context, eventID string) ([]domain.Odd, error)
GetALLPrematchOdds(ctx context.Context) ([]domain.Odd, error) GetALLPrematchOdds(ctx context.Context) ([]domain.Odd, error)
GetRawOddsByMarketID(ctx context.Context, marketID string) ([]domain.RawOddsByMarketID, error) GetRawOddsByMarketID(ctx context.Context, marketID string, upcomingID string) ([]domain.RawOddsByMarketID, error)
} }

View File

@ -22,7 +22,6 @@ func New(token string, store *repository.Store) *ServiceImpl {
return &ServiceImpl{token: token, store: store} return &ServiceImpl{token: token, store: store}
} }
func (s *ServiceImpl) FetchNonLiveOdds(ctx context.Context) error { func (s *ServiceImpl) FetchNonLiveOdds(ctx context.Context) error {
eventIDs, err := s.store.GetAllUpcomingEvents(ctx) eventIDs, err := s.store.GetAllUpcomingEvents(ctx)
if err != nil { if err != nil {
@ -30,43 +29,43 @@ func (s *ServiceImpl) FetchNonLiveOdds(ctx context.Context) error {
return err return err
} }
for _, event := range eventIDs { for _, event := range eventIDs {
eventID := event.ID eventID := event.ID
prematchURL := "https://api.b365api.com/v3/bet365/prematch?token=" + s.token + "&FI=" + eventID prematchURL := "https://api.b365api.com/v3/bet365/prematch?token=" + s.token + "&FI=" + eventID
log.Printf("📡 Fetching prematch odds for event ID: %s", eventID) log.Printf("📡 Fetching prematch odds for event ID: %s", eventID)
resp, err := http.Get(prematchURL) resp, err := http.Get(prematchURL)
if err != nil { if err != nil {
log.Printf("❌ Failed to fetch prematch odds for event %s: %v", eventID, err) log.Printf("❌ Failed to fetch prematch odds for event %s: %v", eventID, err)
continue continue
} }
defer resp.Body.Close() defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body) body, _ := io.ReadAll(resp.Body)
var oddsData struct { var oddsData struct {
Success int `json:"success"` Success int `json:"success"`
Results []struct { Results []struct {
EventID string `json:"event_id"` EventID string `json:"event_id"`
FI string `json:"FI"` FI string `json:"FI"`
Main OddsSection `json:"main"` Main OddsSection `json:"main"`
} `json:"results"` } `json:"results"`
} }
if err := json.Unmarshal(body, &oddsData); err != nil || oddsData.Success != 1 || len(oddsData.Results) == 0 { if err := json.Unmarshal(body, &oddsData); err != nil || oddsData.Success != 1 || len(oddsData.Results) == 0 {
log.Printf("❌ Invalid prematch data for event %s", eventID) log.Printf("❌ Invalid prematch data for event %s", eventID)
continue continue
} }
result := oddsData.Results[0] result := oddsData.Results[0]
finalID := result.EventID finalID := result.EventID
if finalID == "" { if finalID == "" {
finalID = result.FI finalID = result.FI
} }
if finalID == "" { if finalID == "" {
log.Printf("⚠️ Skipping event %s with no valid ID", eventID) log.Printf("⚠️ Skipping event %s with no valid ID", eventID)
continue continue
} }
s.storeSection(ctx, finalID, result.FI, "main", result.Main) s.storeSection(ctx, finalID, result.FI, "main", result.Main)
} }
return nil return nil
} }
@ -108,12 +107,10 @@ type OddsMarket struct {
} }
type OddsSection struct { type OddsSection struct {
UpdatedAt string `json:"updated_at"` UpdatedAt string `json:"updated_at"`
Sp map[string]OddsMarket `json:"sp"` Sp map[string]OddsMarket `json:"sp"`
} }
func (s *ServiceImpl) GetPrematchOdds(ctx context.Context, eventID string) ([]domain.Odd, error) { func (s *ServiceImpl) GetPrematchOdds(ctx context.Context, eventID string) ([]domain.Odd, error) {
return s.store.GetPrematchOdds(ctx, eventID) return s.store.GetPrematchOdds(ctx, eventID)
} }
@ -122,8 +119,8 @@ func (s *ServiceImpl) GetALLPrematchOdds(ctx context.Context) ([]domain.Odd, err
return s.store.GetALLPrematchOdds(ctx) return s.store.GetALLPrematchOdds(ctx)
} }
func (s *ServiceImpl) GetRawOddsByMarketID(ctx context.Context, marketID string) ([]domain.RawOddsByMarketID, error) { func (s *ServiceImpl) GetRawOddsByMarketID(ctx context.Context, marketID string, upcomingID string) ([]domain.RawOddsByMarketID, error) {
rows, err := s.store.GetRawOddsByMarketID(ctx, marketID) rows, err := s.store.GetRawOddsByMarketID(ctx, marketID, upcomingID)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -132,5 +129,5 @@ func (s *ServiceImpl) GetRawOddsByMarketID(ctx context.Context, marketID string)
} }
func (s *ServiceImpl) GetPrematchOddsByUpcomingID(ctx context.Context, upcomingID string, limit, offset int32) ([]domain.Odd, error) { func (s *ServiceImpl) GetPrematchOddsByUpcomingID(ctx context.Context, upcomingID string, limit, offset int32) ([]domain.Odd, error) {
return s.store.GetPrematchOddsByUpcomingID(ctx, upcomingID, limit, offset) return s.store.GetPrematchOddsByUpcomingID(ctx, upcomingID, limit, offset)
} }

View File

@ -1,61 +1,58 @@
package httpserver package httpserver
import ( import (
// "context" // "context"
"log"
eventsvc "github.com/SamuelTariku/FortuneBet-Backend/internal/services/event" "log"
oddssvc "github.com/SamuelTariku/FortuneBet-Backend/internal/services/odds"
"github.com/robfig/cron/v3" eventsvc "github.com/SamuelTariku/FortuneBet-Backend/internal/services/event"
oddssvc "github.com/SamuelTariku/FortuneBet-Backend/internal/services/odds"
"github.com/robfig/cron/v3"
) )
func StartDataFetchingCrons(eventService eventsvc.Service, oddsService oddssvc.Service) { func StartDataFetchingCrons(eventService eventsvc.Service, oddsService oddssvc.Service) {
c := cron.New(cron.WithSeconds()) c := cron.New(cron.WithSeconds())
schedule := []struct { schedule := []struct {
spec string spec string
task func() task func()
}{ }{
// { // {
// spec: "*/30 * * * * *", // Every 30 seconds // spec: "*/30 * * * * *", // Every 30 seconds
// task: func() { // task: func() {
// if err := eventService.FetchUpcomingEvents(context.Background()); err != nil { // if err := eventService.FetchUpcomingEvents(context.Background()); err != nil {
// log.Printf("FetchUpcomingEvents error: %v", err) // log.Printf("FetchUpcomingEvents error: %v", err)
// } // }
// }, // },
// }, // },
// {
// spec: "*/5 * * * * *", // Every 5 seconds
// task: func() {
// if err := eventService.FetchLiveEvents(context.Background()); err != nil {
// log.Printf("FetchLiveEvents error: %v", err)
// }
// },
// },
// {
// spec: "*/5 * * * * *", // Every 5 seconds
// task: func() {
// if err := eventService.FetchLiveEvents(context.Background()); err != nil {
// log.Printf("FetchLiveEvents error: %v", err)
// }
// },
// },
// { // {
// spec: "*/5 * * * * *", // Every 5 seconds // spec: "*/5 * * * * *", // Every 5 seconds
// task: func() { // task: func() {
// if err := oddsService.FetchNonLiveOdds(context.Background()); err != nil { // if err := oddsService.FetchNonLiveOdds(context.Background()); err != nil {
// log.Printf("FetchNonLiveOdds error: %v", err) // log.Printf("FetchNonLiveOdds error: %v", err)
// } // }
// }, // },
// }, // },
}
}
for _, job := range schedule {
for _, job := range schedule { job.task()
if _, err := c.AddFunc(job.spec, job.task); err != nil { if _, err := c.AddFunc(job.spec, job.task); err != nil {
log.Fatalf("Failed to schedule cron job: %v", err) log.Fatalf("Failed to schedule cron job: %v", err)
} }
} }
c.Start() c.Start()
log.Println("Cron jobs started for event and odds services") log.Println("Cron jobs started for event and odds services")
} }

View File

@ -22,21 +22,22 @@ import (
// @Failure 500 {object} response.APIResponse // @Failure 500 {object} response.APIResponse
// @Router /prematch/odds/{event_id} [get] // @Router /prematch/odds/{event_id} [get]
func GetPrematchOdds(logger *slog.Logger, prematchSvc *odds.ServiceImpl) fiber.Handler { func GetPrematchOdds(logger *slog.Logger, prematchSvc *odds.ServiceImpl) fiber.Handler {
return func(c *fiber.Ctx) error { return func(c *fiber.Ctx) error {
eventID := c.Params("event_id") eventID := c.Params("event_id")
if eventID == "" { if eventID == "" {
return response.WriteJSON(c, fiber.StatusBadRequest, "Missing event_id", nil, nil) return response.WriteJSON(c, fiber.StatusBadRequest, "Missing event_id", nil, nil)
} }
odds, err := prematchSvc.GetPrematchOdds(c.Context(), eventID) odds, err := prematchSvc.GetPrematchOdds(c.Context(), eventID)
if err != nil { if err != nil {
return response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to retrieve odds", nil, nil) return response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to retrieve odds", nil, nil)
} }
return response.WriteJSON(c, fiber.StatusOK, "Prematch odds retrieved successfully", odds, nil) return response.WriteJSON(c, fiber.StatusOK, "Prematch odds retrieved successfully", odds, nil)
} }
} }
//GetALLPrematchOdds
// GetALLPrematchOdds
// @Summary Retrieve all prematch odds // @Summary Retrieve all prematch odds
// @Description Retrieve all prematch odds from the database // @Description Retrieve all prematch odds from the database
// @Tags prematch // @Tags prematch
@ -46,41 +47,48 @@ func GetPrematchOdds(logger *slog.Logger, prematchSvc *odds.ServiceImpl) fiber.H
// @Failure 500 {object} response.APIResponse // @Failure 500 {object} response.APIResponse
// @Router /prematch/odds [get] // @Router /prematch/odds [get]
func GetALLPrematchOdds(logger *slog.Logger, prematchSvc *odds.ServiceImpl) fiber.Handler { func GetALLPrematchOdds(logger *slog.Logger, prematchSvc *odds.ServiceImpl) fiber.Handler {
return func(c *fiber.Ctx) error { return func(c *fiber.Ctx) error {
odds, err := prematchSvc.GetALLPrematchOdds(c.Context()) odds, err := prematchSvc.GetALLPrematchOdds(c.Context())
if err != nil { if err != nil {
return response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to retrieve all prematch odds", nil, nil) return response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to retrieve all prematch odds", nil, nil)
} }
return response.WriteJSON(c, fiber.StatusOK, "All prematch odds retrieved successfully", odds, nil) return response.WriteJSON(c, fiber.StatusOK, "All prematch odds retrieved successfully", odds, nil)
} }
} }
// GetRawOddsByMarketID // GetRawOddsByMarketID
// @Summary Retrieve raw odds by Market ID // @Summary Retrieve raw odds by Market ID
// @Description Retrieve raw odds records using a Market ID // @Description Retrieve raw odds records using a Market ID
// @Tags prematch // @Tags prematch
// @Accept json // @Accept json
// @Produce json // @Produce json
// @Param upcoming_id path string true "Upcoming ID"
// @Param market_id path string true "Market ID" // @Param market_id path string true "Market ID"
// @Success 200 {array} domain.RawOddsByMarketID // @Success 200 {array} domain.RawOddsByMarketID
// @Failure 400 {object} response.APIResponse // @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse // @Failure 500 {object} response.APIResponse
// @Router /prematch/odds/raw/{market_id} [get] // @Router /prematch/odds/upcoming/{upcoming_id}/market/{market_id} [get]
func GetRawOddsByMarketID(logger *slog.Logger, prematchSvc *odds.ServiceImpl) fiber.Handler { func GetRawOddsByMarketID(logger *slog.Logger, prematchSvc *odds.ServiceImpl) fiber.Handler {
return func(c *fiber.Ctx) error { return func(c *fiber.Ctx) error {
marketID := c.Params("market_id") marketID := c.Params("market_id")
if marketID == "" { upcomingID := c.Params("upcoming_id")
return response.WriteJSON(c, fiber.StatusBadRequest, "Missing market_id", nil, nil) if marketID == "" {
} return response.WriteJSON(c, fiber.StatusBadRequest, "Missing market_id", nil, nil)
}
rawOdds, err := prematchSvc.GetRawOddsByMarketID(c.Context(), marketID) if upcomingID == "" {
if err != nil { return response.WriteJSON(c, fiber.StatusBadRequest, "Missing upcoming_id", nil, nil)
logger.Error("failed to fetch raw odds", "error", err) }
return response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to retrieve raw odds", nil, nil)
}
return response.WriteJSON(c, fiber.StatusOK, "Raw odds retrieved successfully", rawOdds, nil) rawOdds, err := prematchSvc.GetRawOddsByMarketID(c.Context(), marketID, upcomingID)
} if err != nil {
logger.Error("failed to fetch raw odds", "error", err)
return response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to retrieve raw odds", err, nil)
}
return response.WriteJSON(c, fiber.StatusOK, "Raw odds retrieved successfully", rawOdds, nil)
}
} }
// @Summary Retrieve all upcoming events // @Summary Retrieve all upcoming events
@ -92,15 +100,16 @@ func GetRawOddsByMarketID(logger *slog.Logger, prematchSvc *odds.ServiceImpl) fi
// @Failure 500 {object} response.APIResponse // @Failure 500 {object} response.APIResponse
// @Router /prematch/events [get] // @Router /prematch/events [get]
func GetAllUpcomingEvents(logger *slog.Logger, eventSvc event.Service) fiber.Handler { func GetAllUpcomingEvents(logger *slog.Logger, eventSvc event.Service) fiber.Handler {
return func(c *fiber.Ctx) error { return func(c *fiber.Ctx) error {
events, err := eventSvc.GetAllUpcomingEvents(c.Context()) events, err := eventSvc.GetAllUpcomingEvents(c.Context())
if err != nil { if err != nil {
return response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to retrieve all upcoming events", nil, nil) return response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to retrieve all upcoming events", nil, nil)
} }
return response.WriteJSON(c, fiber.StatusOK, "All upcoming events retrieved successfully", events, nil) return response.WriteJSON(c, fiber.StatusOK, "All upcoming events retrieved successfully", events, nil)
} }
} }
// @Summary Retrieve an upcoming by ID // @Summary Retrieve an upcoming by ID
// @Description Retrieve an upcoming event by ID // @Description Retrieve an upcoming event by ID
// @Tags prematch // @Tags prematch
@ -112,20 +121,21 @@ func GetAllUpcomingEvents(logger *slog.Logger, eventSvc event.Service) fiber.Han
// @Failure 500 {object} response.APIResponse // @Failure 500 {object} response.APIResponse
// @Router /prematch/events/{id} [get] // @Router /prematch/events/{id} [get]
func GetUpcomingEventByID(logger *slog.Logger, eventSvc event.Service) fiber.Handler { func GetUpcomingEventByID(logger *slog.Logger, eventSvc event.Service) fiber.Handler {
return func(c *fiber.Ctx) error { return func(c *fiber.Ctx) error {
id := c.Params("id") id := c.Params("id")
if id == "" { if id == "" {
return response.WriteJSON(c, fiber.StatusBadRequest, "Missing id", nil, nil) return response.WriteJSON(c, fiber.StatusBadRequest, "Missing id", nil, nil)
} }
event, err := eventSvc.GetUpcomingEventByID(c.Context(), id) event, err := eventSvc.GetUpcomingEventByID(c.Context(), id)
if err != nil { if err != nil {
return response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to retrieve upcoming event", nil, nil) return response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to retrieve upcoming event", nil, nil)
} }
return response.WriteJSON(c, fiber.StatusOK, "Upcoming event retrieved successfully", event, nil) return response.WriteJSON(c, fiber.StatusOK, "Upcoming event retrieved successfully", event, nil)
} }
} }
// @Summary Retrieve prematch odds by upcoming ID (FI) // @Summary Retrieve prematch odds by upcoming ID (FI)
// @Description Retrieve prematch odds by upcoming event ID (FI from Bet365) with optional pagination // @Description Retrieve prematch odds by upcoming event ID (FI from Bet365) with optional pagination
// @Tags prematch // @Tags prematch
@ -139,28 +149,27 @@ func GetUpcomingEventByID(logger *slog.Logger, eventSvc event.Service) fiber.Han
// @Failure 500 {object} response.APIResponse // @Failure 500 {object} response.APIResponse
// @Router /prematch/odds/upcoming/{upcoming_id} [get] // @Router /prematch/odds/upcoming/{upcoming_id} [get]
func GetPrematchOddsByUpcomingID(logger *slog.Logger, prematchSvc *odds.ServiceImpl) fiber.Handler { func GetPrematchOddsByUpcomingID(logger *slog.Logger, prematchSvc *odds.ServiceImpl) fiber.Handler {
return func(c *fiber.Ctx) error { return func(c *fiber.Ctx) error {
upcomingID := c.Params("upcoming_id") upcomingID := c.Params("upcoming_id")
if upcomingID == "" { if upcomingID == "" {
return response.WriteJSON(c, fiber.StatusBadRequest, "Missing upcoming_id", nil, nil) return response.WriteJSON(c, fiber.StatusBadRequest, "Missing upcoming_id", nil, nil)
} }
limit, err := strconv.Atoi(c.Query("limit", "10")) // Default limit is 10 limit, err := strconv.Atoi(c.Query("limit", "10")) // Default limit is 10
if err != nil || limit <= 0 { if err != nil || limit <= 0 {
return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid limit value", nil, nil) return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid limit value", nil, nil)
} }
offset, err := strconv.Atoi(c.Query("offset", "0")) // Default offset is 0 offset, err := strconv.Atoi(c.Query("offset", "0")) // Default offset is 0
if err != nil || offset < 0 { if err != nil || offset < 0 {
return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid offset value", nil, nil) return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid offset value", nil, nil)
} }
odds, err := prematchSvc.GetPrematchOddsByUpcomingID(c.Context(), upcomingID, int32(limit), int32(offset)) odds, err := prematchSvc.GetPrematchOddsByUpcomingID(c.Context(), upcomingID, int32(limit), int32(offset))
if err != nil { if err != nil {
return response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to retrieve prematch odds", nil, nil) return response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to retrieve prematch odds", nil, nil)
} }
return response.WriteJSON(c, fiber.StatusOK, "Prematch odds retrieved successfully", odds, nil) return response.WriteJSON(c, fiber.StatusOK, "Prematch odds retrieved successfully", odds, nil)
} }
} }

View File

@ -59,7 +59,7 @@ func (a *App) initAppRoutes() {
a.fiber.Get("/prematch/odds/:event_id", handlers.GetPrematchOdds(a.logger, a.prematchSvc)) a.fiber.Get("/prematch/odds/:event_id", handlers.GetPrematchOdds(a.logger, a.prematchSvc))
a.fiber.Get("/prematch/odds", handlers.GetALLPrematchOdds(a.logger, a.prematchSvc)) a.fiber.Get("/prematch/odds", handlers.GetALLPrematchOdds(a.logger, a.prematchSvc))
a.fiber.Get("/prematch/odds/raw/:market_id", handlers.GetRawOddsByMarketID(a.logger, a.prematchSvc)) a.fiber.Get("/prematch/odds/upcoming/:upcoming_id/market/:market_id", handlers.GetRawOddsByMarketID(a.logger, a.prematchSvc))
a.fiber.Get("/prematch/events/:id", handlers.GetUpcomingEventByID(a.logger, a.eventSvc)) a.fiber.Get("/prematch/events/:id", handlers.GetUpcomingEventByID(a.logger, a.eventSvc))
a.fiber.Get("/prematch/events", handlers.GetAllUpcomingEvents(a.logger, a.eventSvc)) a.fiber.Get("/prematch/events", handlers.GetAllUpcomingEvents(a.logger, a.eventSvc))