Skip to content

Commit 6dfb9d1

Browse files
authored
Merge pull request #2 from mmadariaga/get-f1-race-summary-by-year-api-endpoint
f1 race summary
2 parents 29cbfb0 + c20677a commit 6dfb9d1

31 files changed

+862
-122
lines changed

README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,9 @@ go run cmd/api/main.go
2121

2222
## Features
2323

24-
Currently, the API only has one route:
24+
Currently, the API only has two routes:
2525
- `GET /ping`: Returns a simple "pong" response.
26+
- `GET /races`: Returns application_races.Response json object with a summary of all the races of the year 2024 in f1. This endpoint requires Basic Auth
2627

2728
Example:
2829
```bash
@@ -32,7 +33,7 @@ pong
3233

3334
## TODO
3435

35-
- [ ] Add more routes and functionalities.
36+
- [ ] Use goroutines to fetch data from third party APIs faster
3637
- [ ] Enhance test coverage.
3738

3839
## Licence

bin/run

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
#!/bin/bash
2+
3+
set -e
4+
SCRIPT_DIR=$(dirname $(realpath $0))
5+
6+
pushd ${SCRIPT_DIR}/../
7+
go run cmd/api/main.go
8+
popd

cmd/api/main.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,14 @@ import (
88

99
"fmt"
1010

11-
"github.com/mmadariaga/go-api/internal/router"
11+
infrastructure_router "github.com/mmadariaga/go-api/internal/infrastructure/router"
1212
)
1313

1414
func main() {
1515
zerolog.TimeFieldFormat = zerolog.TimeFormatUnix
1616
log.Print("Starting GO API service")
1717

18-
router := router.RouterFactory()
18+
router := infrastructure_router.RouterFactory()
1919

2020
port := ":8080"
2121
log.Print("listening on localhost" + port)

internal/application/pong/Pong.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package application_pong
2+
3+
func Pong() string {
4+
return "pong"
5+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package application_pong
2+
3+
import (
4+
"testing"
5+
6+
"github.com/stretchr/testify/assert"
7+
)
8+
9+
func TestPong(t *testing.T) {
10+
11+
assert := assert.New(t)
12+
13+
resp := Pong()
14+
15+
assert.Equal(resp, "pong")
16+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package application_races
2+
3+
import (
4+
domain_model "github.com/mmadariaga/go-api/internal/domain/model"
5+
domain_service "github.com/mmadariaga/go-api/internal/domain/service"
6+
)
7+
8+
type Response struct {
9+
domain_model.Race
10+
Podium [3]domain_model.Podium `json:"podium"`
11+
}
12+
13+
type GetRacesByYearDependencies interface {
14+
domain_service.GetPodiumByRaceDependencies
15+
FetchRacesByYear(year int) ([]domain_model.Race, error)
16+
}
17+
18+
func GetRacesByYear(
19+
year int,
20+
dependencies GetRacesByYearDependencies,
21+
) ([]Response, error) {
22+
23+
races, error := dependencies.FetchRacesByYear(year)
24+
if error != nil {
25+
return nil, error
26+
}
27+
28+
podiumsByRace, error := domain_service.GetPodiumByRace(
29+
races,
30+
dependencies,
31+
)
32+
if error != nil {
33+
return nil, error
34+
}
35+
36+
response := make([]Response, 0, len(races))
37+
for _, race := range races {
38+
39+
podium := podiumsByRace[race.Id]
40+
41+
response = append(
42+
response,
43+
Response{
44+
Race: domain_model.Race{
45+
Id: race.Id,
46+
Year: race.Year,
47+
StartDate: race.StartDate,
48+
CountryName: race.CountryName,
49+
CircuitName: race.CircuitName,
50+
},
51+
Podium: podium,
52+
},
53+
)
54+
}
55+
56+
return response, nil
57+
}
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
package application_races
2+
3+
import (
4+
"testing"
5+
6+
"github.com/stretchr/testify/assert"
7+
"github.com/stretchr/testify/mock"
8+
9+
domain_model "github.com/mmadariaga/go-api/internal/domain/model"
10+
test_domain_model "github.com/mmadariaga/go-api/internal/test/domain/model"
11+
)
12+
13+
func TestGetRacesByYear(t *testing.T) {
14+
15+
assert := assert.New(t)
16+
17+
suzuka := test_domain_model.Suzuka()
18+
drivers := test_domain_model.GetAllDriverExamplesAsArray()
19+
podiums := test_domain_model.GetPodiumWithDrivers(
20+
"Max VERSTAPPEN",
21+
"Fernando ALONSO",
22+
"Lewis HAMILTON",
23+
)
24+
25+
mockInfraDependencies := new(MockInfraDependencies)
26+
mockInfraDependencies.On(
27+
"FetchRacesByYear",
28+
2024,
29+
).Return(
30+
suzuka,
31+
nil,
32+
)
33+
mockInfraDependencies.On(
34+
"FetchPodiumByRace",
35+
suzuka[0].Id,
36+
drivers,
37+
).Return(
38+
podiums,
39+
nil,
40+
)
41+
mockInfraDependencies.On(
42+
"FetchDriversByRace",
43+
suzuka[0].Id,
44+
).Return(
45+
drivers,
46+
nil,
47+
)
48+
49+
result, error := GetRacesByYear(
50+
2024,
51+
mockInfraDependencies,
52+
)
53+
54+
assert.Equal(error, nil)
55+
assert.Equal(result[0].Race.CircuitName, "Suzuka")
56+
assert.Equal(result[0].Podium[0].Position, 1)
57+
assert.Equal(result[0].Podium[0].FullName, "Max VERSTAPPEN")
58+
assert.Equal(result[0].Podium[1].FullName, "Fernando ALONSO")
59+
60+
mockInfraDependencies.AssertExpectations(t)
61+
}
62+
63+
type MockInfraDependencies struct {
64+
mock.Mock
65+
}
66+
67+
func (m *MockInfraDependencies) FetchRacesByYear(year int) ([]domain_model.Race, error) {
68+
args := m.Called(year)
69+
return args.Get(0).([]domain_model.Race), args.Error(1)
70+
}
71+
func (m *MockInfraDependencies) FetchPodiumByRace(raceId int, drivers []domain_model.Driver) ([3]domain_model.Podium, error) {
72+
args := m.Called(raceId, drivers)
73+
return args.Get(0).([3]domain_model.Podium), args.Error(1)
74+
}
75+
func (m *MockInfraDependencies) FetchDriversByRace(raceId int) ([]domain_model.Driver, error) {
76+
args := m.Called(raceId)
77+
return args.Get(0).([]domain_model.Driver), args.Error(1)
78+
}

internal/controller/Ping.go

Lines changed: 0 additions & 9 deletions
This file was deleted.

internal/domain/model/Driver.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package domain_model
2+
3+
type Driver struct {
4+
Number int `json:"driver_number"`
5+
FullName string `json:"full_name"`
6+
Country string `json:"country_code"`
7+
Avatar string `json:"avatar"`
8+
TeamName string `json:"team_name"`
9+
}

internal/domain/model/Podium.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package domain_model
2+
3+
type Podium struct {
4+
Driver
5+
Position int `json:"position"`
6+
}

internal/domain/model/Race.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package domain_model
2+
3+
import "time"
4+
5+
type Race struct {
6+
Id int `json:"id"`
7+
Year int `json:"year"`
8+
StartDate time.Time `json:"date_start"`
9+
CountryName string `json:"country_name"`
10+
CircuitName string `json:"circuit_name"`
11+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package domain_service
2+
3+
import (
4+
domain_model "github.com/mmadariaga/go-api/internal/domain/model"
5+
)
6+
7+
type GetDriversByRaceDependencies interface {
8+
FetchDriversByRace(raceId int) ([]domain_model.Driver, error)
9+
}
10+
11+
func GetDriversByRace(races []domain_model.Race, dependencies GetDriversByRaceDependencies) (map[int][]domain_model.Driver, error) {
12+
13+
driversByRace := make(map[int][]domain_model.Driver)
14+
15+
for _, race := range races {
16+
result, error := dependencies.FetchDriversByRace(race.Id)
17+
if error != nil {
18+
return nil, error
19+
}
20+
21+
driversByRace[race.Id] = result
22+
}
23+
24+
return driversByRace, nil
25+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package domain_service
2+
3+
import (
4+
domain_model "github.com/mmadariaga/go-api/internal/domain/model"
5+
)
6+
7+
type GetPodiumByRaceDependencies interface {
8+
GetDriversByRaceDependencies
9+
FetchPodiumByRace(raceId int, drivers []domain_model.Driver) ([3]domain_model.Podium, error)
10+
}
11+
12+
func GetPodiumByRace(
13+
races []domain_model.Race,
14+
dependencies GetPodiumByRaceDependencies,
15+
) (map[int][3]domain_model.Podium, error) {
16+
17+
podiumsByRace := make(map[int][3]domain_model.Podium)
18+
19+
drivers, error := GetDriversByRace(races, dependencies)
20+
if error != nil {
21+
return nil, error
22+
}
23+
24+
for _, race := range races {
25+
result, error := dependencies.FetchPodiumByRace(race.Id, drivers[race.Id])
26+
if error != nil {
27+
return nil, error
28+
}
29+
30+
podiumsByRace[race.Id] = result
31+
}
32+
33+
return podiumsByRace, nil
34+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package infrastructure_controller
2+
3+
import (
4+
"net/http"
5+
6+
application_pong "github.com/mmadariaga/go-api/internal/application/pong"
7+
)
8+
9+
func Ping(w http.ResponseWriter, r *http.Request) {
10+
resp := application_pong.Pong()
11+
w.Write([]byte(resp))
12+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package infrastructure_controller
2+
3+
import (
4+
"net/http"
5+
"testing"
6+
7+
"github.com/mmadariaga/go-api/internal/test"
8+
)
9+
10+
func TestPing(t *testing.T) {
11+
12+
mockWriter := new(test.MockResponseWriter)
13+
mockWriter.On(
14+
"Write",
15+
[]byte("pong"),
16+
).Return(
17+
len("pong"),
18+
nil,
19+
)
20+
21+
Ping(mockWriter, new(http.Request))
22+
23+
mockWriter.AssertExpectations(t)
24+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package infrastructure_controller
2+
3+
import (
4+
"encoding/json"
5+
"net/http"
6+
7+
"github.com/rs/zerolog/log"
8+
9+
application_races "github.com/mmadariaga/go-api/internal/application/races"
10+
domain_model "github.com/mmadariaga/go-api/internal/domain/model"
11+
infrastructure_service "github.com/mmadariaga/go-api/internal/infrastructure/service"
12+
)
13+
14+
func Races(w http.ResponseWriter, r *http.Request) {
15+
16+
races, error := application_races.GetRacesByYear(
17+
2024,
18+
&infraDependencies{},
19+
)
20+
if error != nil {
21+
log.Panic().Msg(error.Error())
22+
}
23+
24+
racesJson, error := json.Marshal(races)
25+
if error != nil {
26+
log.Panic().Msg(error.Error())
27+
}
28+
29+
w.Header().Set("Content-Type", "application/json")
30+
w.Write(racesJson)
31+
}
32+
33+
type infraDependencies struct{}
34+
35+
func (d *infraDependencies) FetchRacesByYear(year int) ([]domain_model.Race, error) {
36+
return infrastructure_service.FetchRacesByYear(year)
37+
}
38+
func (d *infraDependencies) FetchPodiumByRace(raceId int, drivers []domain_model.Driver) ([3]domain_model.Podium, error) {
39+
return infrastructure_service.FetchPodiumByRace(raceId, drivers)
40+
}
41+
func (d *infraDependencies) FetchDriversByRace(raceId int) ([]domain_model.Driver, error) {
42+
return infrastructure_service.FetchDriversByRace(raceId)
43+
}

0 commit comments

Comments
 (0)