Add player profiles and community settings
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -65,6 +65,9 @@ func New(service *application.Service, store application.Store, hub *realtime.Hu
|
||||
api.Get("/api/me", s.me)
|
||||
api.Patch("/api/me/player", s.updateProfile)
|
||||
api.Get("/api/players", s.players)
|
||||
api.Get("/api/players/{playerID}", s.playerProfile)
|
||||
api.Get("/api/community/settings", s.communitySettings)
|
||||
api.Put("/api/community/settings", s.updateCommunitySettings)
|
||||
api.Get("/api/accounts", s.accounts)
|
||||
api.Patch("/api/accounts/{accountID}/moderator", s.setModerator)
|
||||
api.Get("/api/events", s.events)
|
||||
@@ -246,7 +249,8 @@ func (s *Server) me(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
func (s *Server) updateProfile(w http.ResponseWriter, r *http.Request) {
|
||||
var in struct {
|
||||
DisplayName string `json:"displayName"`
|
||||
DisplayName *string `json:"displayName"`
|
||||
BattleTag *string `json:"battleTag"`
|
||||
Ratings domain.Ratings `json:"ratings"`
|
||||
PreferredRoles []domain.Role `json:"preferredRoles"`
|
||||
PreferredPlayerIDs []string `json:"preferredPlayerIds"`
|
||||
@@ -256,7 +260,10 @@ func (s *Server) updateProfile(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
id := who(r)
|
||||
out, err := s.service.UpdateOwnProfile(r.Context(), id.account, id.player, in.DisplayName, in.Ratings, in.PreferredRoles, in.PreferredPlayerIDs, in.AvoidedPlayerIDs)
|
||||
out, err := s.service.UpdateOwnProfile(r.Context(), id.account, id.player, application.UpdateOwnProfileInput{
|
||||
DisplayName: in.DisplayName, BattleTag: in.BattleTag, Ratings: in.Ratings,
|
||||
PreferredRoles: in.PreferredRoles, PreferredPlayerIDs: in.PreferredPlayerIDs, AvoidedPlayerIDs: in.AvoidedPlayerIDs,
|
||||
})
|
||||
respond(w, out, err, http.StatusOK)
|
||||
}
|
||||
|
||||
@@ -265,6 +272,27 @@ func (s *Server) players(w http.ResponseWriter, r *http.Request) {
|
||||
respond(w, out, err, 200)
|
||||
}
|
||||
|
||||
func (s *Server) playerProfile(w http.ResponseWriter, r *http.Request) {
|
||||
out, err := s.service.GetPlayerProfile(r.Context(), chi.URLParam(r, "playerID"))
|
||||
respond(w, out, err, http.StatusOK)
|
||||
}
|
||||
|
||||
func (s *Server) communitySettings(w http.ResponseWriter, r *http.Request) {
|
||||
out, err := s.service.GetCommunitySettings(r.Context())
|
||||
respond(w, out, err, http.StatusOK)
|
||||
}
|
||||
|
||||
func (s *Server) updateCommunitySettings(w http.ResponseWriter, r *http.Request) {
|
||||
var input struct {
|
||||
DiscordInviteURL string `json:"discordInviteUrl"`
|
||||
}
|
||||
if !decode(w, r, &input) {
|
||||
return
|
||||
}
|
||||
out, err := s.service.UpdateCommunitySettings(r.Context(), who(r).account, input.DiscordInviteURL)
|
||||
respond(w, out, err, http.StatusOK)
|
||||
}
|
||||
|
||||
func (s *Server) accounts(w http.ResponseWriter, r *http.Request) {
|
||||
out, err := s.service.ListAccounts(r.Context(), who(r).account)
|
||||
respond(w, out, err, http.StatusOK)
|
||||
|
||||
@@ -1,6 +1,18 @@
|
||||
package httpapi
|
||||
|
||||
import "testing"
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"mixmaker/backend/internal/application"
|
||||
"mixmaker/backend/internal/domain"
|
||||
"mixmaker/backend/internal/realtime"
|
||||
)
|
||||
|
||||
func TestCreateEventRequestDefaultsEveryonePing(t *testing.T) {
|
||||
if !((createEventRequest{}).options().PingEveryone) {
|
||||
@@ -14,3 +26,67 @@ func TestCreateEventRequestCanDisableEveryonePing(t *testing.T) {
|
||||
t.Fatal("explicit false pingEveryone must disable the ping")
|
||||
}
|
||||
}
|
||||
|
||||
type profileHTTPStore struct {
|
||||
application.Store
|
||||
account domain.Account
|
||||
player domain.Player
|
||||
}
|
||||
|
||||
func (s *profileHTTPStore) AccountBySession(context.Context, string) (domain.Account, domain.Player, error) {
|
||||
return s.account, s.player, nil
|
||||
}
|
||||
|
||||
func (s *profileHTTPStore) GetPlayer(context.Context, string) (domain.Player, error) {
|
||||
return s.player, nil
|
||||
}
|
||||
|
||||
func (s *profileHTTPStore) ListEvents(context.Context, time.Time) ([]domain.Event, error) {
|
||||
return []domain.Event{}, nil
|
||||
}
|
||||
|
||||
func TestPlayerProfileHTTPResponseIsPublicSafe(t *testing.T) {
|
||||
store := &profileHTTPStore{
|
||||
account: domain.Account{ID: "account", Role: domain.RolePlayer},
|
||||
player: domain.Player{
|
||||
ID: "player", AccountID: "account", DisplayName: "Ana", BattleTag: "Ana#1234",
|
||||
Ratings: domain.Ratings{Tank: 10, Damage: 20, Support: 30},
|
||||
PreferredPlayerIDs: []string{"secret-preference"}, AvoidedPlayerIDs: []string{"secret-avoid"},
|
||||
},
|
||||
}
|
||||
hub := realtime.New()
|
||||
handler := New(application.New(store, hub, nil), store, hub, Config{CookieName: "session"})
|
||||
request := httptest.NewRequest(http.MethodGet, "/api/players/player", nil)
|
||||
request.AddCookie(&http.Cookie{Name: "session", Value: "valid"})
|
||||
response := httptest.NewRecorder()
|
||||
handler.ServeHTTP(response, request)
|
||||
|
||||
if response.Code != http.StatusOK {
|
||||
t.Fatalf("unexpected status %d: %s", response.Code, response.Body.String())
|
||||
}
|
||||
var body map[string]any
|
||||
if err := json.Unmarshal(response.Body.Bytes(), &body); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
encoded := response.Body.String()
|
||||
if strings.Contains(encoded, "preferred") || strings.Contains(encoded, "avoid") || strings.Contains(encoded, "accountId") {
|
||||
t.Fatalf("profile leaked private fields: %s", encoded)
|
||||
}
|
||||
}
|
||||
|
||||
func TestModeratorCannotUpdateCommunitySettings(t *testing.T) {
|
||||
store := &profileHTTPStore{
|
||||
account: domain.Account{ID: "moderator", Role: domain.RoleModerator},
|
||||
player: domain.Player{ID: "player", AccountID: "moderator"},
|
||||
}
|
||||
hub := realtime.New()
|
||||
handler := New(application.New(store, hub, nil), store, hub, Config{CookieName: "session"})
|
||||
request := httptest.NewRequest(http.MethodPut, "/api/community/settings", strings.NewReader(`{"discordInviteUrl":""}`))
|
||||
request.AddCookie(&http.Cookie{Name: "session", Value: "valid"})
|
||||
response := httptest.NewRecorder()
|
||||
handler.ServeHTTP(response, request)
|
||||
|
||||
if response.Code != http.StatusForbidden {
|
||||
t.Fatalf("moderator update returned %d: %s", response.Code, response.Body.String())
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user