Add event management features to API, including event cancellation, deletion, registration closure, and workflow balance generation. Introduce new roster management endpoints and enhance participant handling with avoided player preferences. Update database schema and service logic to support these changes.
This commit is contained in:
@@ -69,13 +69,26 @@ func New(service *application.Service, store application.Store, hub *realtime.Hu
|
||||
api.Post("/api/events", s.createEvent)
|
||||
api.Get("/api/events/{eventID}", s.getEvent)
|
||||
api.Put("/api/events/{eventID}", s.updateEvent)
|
||||
api.Delete("/api/events/{eventID}", s.deleteEvent)
|
||||
api.Post("/api/events/{eventID}/cancel", s.cancelEvent)
|
||||
api.Get("/api/events/{eventID}/tournament", s.getEventTournament)
|
||||
api.Get("/api/events/{eventID}/rsvps", s.rsvps)
|
||||
api.Post("/api/events/{eventID}/participants", s.createParticipant)
|
||||
api.Put("/api/events/{eventID}/rsvps/{playerID}", s.setRSVP)
|
||||
api.Delete("/api/events/{eventID}/rsvps/{playerID}", s.removeParticipant)
|
||||
api.Post("/api/events/{eventID}/balance", s.balance)
|
||||
api.Put("/api/events/{eventID}/teams", s.selectBalance)
|
||||
api.Get("/api/events/{eventID}/teams", s.teams)
|
||||
api.Post("/api/events/{eventID}/registration/close", s.closeRegistration)
|
||||
api.Post("/api/events/{eventID}/balance/generate", s.generateWorkflowBalance)
|
||||
api.Get("/api/events/{eventID}/roster", s.getRoster)
|
||||
api.Put("/api/events/{eventID}/roster", s.selectWorkflowBalance)
|
||||
api.Post("/api/events/{eventID}/roster/swap", s.swapRoster)
|
||||
api.Post("/api/events/{eventID}/roster/substitute", s.substituteRoster)
|
||||
api.Post("/api/events/{eventID}/roster/emergency-substitute", s.emergencySubstitute)
|
||||
api.Put("/api/events/{eventID}/roster/captain", s.setRosterCaptain)
|
||||
api.Post("/api/events/{eventID}/roster/confirm", s.confirmRosters)
|
||||
api.Post("/api/events/{eventID}/start", s.startScrim)
|
||||
api.Put("/api/teams/{teamID}/captain", s.assignCaptain)
|
||||
api.Post("/api/rulesets", s.saveRuleset)
|
||||
api.Get("/api/rulesets", s.listRulesets)
|
||||
@@ -88,6 +101,11 @@ func New(service *application.Service, store application.Store, hub *realtime.Hu
|
||||
api.Post("/api/tournaments", s.createTournament)
|
||||
api.Get("/api/tournaments/{id}", s.getTournament)
|
||||
api.Get("/api/series/{id}", s.getSeries)
|
||||
api.Post("/api/series/{id}/coin-toss", s.tossSeriesCoin)
|
||||
api.Post("/api/series/{id}/map-bans", s.banSeriesMap)
|
||||
api.Post("/api/series/{id}/map-pick", s.pickSeriesMap)
|
||||
api.Post("/api/series/{id}/hero-bans", s.banSeriesHero)
|
||||
api.Post("/api/series/{id}/map-result", s.recordSeriesResult)
|
||||
api.Post("/api/series/{id}/results", s.recordResult)
|
||||
api.Patch("/api/series/{id}/results/{index}", s.correctResult)
|
||||
api.Get("/api/events/stream", s.stream)
|
||||
@@ -220,12 +238,13 @@ func (s *Server) updateProfile(w http.ResponseWriter, r *http.Request) {
|
||||
Ratings domain.Ratings `json:"ratings"`
|
||||
PreferredRoles []domain.Role `json:"preferredRoles"`
|
||||
PreferredPlayerIDs []string `json:"preferredPlayerIds"`
|
||||
AvoidedPlayerIDs []string `json:"avoidedPlayerIds"`
|
||||
}
|
||||
if !decode(w, r, &in) {
|
||||
return
|
||||
}
|
||||
id := who(r)
|
||||
out, err := s.service.UpdateOwnProfile(r.Context(), id.account, id.player, in.DisplayName, in.Ratings, in.PreferredRoles, in.PreferredPlayerIDs)
|
||||
out, err := s.service.UpdateOwnProfile(r.Context(), id.account, id.player, in.DisplayName, in.Ratings, in.PreferredRoles, in.PreferredPlayerIDs, in.AvoidedPlayerIDs)
|
||||
respond(w, out, err, http.StatusOK)
|
||||
}
|
||||
|
||||
@@ -235,7 +254,7 @@ func (s *Server) players(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
func (s *Server) events(w http.ResponseWriter, r *http.Request) {
|
||||
from := time.Now().UTC()
|
||||
from := time.Unix(0, 0).UTC()
|
||||
if raw := r.URL.Query().Get("from"); raw != "" {
|
||||
if parsed, err := time.Parse(time.RFC3339, raw); err == nil {
|
||||
from = parsed
|
||||
@@ -303,6 +322,11 @@ func (s *Server) setRSVP(w http.ResponseWriter, r *http.Request) {
|
||||
respond(w, out, err, 200)
|
||||
}
|
||||
|
||||
func (s *Server) removeParticipant(w http.ResponseWriter, r *http.Request) {
|
||||
err := s.service.RemoveParticipant(r.Context(), who(r).account, chi.URLParam(r, "eventID"), chi.URLParam(r, "playerID"))
|
||||
respond(w, nil, err, http.StatusNoContent)
|
||||
}
|
||||
|
||||
func (s *Server) balance(w http.ResponseWriter, r *http.Request) {
|
||||
out, err := s.service.Balance(r.Context(), who(r).account, chi.URLParam(r, "eventID"))
|
||||
respond(w, out, err, 200)
|
||||
|
||||
243
backend/internal/adapter/httpapi/workflow_handlers.go
Normal file
243
backend/internal/adapter/httpapi/workflow_handlers.go
Normal file
@@ -0,0 +1,243 @@
|
||||
package httpapi
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
|
||||
"mixmaker/backend/internal/domain"
|
||||
)
|
||||
|
||||
func (s *Server) cancelEvent(w http.ResponseWriter, r *http.Request) {
|
||||
var in struct {
|
||||
ExpectedVersion int `json:"expectedVersion"`
|
||||
}
|
||||
if !decode(w, r, &in) {
|
||||
return
|
||||
}
|
||||
out, err := s.service.CancelEvent(r.Context(), who(r).account, chi.URLParam(r, "eventID"), in.ExpectedVersion)
|
||||
respond(w, out, err, http.StatusOK)
|
||||
}
|
||||
|
||||
func (s *Server) deleteEvent(w http.ResponseWriter, r *http.Request) {
|
||||
err := s.service.DeleteEvent(r.Context(), who(r).account, chi.URLParam(r, "eventID"))
|
||||
respond(w, nil, err, http.StatusNoContent)
|
||||
}
|
||||
|
||||
func (s *Server) closeRegistration(w http.ResponseWriter, r *http.Request) {
|
||||
var in struct {
|
||||
RulesetID string `json:"rulesetId"`
|
||||
ExpectedVersion int `json:"expectedVersion"`
|
||||
}
|
||||
if !decode(w, r, &in) {
|
||||
return
|
||||
}
|
||||
out, err := s.service.CloseRegistration(r.Context(), who(r).account, chi.URLParam(r, "eventID"), in.RulesetID, in.ExpectedVersion)
|
||||
respond(w, out, err, http.StatusOK)
|
||||
}
|
||||
|
||||
func (s *Server) generateWorkflowBalance(w http.ResponseWriter, r *http.Request) {
|
||||
var in struct {
|
||||
ExpectedVersion int `json:"expectedVersion"`
|
||||
}
|
||||
if !decode(w, r, &in) {
|
||||
return
|
||||
}
|
||||
out, err := s.service.GenerateBalance(r.Context(), who(r).account, chi.URLParam(r, "eventID"), in.ExpectedVersion)
|
||||
respond(w, out, err, http.StatusOK)
|
||||
}
|
||||
|
||||
func (s *Server) getRoster(w http.ResponseWriter, r *http.Request) {
|
||||
out, err := s.store.GetRoster(r.Context(), chi.URLParam(r, "eventID"))
|
||||
respond(w, out, err, http.StatusOK)
|
||||
}
|
||||
|
||||
func (s *Server) selectWorkflowBalance(w http.ResponseWriter, r *http.Request) {
|
||||
var in struct {
|
||||
Candidate domain.BalanceCandidate `json:"candidate"`
|
||||
ExpectedVersion int `json:"expectedVersion"`
|
||||
}
|
||||
if !decode(w, r, &in) {
|
||||
return
|
||||
}
|
||||
out, err := s.service.SelectWorkflowBalance(r.Context(), who(r).account, chi.URLParam(r, "eventID"), in.Candidate, in.ExpectedVersion)
|
||||
respond(w, out, err, http.StatusOK)
|
||||
}
|
||||
|
||||
func (s *Server) swapRoster(w http.ResponseWriter, r *http.Request) {
|
||||
var in struct {
|
||||
TeamAID string `json:"teamAId"`
|
||||
PlayerAID string `json:"playerAId"`
|
||||
TeamBID string `json:"teamBId"`
|
||||
PlayerBID string `json:"playerBId"`
|
||||
ExpectedVersion int `json:"expectedVersion"`
|
||||
}
|
||||
if !decode(w, r, &in) {
|
||||
return
|
||||
}
|
||||
out, err := s.service.SwapRoster(r.Context(), who(r).account, chi.URLParam(r, "eventID"), in.TeamAID, in.PlayerAID, in.TeamBID, in.PlayerBID, in.ExpectedVersion)
|
||||
respond(w, out, err, http.StatusOK)
|
||||
}
|
||||
|
||||
func (s *Server) substituteRoster(w http.ResponseWriter, r *http.Request) {
|
||||
s.handleSubstitute(w, r, false)
|
||||
}
|
||||
|
||||
func (s *Server) emergencySubstitute(w http.ResponseWriter, r *http.Request) {
|
||||
s.handleSubstitute(w, r, true)
|
||||
}
|
||||
|
||||
func (s *Server) handleSubstitute(w http.ResponseWriter, r *http.Request, emergency bool) {
|
||||
var in struct {
|
||||
TeamID string `json:"teamId"`
|
||||
OutgoingPlayerID string `json:"outgoingPlayerId"`
|
||||
ReservePlayerID string `json:"reservePlayerId"`
|
||||
ExpectedVersion int `json:"expectedVersion"`
|
||||
}
|
||||
if !decode(w, r, &in) {
|
||||
return
|
||||
}
|
||||
out, err := s.service.SubstituteRoster(r.Context(), who(r).account, chi.URLParam(r, "eventID"), in.TeamID, in.OutgoingPlayerID, in.ReservePlayerID, in.ExpectedVersion, emergency)
|
||||
respond(w, out, err, http.StatusOK)
|
||||
}
|
||||
|
||||
func (s *Server) setRosterCaptain(w http.ResponseWriter, r *http.Request) {
|
||||
var in struct {
|
||||
TeamID string `json:"teamId"`
|
||||
PlayerID string `json:"playerId"`
|
||||
ExpectedVersion int `json:"expectedVersion"`
|
||||
}
|
||||
if !decode(w, r, &in) {
|
||||
return
|
||||
}
|
||||
out, err := s.service.SetRosterCaptain(r.Context(), who(r).account, chi.URLParam(r, "eventID"), in.TeamID, in.PlayerID, in.ExpectedVersion)
|
||||
respond(w, out, err, http.StatusOK)
|
||||
}
|
||||
|
||||
func (s *Server) confirmRosters(w http.ResponseWriter, r *http.Request) {
|
||||
var in struct {
|
||||
ExpectedVersion int `json:"expectedVersion"`
|
||||
ExpectedRosterVersion int `json:"expectedRosterVersion"`
|
||||
}
|
||||
if !decode(w, r, &in) {
|
||||
return
|
||||
}
|
||||
out, err := s.service.ConfirmRosters(r.Context(), who(r).account, chi.URLParam(r, "eventID"), in.ExpectedVersion, in.ExpectedRosterVersion)
|
||||
respond(w, out, err, http.StatusOK)
|
||||
}
|
||||
|
||||
func (s *Server) startScrim(w http.ResponseWriter, r *http.Request) {
|
||||
var in struct {
|
||||
ExpectedVersion int `json:"expectedVersion"`
|
||||
}
|
||||
if !decode(w, r, &in) {
|
||||
return
|
||||
}
|
||||
out, err := s.service.StartScrim(r.Context(), who(r).account, chi.URLParam(r, "eventID"), in.ExpectedVersion)
|
||||
respond(w, out, err, http.StatusCreated)
|
||||
}
|
||||
|
||||
func (s *Server) tossSeriesCoin(w http.ResponseWriter, r *http.Request) {
|
||||
var in struct {
|
||||
Seed string `json:"seed"`
|
||||
ActingTeamID string `json:"actingTeamId"`
|
||||
ExpectedVersion int `json:"expectedVersion"`
|
||||
}
|
||||
if !decode(w, r, &in) {
|
||||
return
|
||||
}
|
||||
series, err := s.store.GetSeries(r.Context(), chi.URLParam(r, "id"))
|
||||
if err == nil {
|
||||
err = s.authorizeTeam(r, series.EventID, in.ActingTeamID)
|
||||
}
|
||||
if err != nil {
|
||||
writeError(w, err)
|
||||
return
|
||||
}
|
||||
out, err := s.service.TossSeriesCoin(r.Context(), who(r).account, series.ID, in.Seed, in.ExpectedVersion)
|
||||
respond(w, out, err, http.StatusOK)
|
||||
}
|
||||
|
||||
func (s *Server) banSeriesMap(w http.ResponseWriter, r *http.Request) {
|
||||
var in struct {
|
||||
TeamID string `json:"teamId"`
|
||||
Map string `json:"map"`
|
||||
ExpectedVersion int `json:"expectedVersion"`
|
||||
}
|
||||
if !decode(w, r, &in) {
|
||||
return
|
||||
}
|
||||
series, err := s.store.GetSeries(r.Context(), chi.URLParam(r, "id"))
|
||||
if err == nil {
|
||||
err = s.authorizeTeam(r, series.EventID, in.TeamID)
|
||||
}
|
||||
if err != nil {
|
||||
writeError(w, err)
|
||||
return
|
||||
}
|
||||
out, err := s.service.BanSeriesMap(r.Context(), who(r).account, series.ID, in.TeamID, in.Map, in.ExpectedVersion)
|
||||
respond(w, out, err, http.StatusOK)
|
||||
}
|
||||
|
||||
func (s *Server) pickSeriesMap(w http.ResponseWriter, r *http.Request) {
|
||||
var in struct {
|
||||
TeamID string `json:"teamId"`
|
||||
Map string `json:"map"`
|
||||
ExpectedVersion int `json:"expectedVersion"`
|
||||
}
|
||||
if !decode(w, r, &in) {
|
||||
return
|
||||
}
|
||||
series, err := s.store.GetSeries(r.Context(), chi.URLParam(r, "id"))
|
||||
if err == nil {
|
||||
err = s.authorizeTeam(r, series.EventID, in.TeamID)
|
||||
}
|
||||
if err != nil {
|
||||
writeError(w, err)
|
||||
return
|
||||
}
|
||||
out, err := s.service.PickSeriesMap(r.Context(), who(r).account, series.ID, in.TeamID, in.Map, in.ExpectedVersion)
|
||||
respond(w, out, err, http.StatusOK)
|
||||
}
|
||||
|
||||
func (s *Server) banSeriesHero(w http.ResponseWriter, r *http.Request) {
|
||||
var in struct {
|
||||
TeamID string `json:"teamId"`
|
||||
Hero string `json:"hero"`
|
||||
ExpectedVersion int `json:"expectedVersion"`
|
||||
}
|
||||
if !decode(w, r, &in) {
|
||||
return
|
||||
}
|
||||
series, err := s.store.GetSeries(r.Context(), chi.URLParam(r, "id"))
|
||||
if err == nil {
|
||||
err = s.authorizeTeam(r, series.EventID, in.TeamID)
|
||||
}
|
||||
if err != nil {
|
||||
writeError(w, err)
|
||||
return
|
||||
}
|
||||
out, err := s.service.BanSeriesHero(r.Context(), who(r).account, series.ID, in.TeamID, in.Hero, in.ExpectedVersion)
|
||||
respond(w, out, err, http.StatusOK)
|
||||
}
|
||||
|
||||
func (s *Server) recordSeriesResult(w http.ResponseWriter, r *http.Request) {
|
||||
var in struct {
|
||||
Outcome domain.MapOutcome `json:"outcome"`
|
||||
ActingTeamID string `json:"actingTeamId"`
|
||||
ExpectedVersion int `json:"expectedVersion"`
|
||||
}
|
||||
if !decode(w, r, &in) {
|
||||
return
|
||||
}
|
||||
series, err := s.store.GetSeries(r.Context(), chi.URLParam(r, "id"))
|
||||
if err == nil {
|
||||
err = s.authorizeTeam(r, series.EventID, in.ActingTeamID)
|
||||
}
|
||||
if err != nil {
|
||||
writeError(w, err)
|
||||
return
|
||||
}
|
||||
out, err := s.service.RecordSeriesResult(r.Context(), who(r).account, series.ID, in.Outcome, in.ExpectedVersion)
|
||||
respond(w, out, err, http.StatusOK)
|
||||
}
|
||||
Reference in New Issue
Block a user