From 494aca28b38ee965e32a2205bb072b5e2d939e5c Mon Sep 17 00:00:00 2001 From: Bruno Date: Tue, 26 Apr 2022 17:06:17 +0000 Subject: [PATCH 1/7] feat: add organization_ids in the user(s) response --- coderd/users.go | 66 ++++++++++++++++++++++++++++++++++---------- coderd/users_test.go | 7 +++-- codersdk/users.go | 11 ++++---- 3 files changed, 62 insertions(+), 22 deletions(-) diff --git a/coderd/users.go b/coderd/users.go index a38af8ba12f63..1b0ba8cd371ba 100644 --- a/coderd/users.go +++ b/coderd/users.go @@ -24,6 +24,8 @@ import ( "github.com/coder/coder/cryptorand" ) +type OrganizationsByUserId = map[string][]database.Organization + // Returns whether the initial user has been created or not. func (api *api) firstUser(rw http.ResponseWriter, r *http.Request) { userCount, err := api.Database.GetUserCount(r.Context()) @@ -145,8 +147,14 @@ func (api *api) users(rw http.ResponseWriter, r *http.Request) { return } + organizationsByUserId := OrganizationsByUserId{} + for _, user := range users { + userOrganizations := getUserOrganizations(api, rw, r, user) + organizationsByUserId[user.ID.String()] = userOrganizations + } + render.Status(r, http.StatusOK) - render.JSON(rw, r, convertUsers(users)) + render.JSON(rw, r, convertUsers(users, organizationsByUserId)) } // Creates a new user. @@ -213,15 +221,17 @@ func (api *api) postUser(rw http.ResponseWriter, r *http.Request) { return } - httpapi.Write(rw, http.StatusCreated, convertUser(user)) + organizations := getUserOrganizations(api, rw, r, user) + + httpapi.Write(rw, http.StatusCreated, convertUser(user, organizations)) } // Returns the parameterized user requested. All validation // is completed in the middleware for this route. -func (*api) userByName(rw http.ResponseWriter, r *http.Request) { +func (api *api) userByName(rw http.ResponseWriter, r *http.Request) { user := httpmw.UserParam(r) - - httpapi.Write(rw, http.StatusOK, convertUser(user)) + organizations := getUserOrganizations(api, rw, r, user) + httpapi.Write(rw, http.StatusOK, convertUser(user, organizations)) } func (api *api) putUserProfile(rw http.ResponseWriter, r *http.Request) { @@ -278,7 +288,9 @@ func (api *api) putUserProfile(rw http.ResponseWriter, r *http.Request) { return } - httpapi.Write(rw, http.StatusOK, convertUser(updatedUserProfile)) + organizations := getUserOrganizations(api, rw, r, user) + + httpapi.Write(rw, http.StatusOK, convertUser(updatedUserProfile, organizations)) } func (api *api) putUserSuspend(rw http.ResponseWriter, r *http.Request) { @@ -297,7 +309,9 @@ func (api *api) putUserSuspend(rw http.ResponseWriter, r *http.Request) { return } - httpapi.Write(rw, http.StatusOK, convertUser(suspendedUser)) + organizations := getUserOrganizations(api, rw, r, user) + + httpapi.Write(rw, http.StatusOK, convertUser(suspendedUser, organizations)) } // Returns organizations the parameterized user has access to. @@ -626,20 +640,42 @@ func (api *api) createUser(ctx context.Context, req codersdk.CreateUserRequest) }) } -func convertUser(user database.User) codersdk.User { +func convertUser(user database.User, organizations []database.Organization) codersdk.User { + orgIds := make([]uuid.UUID, 0, len(organizations)) + for _, o := range organizations { + orgIds = append(orgIds, o.ID) + } + return codersdk.User{ - ID: user.ID, - Email: user.Email, - CreatedAt: user.CreatedAt, - Username: user.Username, - Status: codersdk.UserStatus(user.Status), + ID: user.ID, + Email: user.Email, + CreatedAt: user.CreatedAt, + Username: user.Username, + Status: codersdk.UserStatus(user.Status), + OrganizationIds: orgIds, } } -func convertUsers(users []database.User) []codersdk.User { +func convertUsers(users []database.User, organizationsByUserId OrganizationsByUserId) []codersdk.User { converted := make([]codersdk.User, 0, len(users)) for _, u := range users { - converted = append(converted, convertUser(u)) + userOrganizations := organizationsByUserId[u.ID.String()] + converted = append(converted, convertUser(u, userOrganizations)) } return converted } + +func getUserOrganizations(api *api, rw http.ResponseWriter, r *http.Request, user database.User) []database.Organization { + organizations, err := api.Database.GetOrganizationsByUserID(r.Context(), user.ID) + if errors.Is(err, sql.ErrNoRows) { + err = nil + organizations = []database.Organization{} + } + if err != nil { + httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + Message: fmt.Sprintf("get organizations: %s", err.Error()), + }) + return []database.Organization{} + } + return organizations +} diff --git a/coderd/users_test.go b/coderd/users_test.go index 8ee4db1c6f80e..b22aa8ffa2664 100644 --- a/coderd/users_test.go +++ b/coderd/users_test.go @@ -321,9 +321,11 @@ func TestPutUserSuspend(t *testing.T) { func TestUserByName(t *testing.T) { t.Parallel() client := coderdtest.New(t, nil) - _ = coderdtest.CreateFirstUser(t, client) - _, err := client.User(context.Background(), codersdk.Me) + firstUser := coderdtest.CreateFirstUser(t, client) + user, err := client.User(context.Background(), codersdk.Me) + require.NoError(t, err) + require.Equal(t, firstUser.OrganizationID, user.OrganizationIds[0]) } func TestGetUsers(t *testing.T) { @@ -340,6 +342,7 @@ func TestGetUsers(t *testing.T) { users, err := client.Users(context.Background(), codersdk.UsersRequest{}) require.NoError(t, err) require.Len(t, users, 2) + require.Len(t, users[0].OrganizationIds, 1) } func TestOrganizationsByUser(t *testing.T) { diff --git a/codersdk/users.go b/codersdk/users.go index 38506f03cd192..68b4275c40333 100644 --- a/codersdk/users.go +++ b/codersdk/users.go @@ -37,11 +37,12 @@ const ( // User represents a user in Coder. type User struct { - ID uuid.UUID `json:"id" validate:"required"` - Email string `json:"email" validate:"required"` - CreatedAt time.Time `json:"created_at" validate:"required"` - Username string `json:"username" validate:"required"` - Status UserStatus `json:"status"` + ID uuid.UUID `json:"id" validate:"required"` + Email string `json:"email" validate:"required"` + CreatedAt time.Time `json:"created_at" validate:"required"` + Username string `json:"username" validate:"required"` + Status UserStatus `json:"status"` + OrganizationIds []uuid.UUID `json:"organization_ids"` } type CreateFirstUserRequest struct { From 0cfa1cb752e2042d212c615eaac8221173ae9663 Mon Sep 17 00:00:00 2001 From: Bruno Date: Tue, 26 Apr 2022 17:13:09 +0000 Subject: [PATCH 2/7] fix: update user endpoint types --- site/src/api/typesGenerated.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/site/src/api/typesGenerated.ts b/site/src/api/typesGenerated.ts index 268d1f837c347..10acc3acbc4fb 100644 --- a/site/src/api/typesGenerated.ts +++ b/site/src/api/typesGenerated.ts @@ -90,7 +90,7 @@ export interface User { readonly status: UserStatus } -// From codersdk/users.go:47:6. +// From codersdk/users.go:48:6. export interface CreateFirstUserRequest { readonly email: string readonly username: string @@ -98,41 +98,41 @@ export interface CreateFirstUserRequest { readonly organization: string } -// From codersdk/users.go:60:6. +// From codersdk/users.go:61:6. export interface CreateUserRequest { readonly email: string readonly username: string readonly password: string } -// From codersdk/users.go:67:6. +// From codersdk/users.go:68:6. export interface UpdateUserProfileRequest { readonly email: string readonly username: string } -// From codersdk/users.go:73:6. +// From codersdk/users.go:74:6. export interface LoginWithPasswordRequest { readonly email: string readonly password: string } -// From codersdk/users.go:79:6. +// From codersdk/users.go:80:6. export interface LoginWithPasswordResponse { readonly session_token: string } -// From codersdk/users.go:84:6. +// From codersdk/users.go:85:6. export interface GenerateAPIKeyResponse { readonly key: string } -// From codersdk/users.go:88:6. +// From codersdk/users.go:89:6. export interface CreateOrganizationRequest { readonly name: string } -// From codersdk/users.go:93:6. +// From codersdk/users.go:94:6. export interface AuthMethods { readonly password: boolean readonly github: boolean From a39ba22a2dc33612235636056702432563fb4ece Mon Sep 17 00:00:00 2001 From: Bruno Date: Tue, 26 Apr 2022 18:08:16 +0000 Subject: [PATCH 3/7] style: apply code conventions --- coderd/users.go | 20 +++++++++----------- coderd/users_test.go | 4 ++-- codersdk/users.go | 2 +- 3 files changed, 12 insertions(+), 14 deletions(-) diff --git a/coderd/users.go b/coderd/users.go index 1b0ba8cd371ba..d77163adec94b 100644 --- a/coderd/users.go +++ b/coderd/users.go @@ -24,8 +24,6 @@ import ( "github.com/coder/coder/cryptorand" ) -type OrganizationsByUserId = map[string][]database.Organization - // Returns whether the initial user has been created or not. func (api *api) firstUser(rw http.ResponseWriter, r *http.Request) { userCount, err := api.Database.GetUserCount(r.Context()) @@ -147,9 +145,9 @@ func (api *api) users(rw http.ResponseWriter, r *http.Request) { return } - organizationsByUserId := OrganizationsByUserId{} + organizationsByUserId := map[string][]database.Organization{} for _, user := range users { - userOrganizations := getUserOrganizations(api, rw, r, user) + userOrganizations := userOrganizations(api, rw, r, user) organizationsByUserId[user.ID.String()] = userOrganizations } @@ -221,7 +219,7 @@ func (api *api) postUser(rw http.ResponseWriter, r *http.Request) { return } - organizations := getUserOrganizations(api, rw, r, user) + organizations := userOrganizations(api, rw, r, user) httpapi.Write(rw, http.StatusCreated, convertUser(user, organizations)) } @@ -230,7 +228,7 @@ func (api *api) postUser(rw http.ResponseWriter, r *http.Request) { // is completed in the middleware for this route. func (api *api) userByName(rw http.ResponseWriter, r *http.Request) { user := httpmw.UserParam(r) - organizations := getUserOrganizations(api, rw, r, user) + organizations := userOrganizations(api, rw, r, user) httpapi.Write(rw, http.StatusOK, convertUser(user, organizations)) } @@ -288,7 +286,7 @@ func (api *api) putUserProfile(rw http.ResponseWriter, r *http.Request) { return } - organizations := getUserOrganizations(api, rw, r, user) + organizations := userOrganizations(api, rw, r, user) httpapi.Write(rw, http.StatusOK, convertUser(updatedUserProfile, organizations)) } @@ -309,7 +307,7 @@ func (api *api) putUserSuspend(rw http.ResponseWriter, r *http.Request) { return } - organizations := getUserOrganizations(api, rw, r, user) + organizations := userOrganizations(api, rw, r, user) httpapi.Write(rw, http.StatusOK, convertUser(suspendedUser, organizations)) } @@ -652,11 +650,11 @@ func convertUser(user database.User, organizations []database.Organization) code CreatedAt: user.CreatedAt, Username: user.Username, Status: codersdk.UserStatus(user.Status), - OrganizationIds: orgIds, + OrganizationIDs: orgIds, } } -func convertUsers(users []database.User, organizationsByUserId OrganizationsByUserId) []codersdk.User { +func convertUsers(users []database.User, organizationsByUserId map[string][]database.Organization) []codersdk.User { converted := make([]codersdk.User, 0, len(users)) for _, u := range users { userOrganizations := organizationsByUserId[u.ID.String()] @@ -665,7 +663,7 @@ func convertUsers(users []database.User, organizationsByUserId OrganizationsByUs return converted } -func getUserOrganizations(api *api, rw http.ResponseWriter, r *http.Request, user database.User) []database.Organization { +func userOrganizations(api *api, rw http.ResponseWriter, r *http.Request, user database.User) []database.Organization { organizations, err := api.Database.GetOrganizationsByUserID(r.Context(), user.ID) if errors.Is(err, sql.ErrNoRows) { err = nil diff --git a/coderd/users_test.go b/coderd/users_test.go index b22aa8ffa2664..9e41670155989 100644 --- a/coderd/users_test.go +++ b/coderd/users_test.go @@ -325,7 +325,7 @@ func TestUserByName(t *testing.T) { user, err := client.User(context.Background(), codersdk.Me) require.NoError(t, err) - require.Equal(t, firstUser.OrganizationID, user.OrganizationIds[0]) + require.Equal(t, firstUser.OrganizationID, user.OrganizationIDs[0]) } func TestGetUsers(t *testing.T) { @@ -342,7 +342,7 @@ func TestGetUsers(t *testing.T) { users, err := client.Users(context.Background(), codersdk.UsersRequest{}) require.NoError(t, err) require.Len(t, users, 2) - require.Len(t, users[0].OrganizationIds, 1) + require.Len(t, users[0].OrganizationIDs, 1) } func TestOrganizationsByUser(t *testing.T) { diff --git a/codersdk/users.go b/codersdk/users.go index 68b4275c40333..317ab7cd0f85b 100644 --- a/codersdk/users.go +++ b/codersdk/users.go @@ -42,7 +42,7 @@ type User struct { CreatedAt time.Time `json:"created_at" validate:"required"` Username string `json:"username" validate:"required"` Status UserStatus `json:"status"` - OrganizationIds []uuid.UUID `json:"organization_ids"` + OrganizationIDs []uuid.UUID `json:"organization_ids"` } type CreateFirstUserRequest struct { From 5583b78bd2e5e6d19a769b09730329f9aeae5a9e Mon Sep 17 00:00:00 2001 From: Bruno Date: Wed, 27 Apr 2022 17:05:13 +0000 Subject: [PATCH 4/7] refactor: update query to return org IDs fby user IDs --- coderd/database/databasefake/databasefake.go | 20 +++++ coderd/database/querier.go | 1 + coderd/database/queries.sql.go | 39 +++++++++ .../database/queries/organizationmembers.sql | 10 +++ coderd/users.go | 85 ++++++++++++------- 5 files changed, 122 insertions(+), 33 deletions(-) diff --git a/coderd/database/databasefake/databasefake.go b/coderd/database/databasefake/databasefake.go index b48ede661296c..899c490164289 100644 --- a/coderd/database/databasefake/databasefake.go +++ b/coderd/database/databasefake/databasefake.go @@ -709,6 +709,26 @@ func (q *fakeQuerier) GetOrganizationMemberByUserID(_ context.Context, arg datab return database.OrganizationMember{}, sql.ErrNoRows } +func (q *fakeQuerier) GetOrganizationIDsByMemberIDs(c context.Context, ids []uuid.UUID) ([]database.GetOrganizationIDsByMemberIDsRow, error) { + q.mutex.RLock() + defer q.mutex.RUnlock() + + getOrganizationIDsByMemberIDRows := make([]database.GetOrganizationIDsByMemberIDsRow, 0, len(ids)) + for _, userID := range ids { + userOrganizationIDs := make([]uuid.UUID, 0) + for _, membership := range q.organizationMembers { + if membership.UserID == userID { + userOrganizationIDs = append(userOrganizationIDs, membership.OrganizationID) + } + } + getOrganizationIDsByMemberIDRows = append(getOrganizationIDsByMemberIDRows, database.GetOrganizationIDsByMemberIDsRow{ + UserID: userID, + OrganizationIDs: userOrganizationIDs, + }) + } + return getOrganizationIDsByMemberIDRows, sql.ErrNoRows +} + func (q *fakeQuerier) GetProvisionerDaemons(_ context.Context) ([]database.ProvisionerDaemon, error) { q.mutex.RLock() defer q.mutex.RUnlock() diff --git a/coderd/database/querier.go b/coderd/database/querier.go index 3819eb3608934..304e012e24e88 100644 --- a/coderd/database/querier.go +++ b/coderd/database/querier.go @@ -17,6 +17,7 @@ type querier interface { GetGitSSHKey(ctx context.Context, userID uuid.UUID) (GitSSHKey, error) GetOrganizationByID(ctx context.Context, id uuid.UUID) (Organization, error) GetOrganizationByName(ctx context.Context, name string) (Organization, error) + GetOrganizationIDsByMemberIDs(ctx context.Context, ids []uuid.UUID) ([]GetOrganizationIDsByMemberIDsRow, error) GetOrganizationMemberByUserID(ctx context.Context, arg GetOrganizationMemberByUserIDParams) (OrganizationMember, error) GetOrganizations(ctx context.Context) ([]Organization, error) GetOrganizationsByUserID(ctx context.Context, userID uuid.UUID) ([]Organization, error) diff --git a/coderd/database/queries.sql.go b/coderd/database/queries.sql.go index 5f0574812b79c..d80bb3135b6c1 100644 --- a/coderd/database/queries.sql.go +++ b/coderd/database/queries.sql.go @@ -306,6 +306,45 @@ func (q *sqlQuerier) UpdateGitSSHKey(ctx context.Context, arg UpdateGitSSHKeyPar return err } +const getOrganizationIDsByMemberIDs = `-- name: GetOrganizationIDsByMemberIDs :many +SELECT + user_id, array_agg(organization_id) :: uuid [ ] AS "organization_IDs" +FROM + organization_members +WHERE + user_id = ANY($1 :: uuid [ ]) +GROUP BY + user_id +` + +type GetOrganizationIDsByMemberIDsRow struct { + UserID uuid.UUID `db:"user_id" json:"user_id"` + OrganizationIDs []uuid.UUID `db:"organization_IDs" json:"organization_IDs"` +} + +func (q *sqlQuerier) GetOrganizationIDsByMemberIDs(ctx context.Context, ids []uuid.UUID) ([]GetOrganizationIDsByMemberIDsRow, error) { + rows, err := q.db.QueryContext(ctx, getOrganizationIDsByMemberIDs, pq.Array(ids)) + if err != nil { + return nil, err + } + defer rows.Close() + var items []GetOrganizationIDsByMemberIDsRow + for rows.Next() { + var i GetOrganizationIDsByMemberIDsRow + if err := rows.Scan(&i.UserID, pq.Array(&i.OrganizationIDs)); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + const getOrganizationMemberByUserID = `-- name: GetOrganizationMemberByUserID :one SELECT user_id, organization_id, created_at, updated_at, roles diff --git a/coderd/database/queries/organizationmembers.sql b/coderd/database/queries/organizationmembers.sql index 243bdc26c9878..27c41ed53f577 100644 --- a/coderd/database/queries/organizationmembers.sql +++ b/coderd/database/queries/organizationmembers.sql @@ -20,3 +20,13 @@ INSERT INTO ) VALUES ($1, $2, $3, $4, $5) RETURNING *; + +-- name: GetOrganizationIDsByMemberIDs :many +SELECT + user_id, array_agg(organization_id) :: uuid [ ] AS "organization_IDs" +FROM + organization_members +WHERE + user_id = ANY(@ids :: uuid [ ]) +GROUP BY + user_id; diff --git a/coderd/users.go b/coderd/users.go index d77163adec94b..c5aa2e69d47c8 100644 --- a/coderd/users.go +++ b/coderd/users.go @@ -137,7 +137,6 @@ func (api *api) users(rw http.ResponseWriter, r *http.Request) { LimitOpt: int32(pageLimit), Search: searchName, }) - if err != nil { httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ Message: err.Error(), @@ -145,14 +144,24 @@ func (api *api) users(rw http.ResponseWriter, r *http.Request) { return } - organizationsByUserId := map[string][]database.Organization{} + userIDs := make([]uuid.UUID, 0, len(users)) for _, user := range users { - userOrganizations := userOrganizations(api, rw, r, user) - organizationsByUserId[user.ID.String()] = userOrganizations + userIDs = append(userIDs, user.ID) + } + organizationIDsByMemberIDsRows, err := api.Database.GetOrganizationIDsByMemberIDs(r.Context(), userIDs) + if err != nil { + httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + Message: err.Error(), + }) + return + } + organizationIDsByUserID := map[uuid.UUID][]uuid.UUID{} + for _, organizationIDsByMemberIDsRow := range organizationIDsByMemberIDsRows { + organizationIDsByUserID[organizationIDsByMemberIDsRow.UserID] = organizationIDsByMemberIDsRow.OrganizationIDs } render.Status(r, http.StatusOK) - render.JSON(rw, r, convertUsers(users, organizationsByUserId)) + render.JSON(rw, r, convertUsers(users, organizationIDsByUserID)) } // Creates a new user. @@ -219,17 +228,23 @@ func (api *api) postUser(rw http.ResponseWriter, r *http.Request) { return } - organizations := userOrganizations(api, rw, r, user) - - httpapi.Write(rw, http.StatusCreated, convertUser(user, organizations)) + httpapi.Write(rw, http.StatusCreated, convertUser(user, []uuid.UUID{createUser.OrganizationID})) } // Returns the parameterized user requested. All validation // is completed in the middleware for this route. func (api *api) userByName(rw http.ResponseWriter, r *http.Request) { user := httpmw.UserParam(r) - organizations := userOrganizations(api, rw, r, user) - httpapi.Write(rw, http.StatusOK, convertUser(user, organizations)) + organizationIDs, err := userOrganizationIDs(api, rw, r, user) + + if err != nil { + httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + Message: fmt.Sprintf("get organization IDs: %s", err.Error()), + }) + return + } + + httpapi.Write(rw, http.StatusOK, convertUser(user, organizationIDs)) } func (api *api) putUserProfile(rw http.ResponseWriter, r *http.Request) { @@ -286,9 +301,15 @@ func (api *api) putUserProfile(rw http.ResponseWriter, r *http.Request) { return } - organizations := userOrganizations(api, rw, r, user) + organizationIDs, err := userOrganizationIDs(api, rw, r, user) + if err != nil { + httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + Message: fmt.Sprintf("get organization IDs: %s", err.Error()), + }) + return + } - httpapi.Write(rw, http.StatusOK, convertUser(updatedUserProfile, organizations)) + httpapi.Write(rw, http.StatusOK, convertUser(updatedUserProfile, organizationIDs)) } func (api *api) putUserSuspend(rw http.ResponseWriter, r *http.Request) { @@ -307,7 +328,13 @@ func (api *api) putUserSuspend(rw http.ResponseWriter, r *http.Request) { return } - organizations := userOrganizations(api, rw, r, user) + organizations, err := userOrganizationIDs(api, rw, r, user) + if err != nil { + httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + Message: fmt.Sprintf("get organization IDs: %s", err.Error()), + }) + return + } httpapi.Write(rw, http.StatusOK, convertUser(suspendedUser, organizations)) } @@ -638,42 +665,34 @@ func (api *api) createUser(ctx context.Context, req codersdk.CreateUserRequest) }) } -func convertUser(user database.User, organizations []database.Organization) codersdk.User { - orgIds := make([]uuid.UUID, 0, len(organizations)) - for _, o := range organizations { - orgIds = append(orgIds, o.ID) - } - +func convertUser(user database.User, organizationIDs []uuid.UUID) codersdk.User { return codersdk.User{ ID: user.ID, Email: user.Email, CreatedAt: user.CreatedAt, Username: user.Username, Status: codersdk.UserStatus(user.Status), - OrganizationIDs: orgIds, + OrganizationIDs: organizationIDs, } } -func convertUsers(users []database.User, organizationsByUserId map[string][]database.Organization) []codersdk.User { +func convertUsers(users []database.User, organizationIDsByUserID map[uuid.UUID][]uuid.UUID) []codersdk.User { converted := make([]codersdk.User, 0, len(users)) for _, u := range users { - userOrganizations := organizationsByUserId[u.ID.String()] - converted = append(converted, convertUser(u, userOrganizations)) + userOrganizationIDs := organizationIDsByUserID[u.ID] + converted = append(converted, convertUser(u, userOrganizationIDs)) } return converted } -func userOrganizations(api *api, rw http.ResponseWriter, r *http.Request, user database.User) []database.Organization { - organizations, err := api.Database.GetOrganizationsByUserID(r.Context(), user.ID) - if errors.Is(err, sql.ErrNoRows) { - err = nil - organizations = []database.Organization{} +func userOrganizationIDs(api *api, rw http.ResponseWriter, r *http.Request, user database.User) ([]uuid.UUID, error) { + organizationIDsByMemberIDsRows, err := api.Database.GetOrganizationIDsByMemberIDs(r.Context(), []uuid.UUID{user.ID}) + if errors.Is(err, sql.ErrNoRows) || len(organizationIDsByMemberIDsRows) == 0 { + return []uuid.UUID{}, nil } if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ - Message: fmt.Sprintf("get organizations: %s", err.Error()), - }) - return []database.Organization{} + return []uuid.UUID{}, err } - return organizations + member := organizationIDsByMemberIDsRows[0] + return member.OrganizationIDs, nil } From 14f9f0fabef49c328880aec4eb9e24f800f7460b Mon Sep 17 00:00:00 2001 From: Bruno Date: Wed, 27 Apr 2022 17:22:58 +0000 Subject: [PATCH 5/7] fix: fix lint --- coderd/database/databasefake/databasefake.go | 2 +- coderd/users.go | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/coderd/database/databasefake/databasefake.go b/coderd/database/databasefake/databasefake.go index 899c490164289..15e176c7569f2 100644 --- a/coderd/database/databasefake/databasefake.go +++ b/coderd/database/databasefake/databasefake.go @@ -709,7 +709,7 @@ func (q *fakeQuerier) GetOrganizationMemberByUserID(_ context.Context, arg datab return database.OrganizationMember{}, sql.ErrNoRows } -func (q *fakeQuerier) GetOrganizationIDsByMemberIDs(c context.Context, ids []uuid.UUID) ([]database.GetOrganizationIDsByMemberIDsRow, error) { +func (q *fakeQuerier) GetOrganizationIDsByMemberIDs(_ context.Context, ids []uuid.UUID) ([]database.GetOrganizationIDsByMemberIDsRow, error) { q.mutex.RLock() defer q.mutex.RUnlock() diff --git a/coderd/users.go b/coderd/users.go index c5aa2e69d47c8..b351f7164eb32 100644 --- a/coderd/users.go +++ b/coderd/users.go @@ -235,7 +235,7 @@ func (api *api) postUser(rw http.ResponseWriter, r *http.Request) { // is completed in the middleware for this route. func (api *api) userByName(rw http.ResponseWriter, r *http.Request) { user := httpmw.UserParam(r) - organizationIDs, err := userOrganizationIDs(api, rw, r, user) + organizationIDs, err := userOrganizationIDs(r.Context(), api, user) if err != nil { httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ @@ -301,7 +301,7 @@ func (api *api) putUserProfile(rw http.ResponseWriter, r *http.Request) { return } - organizationIDs, err := userOrganizationIDs(api, rw, r, user) + organizationIDs, err := userOrganizationIDs(r.Context(), api, user) if err != nil { httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ Message: fmt.Sprintf("get organization IDs: %s", err.Error()), @@ -328,7 +328,7 @@ func (api *api) putUserSuspend(rw http.ResponseWriter, r *http.Request) { return } - organizations, err := userOrganizationIDs(api, rw, r, user) + organizations, err := userOrganizationIDs(r.Context(), api, user) if err != nil { httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ Message: fmt.Sprintf("get organization IDs: %s", err.Error()), @@ -685,8 +685,8 @@ func convertUsers(users []database.User, organizationIDsByUserID map[uuid.UUID][ return converted } -func userOrganizationIDs(api *api, rw http.ResponseWriter, r *http.Request, user database.User) ([]uuid.UUID, error) { - organizationIDsByMemberIDsRows, err := api.Database.GetOrganizationIDsByMemberIDs(r.Context(), []uuid.UUID{user.ID}) +func userOrganizationIDs(ctx context.Context, api *api, user database.User) ([]uuid.UUID, error) { + organizationIDsByMemberIDsRows, err := api.Database.GetOrganizationIDsByMemberIDs(ctx, []uuid.UUID{user.ID}) if errors.Is(err, sql.ErrNoRows) || len(organizationIDsByMemberIDsRows) == 0 { return []uuid.UUID{}, nil } From fe7fe14248970c5ef7d567ad9fbaf7c8e1f5795f Mon Sep 17 00:00:00 2001 From: Bruno Date: Wed, 27 Apr 2022 17:35:10 +0000 Subject: [PATCH 6/7] fix: fix GetOrganizationIDsByMemberIDs on database fake --- coderd/database/databasefake/databasefake.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/coderd/database/databasefake/databasefake.go b/coderd/database/databasefake/databasefake.go index 15e176c7569f2..746fe45d6f3b0 100644 --- a/coderd/database/databasefake/databasefake.go +++ b/coderd/database/databasefake/databasefake.go @@ -726,7 +726,10 @@ func (q *fakeQuerier) GetOrganizationIDsByMemberIDs(_ context.Context, ids []uui OrganizationIDs: userOrganizationIDs, }) } - return getOrganizationIDsByMemberIDRows, sql.ErrNoRows + if len(getOrganizationIDsByMemberIDRows) == 0 { + return nil, sql.ErrNoRows + } + return getOrganizationIDsByMemberIDRows, nil } func (q *fakeQuerier) GetProvisionerDaemons(_ context.Context) ([]database.ProvisionerDaemon, error) { From e088a0e500d297c75c0e815aebbcb29f73c0a293 Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Wed, 27 Apr 2022 13:44:18 -0500 Subject: [PATCH 7/7] Use only 1 organization for unit test --- coderd/users.go | 3 +++ coderd/users_test.go | 6 ++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/coderd/users.go b/coderd/users.go index b351f7164eb32..b77acee499994 100644 --- a/coderd/users.go +++ b/coderd/users.go @@ -149,6 +149,9 @@ func (api *api) users(rw http.ResponseWriter, r *http.Request) { userIDs = append(userIDs, user.ID) } organizationIDsByMemberIDsRows, err := api.Database.GetOrganizationIDsByMemberIDs(r.Context(), userIDs) + if xerrors.Is(err, sql.ErrNoRows) { + err = nil + } if err != nil { httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ Message: err.Error(), diff --git a/coderd/users_test.go b/coderd/users_test.go index 9e41670155989..8d4d6b2da2f52 100644 --- a/coderd/users_test.go +++ b/coderd/users_test.go @@ -454,14 +454,12 @@ func TestPaginatedUsers(t *testing.T) { coderdtest.CreateFirstUser(t, client) me, err := client.User(context.Background(), codersdk.Me) require.NoError(t, err) + orgID := me.OrganizationIDs[0] allUsers := make([]codersdk.User, 0) allUsers = append(allUsers, me) specialUsers := make([]codersdk.User, 0) - org, err := client.CreateOrganization(ctx, me.ID, codersdk.CreateOrganizationRequest{ - Name: "default", - }) require.NoError(t, err) // When 100 users exist @@ -484,7 +482,7 @@ func TestPaginatedUsers(t *testing.T) { Email: email, Username: username, Password: "password", - OrganizationID: org.ID, + OrganizationID: orgID, }) require.NoError(t, err) allUsers = append(allUsers, newUser)