Skip to content

Commit 6acba0e

Browse files
committed
Merge branch 'main' into create-user/presleyp/734
2 parents 4c56afd + 816441e commit 6acba0e

File tree

10 files changed

+346
-63
lines changed

10 files changed

+346
-63
lines changed

coderd/database/databasefake/databasefake.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -709,6 +709,29 @@ func (q *fakeQuerier) GetOrganizationMemberByUserID(_ context.Context, arg datab
709709
return database.OrganizationMember{}, sql.ErrNoRows
710710
}
711711

712+
func (q *fakeQuerier) GetOrganizationIDsByMemberIDs(_ context.Context, ids []uuid.UUID) ([]database.GetOrganizationIDsByMemberIDsRow, error) {
713+
q.mutex.RLock()
714+
defer q.mutex.RUnlock()
715+
716+
getOrganizationIDsByMemberIDRows := make([]database.GetOrganizationIDsByMemberIDsRow, 0, len(ids))
717+
for _, userID := range ids {
718+
userOrganizationIDs := make([]uuid.UUID, 0)
719+
for _, membership := range q.organizationMembers {
720+
if membership.UserID == userID {
721+
userOrganizationIDs = append(userOrganizationIDs, membership.OrganizationID)
722+
}
723+
}
724+
getOrganizationIDsByMemberIDRows = append(getOrganizationIDsByMemberIDRows, database.GetOrganizationIDsByMemberIDsRow{
725+
UserID: userID,
726+
OrganizationIDs: userOrganizationIDs,
727+
})
728+
}
729+
if len(getOrganizationIDsByMemberIDRows) == 0 {
730+
return nil, sql.ErrNoRows
731+
}
732+
return getOrganizationIDsByMemberIDRows, nil
733+
}
734+
712735
func (q *fakeQuerier) GetProvisionerDaemons(_ context.Context) ([]database.ProvisionerDaemon, error) {
713736
q.mutex.RLock()
714737
defer q.mutex.RUnlock()

coderd/database/querier.go

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/database/queries.sql.go

Lines changed: 39 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/database/queries/organizationmembers.sql

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,13 @@ INSERT INTO
2020
)
2121
VALUES
2222
($1, $2, $3, $4, $5) RETURNING *;
23+
24+
-- name: GetOrganizationIDsByMemberIDs :many
25+
SELECT
26+
user_id, array_agg(organization_id) :: uuid [ ] AS "organization_IDs"
27+
FROM
28+
organization_members
29+
WHERE
30+
user_id = ANY(@ids :: uuid [ ])
31+
GROUP BY
32+
user_id;

coderd/users.go

Lines changed: 70 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -137,16 +137,34 @@ func (api *api) users(rw http.ResponseWriter, r *http.Request) {
137137
LimitOpt: int32(pageLimit),
138138
Search: searchName,
139139
})
140+
if err != nil {
141+
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
142+
Message: err.Error(),
143+
})
144+
return
145+
}
140146

147+
userIDs := make([]uuid.UUID, 0, len(users))
148+
for _, user := range users {
149+
userIDs = append(userIDs, user.ID)
150+
}
151+
organizationIDsByMemberIDsRows, err := api.Database.GetOrganizationIDsByMemberIDs(r.Context(), userIDs)
152+
if xerrors.Is(err, sql.ErrNoRows) {
153+
err = nil
154+
}
141155
if err != nil {
142156
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
143157
Message: err.Error(),
144158
})
145159
return
146160
}
161+
organizationIDsByUserID := map[uuid.UUID][]uuid.UUID{}
162+
for _, organizationIDsByMemberIDsRow := range organizationIDsByMemberIDsRows {
163+
organizationIDsByUserID[organizationIDsByMemberIDsRow.UserID] = organizationIDsByMemberIDsRow.OrganizationIDs
164+
}
147165

148166
render.Status(r, http.StatusOK)
149-
render.JSON(rw, r, convertUsers(users))
167+
render.JSON(rw, r, convertUsers(users, organizationIDsByUserID))
150168
}
151169

152170
// Creates a new user.
@@ -213,15 +231,23 @@ func (api *api) postUser(rw http.ResponseWriter, r *http.Request) {
213231
return
214232
}
215233

216-
httpapi.Write(rw, http.StatusCreated, convertUser(user))
234+
httpapi.Write(rw, http.StatusCreated, convertUser(user, []uuid.UUID{createUser.OrganizationID}))
217235
}
218236

219237
// Returns the parameterized user requested. All validation
220238
// is completed in the middleware for this route.
221-
func (*api) userByName(rw http.ResponseWriter, r *http.Request) {
239+
func (api *api) userByName(rw http.ResponseWriter, r *http.Request) {
222240
user := httpmw.UserParam(r)
241+
organizationIDs, err := userOrganizationIDs(r.Context(), api, user)
242+
243+
if err != nil {
244+
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
245+
Message: fmt.Sprintf("get organization IDs: %s", err.Error()),
246+
})
247+
return
248+
}
223249

224-
httpapi.Write(rw, http.StatusOK, convertUser(user))
250+
httpapi.Write(rw, http.StatusOK, convertUser(user, organizationIDs))
225251
}
226252

227253
func (api *api) putUserProfile(rw http.ResponseWriter, r *http.Request) {
@@ -278,7 +304,15 @@ func (api *api) putUserProfile(rw http.ResponseWriter, r *http.Request) {
278304
return
279305
}
280306

281-
httpapi.Write(rw, http.StatusOK, convertUser(updatedUserProfile))
307+
organizationIDs, err := userOrganizationIDs(r.Context(), api, user)
308+
if err != nil {
309+
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
310+
Message: fmt.Sprintf("get organization IDs: %s", err.Error()),
311+
})
312+
return
313+
}
314+
315+
httpapi.Write(rw, http.StatusOK, convertUser(updatedUserProfile, organizationIDs))
282316
}
283317

284318
func (api *api) putUserSuspend(rw http.ResponseWriter, r *http.Request) {
@@ -297,7 +331,15 @@ func (api *api) putUserSuspend(rw http.ResponseWriter, r *http.Request) {
297331
return
298332
}
299333

300-
httpapi.Write(rw, http.StatusOK, convertUser(suspendedUser))
334+
organizations, err := userOrganizationIDs(r.Context(), api, user)
335+
if err != nil {
336+
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
337+
Message: fmt.Sprintf("get organization IDs: %s", err.Error()),
338+
})
339+
return
340+
}
341+
342+
httpapi.Write(rw, http.StatusOK, convertUser(suspendedUser, organizations))
301343
}
302344

303345
// Returns organizations the parameterized user has access to.
@@ -626,20 +668,34 @@ func (api *api) createUser(ctx context.Context, req codersdk.CreateUserRequest)
626668
})
627669
}
628670

629-
func convertUser(user database.User) codersdk.User {
671+
func convertUser(user database.User, organizationIDs []uuid.UUID) codersdk.User {
630672
return codersdk.User{
631-
ID: user.ID,
632-
Email: user.Email,
633-
CreatedAt: user.CreatedAt,
634-
Username: user.Username,
635-
Status: codersdk.UserStatus(user.Status),
673+
ID: user.ID,
674+
Email: user.Email,
675+
CreatedAt: user.CreatedAt,
676+
Username: user.Username,
677+
Status: codersdk.UserStatus(user.Status),
678+
OrganizationIDs: organizationIDs,
636679
}
637680
}
638681

639-
func convertUsers(users []database.User) []codersdk.User {
682+
func convertUsers(users []database.User, organizationIDsByUserID map[uuid.UUID][]uuid.UUID) []codersdk.User {
640683
converted := make([]codersdk.User, 0, len(users))
641684
for _, u := range users {
642-
converted = append(converted, convertUser(u))
685+
userOrganizationIDs := organizationIDsByUserID[u.ID]
686+
converted = append(converted, convertUser(u, userOrganizationIDs))
643687
}
644688
return converted
645689
}
690+
691+
func userOrganizationIDs(ctx context.Context, api *api, user database.User) ([]uuid.UUID, error) {
692+
organizationIDsByMemberIDsRows, err := api.Database.GetOrganizationIDsByMemberIDs(ctx, []uuid.UUID{user.ID})
693+
if errors.Is(err, sql.ErrNoRows) || len(organizationIDsByMemberIDsRows) == 0 {
694+
return []uuid.UUID{}, nil
695+
}
696+
if err != nil {
697+
return []uuid.UUID{}, err
698+
}
699+
member := organizationIDsByMemberIDsRows[0]
700+
return member.OrganizationIDs, nil
701+
}

coderd/users_test.go

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -321,9 +321,11 @@ func TestPutUserSuspend(t *testing.T) {
321321
func TestUserByName(t *testing.T) {
322322
t.Parallel()
323323
client := coderdtest.New(t, nil)
324-
_ = coderdtest.CreateFirstUser(t, client)
325-
_, err := client.User(context.Background(), codersdk.Me)
324+
firstUser := coderdtest.CreateFirstUser(t, client)
325+
user, err := client.User(context.Background(), codersdk.Me)
326+
326327
require.NoError(t, err)
328+
require.Equal(t, firstUser.OrganizationID, user.OrganizationIDs[0])
327329
}
328330

329331
func TestGetUsers(t *testing.T) {
@@ -340,6 +342,7 @@ func TestGetUsers(t *testing.T) {
340342
users, err := client.Users(context.Background(), codersdk.UsersRequest{})
341343
require.NoError(t, err)
342344
require.Len(t, users, 2)
345+
require.Len(t, users[0].OrganizationIDs, 1)
343346
}
344347

345348
func TestOrganizationsByUser(t *testing.T) {
@@ -451,14 +454,12 @@ func TestPaginatedUsers(t *testing.T) {
451454
coderdtest.CreateFirstUser(t, client)
452455
me, err := client.User(context.Background(), codersdk.Me)
453456
require.NoError(t, err)
457+
orgID := me.OrganizationIDs[0]
454458

455459
allUsers := make([]codersdk.User, 0)
456460
allUsers = append(allUsers, me)
457461
specialUsers := make([]codersdk.User, 0)
458462

459-
org, err := client.CreateOrganization(ctx, me.ID, codersdk.CreateOrganizationRequest{
460-
Name: "default",
461-
})
462463
require.NoError(t, err)
463464

464465
// When 100 users exist
@@ -481,7 +482,7 @@ func TestPaginatedUsers(t *testing.T) {
481482
Email: email,
482483
Username: username,
483484
Password: "password",
484-
OrganizationID: org.ID,
485+
OrganizationID: orgID,
485486
})
486487
require.NoError(t, err)
487488
allUsers = append(allUsers, newUser)

codersdk/users.go

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,12 @@ const (
3737

3838
// User represents a user in Coder.
3939
type User struct {
40-
ID uuid.UUID `json:"id" validate:"required"`
41-
Email string `json:"email" validate:"required"`
42-
CreatedAt time.Time `json:"created_at" validate:"required"`
43-
Username string `json:"username" validate:"required"`
44-
Status UserStatus `json:"status"`
40+
ID uuid.UUID `json:"id" validate:"required"`
41+
Email string `json:"email" validate:"required"`
42+
CreatedAt time.Time `json:"created_at" validate:"required"`
43+
Username string `json:"username" validate:"required"`
44+
Status UserStatus `json:"status"`
45+
OrganizationIDs []uuid.UUID `json:"organization_ids"`
4546
}
4647

4748
type CreateFirstUserRequest struct {

go.mod

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ require (
6363
github.com/go-playground/validator/v10 v10.10.1
6464
github.com/gohugoio/hugo v0.97.3
6565
github.com/golang-jwt/jwt v3.2.2+incompatible
66-
github.com/golang-migrate/migrate/v4 v4.15.1
66+
github.com/golang-migrate/migrate/v4 v4.15.2
6767
github.com/google/go-github/v43 v43.0.1-0.20220414155304-00e42332e405
6868
github.com/google/uuid v1.3.0
6969
github.com/hashicorp/go-syslog v1.0.0
@@ -143,10 +143,8 @@ require (
143143
github.com/coreos/go-oidc v2.2.1+incompatible // indirect
144144
github.com/davecgh/go-spew v1.1.1 // indirect
145145
github.com/dgraph-io/ristretto v0.1.0 // indirect
146-
github.com/dhui/dktest v0.3.9 // indirect
147146
github.com/dlclark/regexp2 v1.4.0 // indirect
148147
github.com/docker/cli v20.10.13+incompatible // indirect
149-
github.com/docker/distribution v2.8.0+incompatible // indirect
150148
github.com/docker/docker v20.10.13+incompatible // indirect
151149
github.com/docker/go-connections v0.4.0 // indirect
152150
github.com/docker/go-units v0.4.0 // indirect
@@ -230,7 +228,7 @@ require (
230228
github.com/zclconf/go-cty v1.10.0 // indirect
231229
github.com/zeebo/errs v1.2.2 // indirect
232230
go.opencensus.io v0.23.0 // indirect
233-
golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11 // indirect
231+
golang.org/x/time v0.0.0-20220224211638-0e9765cccd65 // indirect
234232
google.golang.org/appengine v1.6.7 // indirect
235233
google.golang.org/genproto v0.0.0-20220421151946-72621c1f0bd3 // indirect
236234
google.golang.org/grpc v1.45.0 // indirect

0 commit comments

Comments
 (0)