• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

gameap / gameap / 25960901307

16 May 2026 11:29AM UTC coverage: 76.887% (+0.2%) from 76.64%
25960901307

push

github

et-nik
audit logs tests

45395 of 59041 relevant lines covered (76.89%)

33895.16 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

72.17
/internal/api/users/putuser/handler.go
1
package putuser
2

3
import (
4
        "context"
5
        "encoding/json"
6
        "log/slog"
7
        "net/http"
8
        "strconv"
9
        "strings"
10

11
        "github.com/gameap/gameap/internal/api/base"
12
        "github.com/gameap/gameap/internal/audit"
13
        "github.com/gameap/gameap/internal/filters"
14
        "github.com/gameap/gameap/internal/rbac"
15
        "github.com/gameap/gameap/internal/repositories"
16
        "github.com/gameap/gameap/pkg/api"
17
        "github.com/gameap/gameap/pkg/auth"
18
        "github.com/pkg/errors"
19
)
20

21
type Handler struct {
22
        usersRepo   repositories.UserRepository
23
        serversRepo repositories.ServerRepository
24
        rbac        base.RBAC
25
        tm          base.TransactionManager
26
        responder   base.Responder
27
        audit       audit.Logger
28
}
29

30
func NewHandler(
31
        usersRepo repositories.UserRepository,
32
        serversRepo repositories.ServerRepository,
33
        rbac base.RBAC,
34
        tm base.TransactionManager,
35
        responder base.Responder,
36
        auditLogger audit.Logger,
37
) *Handler {
13✔
38
        if auditLogger == nil {
24✔
39
                auditLogger = audit.NopLogger{}
11✔
40
        }
11✔
41

42
        return &Handler{
13✔
43
                usersRepo:   usersRepo,
13✔
44
                serversRepo: serversRepo,
13✔
45
                rbac:        rbac,
13✔
46
                tm:          tm,
13✔
47
                responder:   responder,
13✔
48
                audit:       auditLogger,
13✔
49
        }
13✔
50
}
51

52
//nolint:funlen
53
func (h *Handler) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
13✔
54
        ctx := r.Context()
13✔
55

13✔
56
        session := auth.SessionFromContext(ctx)
13✔
57
        if !session.IsAuthenticated() {
14✔
58
                h.responder.WriteError(ctx, rw, api.WrapHTTPError(
1✔
59
                        errors.New("user not authenticated"),
1✔
60
                        http.StatusUnauthorized,
1✔
61
                ))
1✔
62

1✔
63
                return
1✔
64
        }
1✔
65

66
        input := api.NewInputReader(r)
12✔
67

12✔
68
        userID, err := input.ReadUint("id")
12✔
69
        if err != nil {
13✔
70
                h.responder.WriteError(ctx, rw, api.WrapHTTPError(
1✔
71
                        errors.WithMessage(err, "invalid user id"),
1✔
72
                        http.StatusBadRequest,
1✔
73
                ))
1✔
74

1✔
75
                return
1✔
76
        }
1✔
77

78
        updateInput := &updateUserInput{}
11✔
79

11✔
80
        err = json.NewDecoder(r.Body).Decode(&updateInput)
11✔
81
        if err != nil {
11✔
82
                h.responder.WriteError(ctx, rw, api.WrapHTTPError(
×
83
                        errors.WithMessage(err, "invalid request"),
×
84
                        http.StatusBadRequest,
×
85
                ))
×
86

×
87
                return
×
88
        }
×
89

90
        err = updateInput.Validate()
11✔
91
        if err != nil {
14✔
92
                h.responder.WriteError(ctx, rw, api.WrapHTTPError(
3✔
93
                        errors.WithMessage(err, "invalid input"),
3✔
94
                        http.StatusBadRequest,
3✔
95
                ))
3✔
96

3✔
97
                return
3✔
98
        }
3✔
99

100
        users, err := h.usersRepo.Find(ctx, &filters.FindUser{
8✔
101
                IDs: []uint{userID},
8✔
102
        }, nil, &filters.Pagination{
8✔
103
                Limit: 1,
8✔
104
        })
8✔
105
        if err != nil {
8✔
106
                h.responder.WriteError(ctx, rw, errors.WithMessage(err, "failed to find user"))
×
107

×
108
                return
×
109
        }
×
110

111
        if len(users) == 0 {
9✔
112
                h.responder.WriteError(ctx, rw, api.NewNotFoundError("user not found"))
1✔
113

1✔
114
                return
1✔
115
        }
1✔
116

117
        user := &users[0]
7✔
118

7✔
119
        err = updateInput.Apply(user)
7✔
120
        if err != nil {
7✔
121
                h.responder.WriteError(ctx, rw, errors.WithMessage(err, "failed to apply input"))
×
122

×
123
                return
×
124
        }
×
125

126
        err = h.tm.Do(ctx, func(ctx context.Context) error {
14✔
127
                err = h.usersRepo.Save(ctx, user)
7✔
128
                if err != nil {
7✔
129
                        return errors.WithMessage(err, "failed to save user")
×
130
                }
×
131

132
                err = h.serversRepo.SetUserServers(ctx, user.ID, updateInput.ServerIDs())
7✔
133
                if err != nil {
7✔
134
                        return errors.WithMessage(err, "failed to assign servers to user")
×
135
                }
×
136

137
                err = h.rbac.SetRolesToUser(ctx, user.ID, updateInput.Roles)
7✔
138
                if err != nil {
7✔
139
                        var errInvalidRole rbac.InvalidRoleNameError
×
140
                        if errors.As(err, &errInvalidRole) {
×
141
                                return api.WrapHTTPError(err, http.StatusUnprocessableEntity)
×
142
                        }
×
143

144
                        return errors.WithMessage(err, "failed to assign roles to user")
×
145
                }
146

147
                return nil
7✔
148
        })
149
        if err != nil {
7✔
150
                h.responder.WriteError(ctx, rw, err)
×
151

×
152
                return
×
153
        }
×
154

155
        userResID := strconv.FormatUint(uint64(user.ID), 10)
7✔
156
        audit.SensitiveOp(ctx, h.audit, audit.EventUserUpdate, audit.CategoryAdminOp,
7✔
157
                "user", userResID, "update")
7✔
158
        if len(updateInput.Roles) > 0 {
11✔
159
                audit.SensitiveOp(ctx, h.audit, audit.EventUserRolesAssign, audit.CategoryAdminOp,
4✔
160
                        "user", userResID, "role_assign",
4✔
161
                        slog.String("roles", strings.Join(updateInput.Roles, ",")))
4✔
162
        }
4✔
163

164
        roleNames, err := h.rbac.GetRoles(ctx, user.ID)
7✔
165
        if err != nil {
7✔
166
                h.responder.WriteError(ctx, rw, errors.WithMessage(err, "failed to get user roles"))
×
167

×
168
                return
×
169
        }
×
170

171
        h.responder.Write(ctx, rw, newUserResponseFromUser(user, roleNames))
7✔
172
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc