Skip to content

Commit a1457b5

Browse files
committed
feat: convertGroups() no longer requires organization info
Removing role information from some users in the api. This info is excessive and not required. It is costly to always include
1 parent f17149c commit a1457b5

File tree

8 files changed

+103
-115
lines changed

8 files changed

+103
-115
lines changed

coderd/audit.go

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -186,13 +186,17 @@ func (api *API) convertAuditLog(ctx context.Context, dblog database.GetAuditLogs
186186

187187
if dblog.UserUsername.Valid {
188188
user = &codersdk.User{
189-
ID: dblog.UserID,
190-
Username: dblog.UserUsername.String,
191-
Email: dblog.UserEmail.String,
192-
CreatedAt: dblog.UserCreatedAt.Time,
193-
Status: codersdk.UserStatus(dblog.UserStatus.UserStatus),
194-
Roles: []codersdk.Role{},
195-
AvatarURL: dblog.UserAvatarUrl.String,
189+
ReducedUser: codersdk.ReducedUser{
190+
MinimalUser: codersdk.MinimalUser{
191+
ID: dblog.UserID,
192+
Username: dblog.UserUsername.String,
193+
AvatarURL: dblog.UserAvatarUrl.String,
194+
},
195+
Email: dblog.UserEmail.String,
196+
CreatedAt: dblog.UserCreatedAt.Time,
197+
Status: codersdk.UserStatus(dblog.UserStatus.UserStatus),
198+
},
199+
Roles: []codersdk.Role{},
196200
}
197201

198202
for _, roleName := range dblog.UserRoles {

coderd/database/db2sdk/db2sdk.go

Lines changed: 56 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,17 @@ import (
2424
"github.com/coder/coder/v2/tailnet"
2525
)
2626

27+
// List is a helper function to reduce boilerplate when converting slices of
28+
// database types to slices of codersdk types.
29+
// Only works if the function takes a single argument.
30+
func List[F any, T any](list []F, convert func(F) T) []T {
31+
into := make([]T, 0, len(list))
32+
for _, item := range list {
33+
into = append(into, convert(item))
34+
}
35+
return into
36+
}
37+
2738
type ExternalAuthMeta struct {
2839
Authenticated bool
2940
ValidateError string
@@ -49,21 +60,17 @@ func ExternalAuth(auth database.ExternalAuthLink, meta ExternalAuthMeta) codersd
4960
}
5061
}
5162

52-
func WorkspaceBuildParameters(params []database.WorkspaceBuildParameter) []codersdk.WorkspaceBuildParameter {
53-
out := make([]codersdk.WorkspaceBuildParameter, len(params))
54-
for i, p := range params {
55-
out[i] = WorkspaceBuildParameter(p)
56-
}
57-
return out
58-
}
59-
6063
func WorkspaceBuildParameter(p database.WorkspaceBuildParameter) codersdk.WorkspaceBuildParameter {
6164
return codersdk.WorkspaceBuildParameter{
6265
Name: p.Name,
6366
Value: p.Value,
6467
}
6568
}
6669

70+
func WorkspaceBuildParameters(params []database.WorkspaceBuildParameter) []codersdk.WorkspaceBuildParameter {
71+
return List(params, WorkspaceBuildParameter)
72+
}
73+
6774
func TemplateVersionParameters(params []database.TemplateVersionParameter) ([]codersdk.TemplateVersionParameter, error) {
6875
out := make([]codersdk.TemplateVersionParameter, len(params))
6976
var err error
@@ -118,21 +125,33 @@ func TemplateVersionParameter(param database.TemplateVersionParameter) (codersdk
118125
}, nil
119126
}
120127

121-
func User(user database.User, organizationIDs []uuid.UUID) codersdk.User {
122-
convertedUser := codersdk.User{
123-
ID: user.ID,
128+
func ReducedUser(user database.User) codersdk.ReducedUser {
129+
return codersdk.ReducedUser{
130+
MinimalUser: codersdk.MinimalUser{
131+
ID: user.ID,
132+
Username: user.Username,
133+
AvatarURL: user.AvatarURL,
134+
},
124135
Email: user.Email,
125136
Name: user.Name,
126137
CreatedAt: user.CreatedAt,
127138
LastSeenAt: user.LastSeenAt,
128-
Username: user.Username,
129139
Status: codersdk.UserStatus(user.Status),
130-
OrganizationIDs: organizationIDs,
131-
Roles: make([]codersdk.Role, 0, len(user.RBACRoles)),
132-
AvatarURL: user.AvatarURL,
133140
LoginType: codersdk.LoginType(user.LoginType),
134141
ThemePreference: user.ThemePreference,
135142
}
143+
}
144+
145+
func ReducedUsers(users []database.User) []codersdk.ReducedUser {
146+
return List(users, ReducedUser)
147+
}
148+
149+
func User(user database.User, organizationIDs []uuid.UUID) codersdk.User {
150+
convertedUser := codersdk.User{
151+
ReducedUser: ReducedUser(user),
152+
OrganizationIDs: organizationIDs,
153+
Roles: make([]codersdk.Role, 0, len(user.RBACRoles)),
154+
}
136155

137156
for _, roleName := range user.RBACRoles {
138157
rbacRole, _ := rbac.RoleByName(roleName)
@@ -142,6 +161,25 @@ func User(user database.User, organizationIDs []uuid.UUID) codersdk.User {
142161
return convertedUser
143162
}
144163

164+
func Users(users []database.User, organizationIDs map[uuid.UUID][]uuid.UUID) []codersdk.User {
165+
return List(users, func(user database.User) codersdk.User {
166+
return User(user, organizationIDs[user.ID])
167+
})
168+
}
169+
170+
func Group(group database.Group, members []database.User) codersdk.Group {
171+
return codersdk.Group{
172+
ID: group.ID,
173+
Name: group.Name,
174+
DisplayName: group.DisplayName,
175+
OrganizationID: group.OrganizationID,
176+
AvatarURL: group.AvatarURL,
177+
Members: ReducedUsers(members),
178+
QuotaAllowance: int(group.QuotaAllowance),
179+
Source: codersdk.GroupSource(group.Source),
180+
}
181+
}
182+
145183
func Role(role rbac.Role) codersdk.Role {
146184
return codersdk.Role{
147185
DisplayName: role.DisplayName,
@@ -248,11 +286,9 @@ func OAuth2ProviderApp(accessURL *url.URL, dbApp database.OAuth2ProviderApp) cod
248286
}
249287

250288
func OAuth2ProviderApps(accessURL *url.URL, dbApps []database.OAuth2ProviderApp) []codersdk.OAuth2ProviderApp {
251-
apps := []codersdk.OAuth2ProviderApp{}
252-
for _, dbApp := range dbApps {
253-
apps = append(apps, OAuth2ProviderApp(accessURL, dbApp))
254-
}
255-
return apps
289+
return List(dbApps, func(dbApp database.OAuth2ProviderApp) codersdk.OAuth2ProviderApp {
290+
return OAuth2ProviderApp(accessURL, dbApp)
291+
})
256292
}
257293

258294
func convertDisplayApps(apps []database.DisplayApp) []codersdk.DisplayApp {

codersdk/groups.go

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,14 @@ type CreateGroupRequest struct {
2525
}
2626

2727
type Group struct {
28-
ID uuid.UUID `json:"id" format:"uuid"`
29-
Name string `json:"name"`
30-
DisplayName string `json:"display_name"`
31-
OrganizationID uuid.UUID `json:"organization_id" format:"uuid"`
32-
Members []User `json:"members"`
33-
AvatarURL string `json:"avatar_url"`
34-
QuotaAllowance int `json:"quota_allowance"`
35-
Source GroupSource `json:"source"`
28+
ID uuid.UUID `json:"id" format:"uuid"`
29+
Name string `json:"name"`
30+
DisplayName string `json:"display_name"`
31+
OrganizationID uuid.UUID `json:"organization_id" format:"uuid"`
32+
Members []ReducedUser `json:"members"`
33+
AvatarURL string `json:"avatar_url"`
34+
QuotaAllowance int `json:"quota_allowance"`
35+
Source GroupSource `json:"source"`
3636
}
3737

3838
func (g Group) IsEveryone() bool {

codersdk/templates.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -200,8 +200,8 @@ type UpdateTemplateACL struct {
200200
// ACLAvailable is a list of users and groups that can be added to a template
201201
// ACL.
202202
type ACLAvailable struct {
203-
Users []User `json:"users"`
204-
Groups []Group `json:"groups"`
203+
Users []ReducedUser `json:"users"`
204+
Groups []Group `json:"groups"`
205205
}
206206

207207
type UpdateTemplateMeta struct {

codersdk/users.go

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -42,21 +42,28 @@ type MinimalUser struct {
4242
AvatarURL string `json:"avatar_url" format:"uri"`
4343
}
4444

45-
// User represents a user in Coder.
46-
type User struct {
47-
ID uuid.UUID `json:"id" validate:"required" table:"id" format:"uuid"`
48-
Username string `json:"username" validate:"required" table:"username,default_sort"`
45+
// ReducedUser omits role and organization information. Roles are deduced from
46+
// the user's site and organization roles. This requires fetching the user's
47+
// organizational memberships. Fetching that is more expensive, and not usually
48+
// required by the frontend.
49+
type ReducedUser struct {
50+
MinimalUser
4951
Name string `json:"name"`
5052
Email string `json:"email" validate:"required" table:"email" format:"email"`
5153
CreatedAt time.Time `json:"created_at" validate:"required" table:"created at" format:"date-time"`
5254
LastSeenAt time.Time `json:"last_seen_at" format:"date-time"`
5355

54-
Status UserStatus `json:"status" table:"status" enums:"active,suspended"`
56+
Status UserStatus `json:"status" table:"status" enums:"active,suspended"`
57+
LoginType LoginType `json:"login_type"`
58+
ThemePreference string `json:"theme_preference"`
59+
}
60+
61+
// User represents a user in Coder.
62+
type User struct {
63+
ReducedUser
64+
5565
OrganizationIDs []uuid.UUID `json:"organization_ids" format:"uuid"`
5666
Roles []Role `json:"roles"`
57-
AvatarURL string `json:"avatar_url" format:"uri"`
58-
LoginType LoginType `json:"login_type"`
59-
ThemePreference string `json:"theme_preference"`
6067
}
6168

6269
type GetUsersResponse struct {

enterprise/coderd/groups.go

Lines changed: 5 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"github.com/coder/coder/v2/coderd"
1212
"github.com/coder/coder/v2/coderd/audit"
1313
"github.com/coder/coder/v2/coderd/database"
14+
"github.com/coder/coder/v2/coderd/database/db2sdk"
1415
"github.com/coder/coder/v2/coderd/httpapi"
1516
"github.com/coder/coder/v2/coderd/httpmw"
1617
"github.com/coder/coder/v2/coderd/rbac"
@@ -77,7 +78,7 @@ func (api *API) postGroupByOrganization(rw http.ResponseWriter, r *http.Request)
7778
var emptyUsers []database.User
7879
aReq.New = group.Auditable(emptyUsers)
7980

80-
httpapi.Write(ctx, rw, http.StatusCreated, convertGroup(group, nil))
81+
httpapi.Write(ctx, rw, http.StatusCreated, db2sdk.Group(group, nil))
8182
}
8283

8384
// @Summary Update group by name
@@ -281,7 +282,7 @@ func (api *API) patchGroup(rw http.ResponseWriter, r *http.Request) {
281282

282283
aReq.New = group.Auditable(patchedMembers)
283284

284-
httpapi.Write(ctx, rw, http.StatusOK, convertGroup(group, patchedMembers))
285+
httpapi.Write(ctx, rw, http.StatusOK, db2sdk.Group(group, patchedMembers))
285286
}
286287

287288
// @Summary Delete group by name
@@ -365,7 +366,7 @@ func (api *API) group(rw http.ResponseWriter, r *http.Request) {
365366
return
366367
}
367368

368-
httpapi.Write(ctx, rw, http.StatusOK, convertGroup(group, users))
369+
httpapi.Write(ctx, rw, http.StatusOK, db2sdk.Group(group, users))
369370
}
370371

371372
// @Summary Get groups by organization
@@ -410,68 +411,8 @@ func (api *API) groups(rw http.ResponseWriter, r *http.Request) {
410411
return
411412
}
412413

413-
resp = append(resp, convertGroup(group, members))
414+
resp = append(resp, db2sdk.Group(group, members))
414415
}
415416

416417
httpapi.Write(ctx, rw, http.StatusOK, resp)
417418
}
418-
419-
func convertGroup(g database.Group, users []database.User) codersdk.Group {
420-
// It's ridiculous to query all the orgs of a user here
421-
// especially since as of the writing of this comment there
422-
// is only one org. So we pretend everyone is only part of
423-
// the group's organization.
424-
orgs := make(map[uuid.UUID][]uuid.UUID)
425-
for _, user := range users {
426-
orgs[user.ID] = []uuid.UUID{g.OrganizationID}
427-
}
428-
429-
return codersdk.Group{
430-
ID: g.ID,
431-
Name: g.Name,
432-
DisplayName: g.DisplayName,
433-
OrganizationID: g.OrganizationID,
434-
AvatarURL: g.AvatarURL,
435-
QuotaAllowance: int(g.QuotaAllowance),
436-
Members: convertUsers(users, orgs),
437-
Source: codersdk.GroupSource(g.Source),
438-
}
439-
}
440-
441-
func convertUser(user database.User, organizationIDs []uuid.UUID) codersdk.User {
442-
convertedUser := codersdk.User{
443-
ID: user.ID,
444-
Email: user.Email,
445-
CreatedAt: user.CreatedAt,
446-
LastSeenAt: user.LastSeenAt,
447-
Username: user.Username,
448-
Status: codersdk.UserStatus(user.Status),
449-
OrganizationIDs: organizationIDs,
450-
Roles: make([]codersdk.Role, 0, len(user.RBACRoles)),
451-
AvatarURL: user.AvatarURL,
452-
LoginType: codersdk.LoginType(user.LoginType),
453-
}
454-
455-
for _, roleName := range user.RBACRoles {
456-
rbacRole, _ := rbac.RoleByName(roleName)
457-
convertedUser.Roles = append(convertedUser.Roles, convertRole(rbacRole))
458-
}
459-
460-
return convertedUser
461-
}
462-
463-
func convertUsers(users []database.User, organizationIDsByUserID map[uuid.UUID][]uuid.UUID) []codersdk.User {
464-
converted := make([]codersdk.User, 0, len(users))
465-
for _, u := range users {
466-
userOrganizationIDs := organizationIDsByUserID[u.ID]
467-
converted = append(converted, convertUser(u, userOrganizationIDs))
468-
}
469-
return converted
470-
}
471-
472-
func convertRole(role rbac.Role) codersdk.Role {
473-
return codersdk.Role{
474-
DisplayName: role.DisplayName,
475-
Name: role.Name,
476-
}
477-
}

enterprise/coderd/templates.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111

1212
"github.com/coder/coder/v2/coderd/audit"
1313
"github.com/coder/coder/v2/coderd/database"
14+
"github.com/coder/coder/v2/coderd/database/db2sdk"
1415
"github.com/coder/coder/v2/coderd/database/dbauthz"
1516
"github.com/coder/coder/v2/coderd/httpapi"
1617
"github.com/coder/coder/v2/coderd/httpmw"
@@ -64,15 +65,14 @@ func (api *API) templateAvailablePermissions(rw http.ResponseWriter, r *http.Req
6465
return
6566
}
6667

67-
sdkGroups = append(sdkGroups, convertGroup(group, members))
68+
sdkGroups = append(sdkGroups, db2sdk.Group(group, members))
6869
}
6970

7071
httpapi.Write(ctx, rw, http.StatusOK, codersdk.ACLAvailable{
71-
// No need to pass organization info here.
7272
// TODO: @emyrk we should return a MinimalUser here instead of a full user.
7373
// The FE requires the `email` field, so this cannot be done without
7474
// a UI change.
75-
Users: convertUsers(users, map[uuid.UUID][]uuid.UUID{}),
75+
Users: db2sdk.ReducedUsers(users),
7676
Groups: sdkGroups,
7777
})
7878
}
@@ -134,7 +134,7 @@ func (api *API) templateACL(rw http.ResponseWriter, r *http.Request) {
134134
return
135135
}
136136
groups = append(groups, codersdk.TemplateGroup{
137-
Group: convertGroup(group.Group, members),
137+
Group: db2sdk.Group(group.Group, members),
138138
Role: convertToTemplateRole(group.Actions),
139139
})
140140
}
@@ -287,7 +287,7 @@ func convertTemplateUsers(tus []database.TemplateUser, orgIDsByUserIDs map[uuid.
287287

288288
for _, tu := range tus {
289289
users = append(users, codersdk.TemplateUser{
290-
User: convertUser(tu.User, orgIDsByUserIDs[tu.User.ID]),
290+
User: db2sdk.User(tu.User, orgIDsByUserIDs[tu.User.ID]),
291291
Role: convertToTemplateRole(tu.Actions),
292292
})
293293
}

enterprise/coderd/userauth_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -668,7 +668,7 @@ func TestGroupSync(t *testing.T) {
668668
}
669669

670670
for _, group := range orgGroups {
671-
userInGroup := slice.ContainsCompare(group.Members, codersdk.User{Email: user.Email}, func(a, b codersdk.User) bool {
671+
userInGroup := slice.ContainsCompare(group.Members, codersdk.ReducedUser{Email: user.Email}, func(a, b codersdk.ReducedUser) bool {
672672
return a.Email == b.Email
673673
})
674674
if group.IsEveryone() {

0 commit comments

Comments
 (0)