Add races results GET endpoints (by ID and last finished) and regenerate swagger specs

This commit is contained in:
2026-07-17 22:41:55 +04:00
parent c8ff4c04ad
commit 9c9632343d
10 changed files with 812 additions and 318 deletions
+94
View File
@@ -90,6 +90,100 @@ func racesListHandler(svc *races.Service) http.HandlerFunc {
}
}
// racesGetLastHandler godoc
// @Summary Get the results of the last finished race
// @Description Returns the details and podium/results of the most recently finished race.
// @Tags races
// @Produce json
// @Success 200 {object} transport.LobbyRace "Last race results"
// @Failure 404 {object} map[string]interface{} "No finished races found"
// @Failure 500 {object} map[string]interface{} "Internal server error"
// @Router /api/races/last [get]
func racesGetLastHandler(svc *races.Service) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
res, err := svc.GetLastFinished(r.Context())
if err != nil {
if errors.Is(err, races.ErrNotFound) {
writeError(w, http.StatusNotFound, "not_found", "no finished races found")
return
}
writeError(w, http.StatusInternalServerError, "internal", err.Error())
return
}
var out transport.LobbyRace
meta := lobby.RaceMeta{
ID: res.ID,
Name: res.Name,
TrackID: res.TrackID,
MaxCars: res.MaxCars,
Laps: res.Laps,
TimeLimitS: res.TimeLimitS,
DriverIDs: res.DriverIDs,
Status: lobby.RaceStatus(res.Status),
CreatedMs: res.CreatedMs,
StartedMs: res.StartedMs,
}
if len(res.Podium) > 0 {
out = lobbyToWireFinished(meta, res.Podium)
} else {
out = lobbyToWire(meta)
}
writeJSON(w, http.StatusOK, out)
}
}
// racesGetHandler godoc
// @Summary Get results of a specific race by ID
// @Description Returns details of a single race by its ID. Works for both live and finished races.
// @Tags races
// @Produce json
// @Param id path string true "Race ID"
// @Success 200 {object} transport.LobbyRace "Race details"
// @Failure 400 {object} map[string]interface{} "Missing race id"
// @Failure 404 {object} map[string]interface{} "Race not found"
// @Failure 500 {object} map[string]interface{} "Internal server error"
// @Router /api/races/{id} [get]
func racesGetHandler(svc *races.Service) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
id := strings.TrimPrefix(r.URL.Path, "/api/races/")
if id == "" || strings.Contains(id, "/") {
writeError(w, http.StatusBadRequest, "bad_request", "missing race id")
return
}
res, err := svc.GetRace(r.Context(), id)
if err != nil {
if errors.Is(err, races.ErrNotFound) {
writeError(w, http.StatusNotFound, "not_found", "race not found")
return
}
writeError(w, http.StatusInternalServerError, "internal", err.Error())
return
}
var out transport.LobbyRace
meta := lobby.RaceMeta{
ID: res.ID,
Name: res.Name,
TrackID: res.TrackID,
MaxCars: res.MaxCars,
Laps: res.Laps,
TimeLimitS: res.TimeLimitS,
DriverIDs: res.DriverIDs,
Status: lobby.RaceStatus(res.Status),
CreatedMs: res.CreatedMs,
StartedMs: res.StartedMs,
}
if len(res.Podium) > 0 {
out = lobbyToWireFinished(meta, res.Podium)
} else {
out = lobbyToWire(meta)
}
writeJSON(w, http.StatusOK, out)
}
}
func parseRaceListFilter(r *http.Request) (races.ListFilter, error) {
q := r.URL.Query()
cur, err := races.DecodeCursor(q.Get("cursor"))