Skip to content

Commit d3c8199

Browse files
committed
feat: improve error handling in club and census services with detailed messages
1 parent 41556eb commit d3c8199

File tree

7 files changed

+62
-37
lines changed

7 files changed

+62
-37
lines changed

src/api/api_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,11 @@ func TestDetectLanguage(t *testing.T) {
1616
expected string
1717
}{
1818
{
19-
name: "User preference takes precedence",
19+
name: "Header takes precedence over user preference",
2020
userLanguage: "fr",
2121
headerLanguage: "es",
2222
acceptLanguage: "de",
23-
expected: "fr",
23+
expected: "es",
2424
},
2525
{
2626
name: "X-Language header used if no user preference",

src/endpoints/clubs/club.go

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ type CreateClubRequest struct {
3030
func (h *ClubHandler) Create(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
3131
user, err := api.GetUserFromContext(r)
3232
if err != nil {
33-
api.Error(w, r, err, http.StatusUnauthorized)
33+
api.Error(w, r, t.Errorf("failed to get user from context: %w", err), http.StatusUnauthorized)
3434
return
3535
}
3636

@@ -56,7 +56,7 @@ func (h *ClubHandler) Create(w http.ResponseWriter, r *http.Request, _ httproute
5656

5757
err = h.Service.CreateClub(r.Context(), clubEntity, user.Key)
5858
if err != nil {
59-
api.Error(w, r, err, http.StatusBadRequest)
59+
api.Error(w, r, t.Errorf("could not create club: %w", err), http.StatusBadRequest)
6060
return
6161
}
6262

@@ -66,14 +66,14 @@ func (h *ClubHandler) Create(w http.ResponseWriter, r *http.Request, _ httproute
6666
func (h *ClubHandler) Get(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
6767
user, err := api.GetUserFromContext(r)
6868
if err != nil {
69-
api.Error(w, r, err, http.StatusUnauthorized)
69+
api.Error(w, r, t.Errorf("failed to get user from context: %w", err), http.StatusUnauthorized)
7070
return
7171
}
7272

7373
key := ps.ByName("key")
7474
club, err := h.Service.GetClub(r.Context(), key, user)
7575
if err != nil {
76-
api.Error(w, r, err, http.StatusForbidden)
76+
api.Error(w, r, t.Errorf("failed to get club: %w", err), http.StatusForbidden)
7777
return
7878
}
7979

@@ -83,7 +83,7 @@ func (h *ClubHandler) Get(w http.ResponseWriter, r *http.Request, ps httprouter.
8383
func (h *ClubHandler) Update(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
8484
user, err := api.GetUserFromContext(r)
8585
if err != nil {
86-
api.Error(w, r, err, http.StatusUnauthorized)
86+
api.Error(w, r, t.Errorf("failed to get user from context: %w", err), http.StatusUnauthorized)
8787
return
8888
}
8989

@@ -102,7 +102,7 @@ func (h *ClubHandler) Update(w http.ResponseWriter, r *http.Request, ps httprout
102102

103103
err = h.Service.UpdateClub(r.Context(), key, updates, user)
104104
if err != nil {
105-
api.Error(w, r, err, http.StatusBadRequest)
105+
api.Error(w, r, t.Errorf("could not update club: %w", err), http.StatusBadRequest)
106106
return
107107
}
108108

@@ -113,14 +113,14 @@ func (h *ClubHandler) Update(w http.ResponseWriter, r *http.Request, ps httprout
113113
func (h *ClubHandler) Delete(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
114114
user, err := api.GetUserFromContext(r)
115115
if err != nil {
116-
api.Error(w, r, err, http.StatusUnauthorized)
116+
api.Error(w, r, t.Errorf("failed to get user from context: %w", err), http.StatusUnauthorized)
117117
return
118118
}
119119

120120
key := ps.ByName("key")
121121
err = h.Service.DeleteClub(r.Context(), key, user)
122122
if err != nil {
123-
api.Error(w, r, err, http.StatusBadRequest)
123+
api.Error(w, r, t.Errorf("could not delete club: %w", err), http.StatusBadRequest)
124124
return
125125
}
126126

@@ -164,7 +164,7 @@ func FilteredResponse(clubEntity *entities.Club) *entities.Club {
164164
func (h *ClubHandler) AddOwner(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
165165
user, err := api.GetUserFromContext(r)
166166
if err != nil {
167-
api.Error(w, r, err, http.StatusUnauthorized)
167+
api.Error(w, r, t.Errorf("failed to get user from context: %w", err), http.StatusUnauthorized)
168168
return
169169
}
170170

@@ -184,7 +184,7 @@ func (h *ClubHandler) AddOwner(w http.ResponseWriter, r *http.Request, ps httpro
184184

185185
err = h.Service.AddOwner(r.Context(), key, req.Email, user)
186186
if err != nil {
187-
api.Error(w, r, err, http.StatusBadRequest)
187+
api.Error(w, r, t.Errorf("could not add owner: %w", err), http.StatusBadRequest)
188188
return
189189
}
190190
// Return updated club
@@ -195,7 +195,7 @@ func (h *ClubHandler) AddOwner(w http.ResponseWriter, r *http.Request, ps httpro
195195
func (h *ClubHandler) RemoveOwner(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
196196
user, err := api.GetUserFromContext(r)
197197
if err != nil {
198-
api.Error(w, r, err, http.StatusUnauthorized)
198+
api.Error(w, r, t.Errorf("failed to get user from context: %w", err), http.StatusUnauthorized)
199199
return
200200
}
201201

@@ -204,7 +204,7 @@ func (h *ClubHandler) RemoveOwner(w http.ResponseWriter, r *http.Request, ps htt
204204

205205
err = h.Service.RemoveOwner(r.Context(), key, targetUserKey, user)
206206
if err != nil {
207-
api.Error(w, r, err, http.StatusBadRequest)
207+
api.Error(w, r, t.Errorf("could not remove user from list of owners: %w", err), http.StatusBadRequest)
208208
return
209209
}
210210
// Return updated club

src/repository/graph/club.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ func (db *Db) CreateClub(ctx context.Context, club *entities.Club, userKey strin
2222
// Create the club document
2323
err := db.Clubs.Create(club, ctx)
2424
if err != nil {
25-
return err
25+
return t.Errorf("could not create club document: %w", err)
2626
}
2727

2828
// Create the 'authorizes' edge

src/repository/t/t.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,13 @@ func LoadLanguages(config *dpv.Config) error {
9595
}
9696

9797
for _, lang := range config.Settings.SupportedLanguages {
98+
// English uses the raw keys as readable fallbacks and does not require a
99+
// translation file. Skipping prevents noisy warnings about a missing
100+
// strings_en.ini while still allowing "en" as a valid language choice.
101+
if lang == "en" {
102+
continue
103+
}
104+
98105
path := config.Path + "strings_" + lang + ".ini"
99106
m, err := loadFile(path)
100107
if err != nil {

src/service/census/census.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ func NewService(db *graph.Db) *Service {
2727
func (s *Service) Get(ctx context.Context, clubKey string, year int, user *entities.User) (*entities.Census, error) {
2828
authorized, err := s.IsAuthorized(ctx, user, clubKey)
2929
if err != nil {
30-
return nil, err
30+
return nil, t.Errorf("authorization check failed while getting census: %w", err)
3131
}
3232
if !authorized {
3333
return nil, t.Errorf("unauthorized: you are not a board member or admin")
@@ -38,7 +38,7 @@ func (s *Service) Get(ctx context.Context, clubKey string, year int, user *entit
3838
func (s *Service) Upsert(ctx context.Context, clubKey string, censusData *entities.Census, user *entities.User) error {
3939
authorized, err := s.IsAuthorized(ctx, user, clubKey)
4040
if err != nil {
41-
return err
41+
return t.Errorf("authorization check failed while upserting census: %w", err)
4242
}
4343
if !authorized {
4444
return t.Errorf("unauthorized: you are not a board member or admin")
@@ -53,7 +53,7 @@ func (s *Service) IsAuthorized(ctx context.Context, user *entities.User, clubKey
5353
}
5454
administered, err := s.Db.GetAdministeredClubs(ctx, user.Key)
5555
if err != nil {
56-
return false, err
56+
return false, t.Errorf("failed to load administered clubs for census authorization: %w", err)
5757
}
5858
for _, c := range administered {
5959
if c.GetKey() == clubKey {

src/service/club/club.go

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ func (s *Service) IsAuthorized(ctx context.Context, user *entities.User, clubKey
4343
}
4444
administered, err := s.DB.GetAdministeredClubs(ctx, user.Key)
4545
if err != nil {
46-
return false, err
46+
return false, t.Errorf("failed to load administered clubs for authorization: %w", err)
4747
}
4848
for _, c := range administered {
4949
if c.GetKey() == clubKey {
@@ -57,7 +57,7 @@ func (s *Service) IsAuthorized(ctx context.Context, user *entities.User, clubKey
5757
func (s *Service) GetClub(ctx context.Context, key string, user *entities.User) (*entities.Club, error) {
5858
authorized, err := s.IsAuthorized(ctx, user, key)
5959
if err != nil {
60-
return nil, err
60+
return nil, t.Errorf("authorization check failed while getting club: %w", err)
6161
}
6262
if !authorized {
6363
return nil, t.Errorf("unauthorized: you are not a board member or admin")
@@ -75,15 +75,15 @@ func (s *Service) ListClubs(ctx context.Context, userKey string) ([]entities.Clu
7575
func (s *Service) UpdateClub(ctx context.Context, key string, updates map[string]interface{}, user *entities.User) error {
7676
authorized, err := s.IsAuthorized(ctx, user, key)
7777
if err != nil {
78-
return err
78+
return t.Errorf("authorization check failed while updating club: %w", err)
7979
}
8080
if !authorized {
8181
return t.Errorf("unauthorized: you cannot update this club")
8282
}
8383

8484
club, err := s.DB.GetClubByKey(ctx, key)
8585
if err != nil {
86-
return err
86+
return t.Errorf("failed to load club for update: %w", err)
8787
}
8888

8989
// Apply updates
@@ -110,32 +110,38 @@ func (s *Service) UpdateClub(ctx context.Context, key string, updates map[string
110110
club.Membership.Address = addr
111111
}
112112

113-
return s.DB.UpdateClub(ctx, club)
113+
if err := s.DB.UpdateClub(ctx, club); err != nil {
114+
return t.Errorf("failed to update club: %w", err)
115+
}
116+
return nil
114117
}
115118

116119
// DeleteClub deletes a club if the user is authorized.
117120
func (s *Service) DeleteClub(ctx context.Context, key string, user *entities.User) error {
118121
authorized, err := s.IsAuthorized(ctx, user, key)
119122
if err != nil {
120-
return err
123+
return t.Errorf("authorization check failed while deleting club: %w", err)
121124
}
122125
if !authorized {
123126
return t.Errorf("unauthorized: you cannot delete this club")
124127
}
125128

126129
club, err := s.DB.GetClubByKey(ctx, key)
127130
if err != nil {
128-
return err
131+
return t.Errorf("failed to load club for deletion: %w", err)
129132
}
130133

131-
return s.DB.DeleteClub(ctx, club)
134+
if err := s.DB.DeleteClub(ctx, club); err != nil {
135+
return t.Errorf("failed to delete club: %w", err)
136+
}
137+
return nil
132138
}
133139

134140
// AddOwner adds a user as a club owner by email.
135141
func (s *Service) AddOwner(ctx context.Context, clubKey, email string, actor *entities.User) error {
136142
authorized, err := s.IsAuthorized(ctx, actor, clubKey)
137143
if err != nil {
138-
return err
144+
return t.Errorf("authorization check failed while adding owner: %w", err)
139145
}
140146
if !authorized {
141147
return t.Errorf("unauthorized: you cannot manage owners for this club")
@@ -157,7 +163,7 @@ func (s *Service) AddOwner(ctx context.Context, clubKey, email string, actor *en
157163
func (s *Service) RemoveOwner(ctx context.Context, clubKey, targetUserKey string, actor *entities.User) error {
158164
authorized, err := s.IsAuthorized(ctx, actor, clubKey)
159165
if err != nil {
160-
return err
166+
return t.Errorf("authorization check failed while removing owner: %w", err)
161167
}
162168
if !authorized {
163169
return t.Errorf("unauthorized: you cannot manage owners for this club")
@@ -171,7 +177,7 @@ func (s *Service) RemoveOwner(ctx context.Context, clubKey, targetUserKey string
171177
// Rule: Cannot remove the last remaining owner
172178
count, err := s.DB.CountVorstand(ctx, clubKey)
173179
if err != nil {
174-
return err
180+
return t.Errorf("failed to count current owners: %w", err)
175181
}
176182
if count <= 1 {
177183
// Verify if the target IS the one (should be if count is 1 and they exist)

src/service/club/membership.go

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import (
1010
func (s *Service) Apply(ctx context.Context, key string, user *entities.User) error {
1111
club, err := s.GetClub(ctx, key, user)
1212
if err != nil {
13-
return err
13+
return t.Errorf("failed to load club for membership application: %w", err)
1414
}
1515

1616
m := club.GetMembership()
@@ -19,14 +19,17 @@ func (s *Service) Apply(ctx context.Context, key string, user *entities.User) er
1919
}
2020

2121
m.Status = "requested"
22-
return s.DB.UpdateClub(ctx, club)
22+
if err := s.DB.UpdateClub(ctx, club); err != nil {
23+
return t.Errorf("failed to update club for membership application: %w", err)
24+
}
25+
return nil
2326
}
2427

2528
// Approve marks a club's membership as approved.
2629
func (s *Service) Approve(ctx context.Context, key string) error {
2730
club, err := s.DB.GetClubByKey(ctx, key)
2831
if err != nil {
29-
return err
32+
return t.Errorf("failed to load club for approval: %w", err)
3033
}
3134

3235
m := club.GetMembership()
@@ -35,14 +38,17 @@ func (s *Service) Approve(ctx context.Context, key string) error {
3538
}
3639

3740
m.Status = "active"
38-
return s.DB.UpdateClub(ctx, club)
41+
if err := s.DB.UpdateClub(ctx, club); err != nil {
42+
return t.Errorf("failed to update club for membership approval: %w", err)
43+
}
44+
return nil
3945
}
4046

4147
// Deny marks a club's membership as denied.
4248
func (s *Service) Deny(ctx context.Context, key string) error {
4349
club, err := s.DB.GetClubByKey(ctx, key)
4450
if err != nil {
45-
return err
51+
return t.Errorf("failed to load club for denial: %w", err)
4652
}
4753

4854
m := club.GetMembership()
@@ -51,14 +57,17 @@ func (s *Service) Deny(ctx context.Context, key string) error {
5157
}
5258

5359
m.Status = "denied"
54-
return s.DB.UpdateClub(ctx, club)
60+
if err := s.DB.UpdateClub(ctx, club); err != nil {
61+
return t.Errorf("failed to update club for membership denial: %w", err)
62+
}
63+
return nil
5564
}
5665

5766
// Cancel marks a club's membership as cancelled or none.
5867
func (s *Service) Cancel(ctx context.Context, key string, user *entities.User) error {
5968
club, err := s.GetClub(ctx, key, user)
6069
if err != nil {
61-
return err
70+
return t.Errorf("failed to load club for membership cancellation: %w", err)
6271
}
6372

6473
m := club.GetMembership()
@@ -68,5 +77,8 @@ func (s *Service) Cancel(ctx context.Context, key string, user *entities.User) e
6877
m.Status = "inactive"
6978
}
7079

71-
return s.DB.UpdateClub(ctx, club)
80+
if err := s.DB.UpdateClub(ctx, club); err != nil {
81+
return t.Errorf("failed to update club for membership cancellation: %w", err)
82+
}
83+
return nil
7284
}

0 commit comments

Comments
 (0)