From c0ff16314529b7b2bba753abde87a180227e5fa6 Mon Sep 17 00:00:00 2001 From: joobisb Date: Sat, 23 Nov 2024 00:21:05 +0530 Subject: [PATCH 01/14] feat: support created_at filter for the GET /users endpoint --- coderd/database/dbmem/dbmem.go | 10 ++++++++++ coderd/database/modelqueries.go | 1 + coderd/database/queries.sql.go | 12 ++++++++++-- coderd/database/queries/users.sql | 6 ++++++ coderd/searchquery/search.go | 1 + coderd/users.go | 1 + coderd/users_test.go | 11 +++++++++++ docs/admin/users/index.md | 6 ++++-- 8 files changed, 44 insertions(+), 4 deletions(-) diff --git a/coderd/database/dbmem/dbmem.go b/coderd/database/dbmem/dbmem.go index aed57e9284b3a..90da06e09320a 100644 --- a/coderd/database/dbmem/dbmem.go +++ b/coderd/database/dbmem/dbmem.go @@ -5648,6 +5648,16 @@ func (q *FakeQuerier) GetUsers(_ context.Context, params database.GetUsersParams users = usersFilteredByRole } + if !params.CreatedAt.IsZero() { + usersFilteredByCreatedAt := make([]database.User, 0, len(users)) + for i, user := range users { + if user.CreatedAt.Equal(params.CreatedAt) { + usersFilteredByCreatedAt = append(usersFilteredByCreatedAt, users[i]) + } + } + users = usersFilteredByCreatedAt + } + if !params.LastSeenBefore.IsZero() { usersFilteredByLastSeen := make([]database.User, 0, len(users)) for i, user := range users { diff --git a/coderd/database/modelqueries.go b/coderd/database/modelqueries.go index ff77012755fa2..58d0f9cbb3bba 100644 --- a/coderd/database/modelqueries.go +++ b/coderd/database/modelqueries.go @@ -390,6 +390,7 @@ func (q *sqlQuerier) GetAuthorizedUsers(ctx context.Context, arg GetUsersParams, pq.Array(arg.RbacRole), arg.LastSeenBefore, arg.LastSeenAfter, + arg.CreatedAt, arg.OffsetOpt, arg.LimitOpt, ) diff --git a/coderd/database/queries.sql.go b/coderd/database/queries.sql.go index 09dd4c1fbc488..40cfe4579436e 100644 --- a/coderd/database/queries.sql.go +++ b/coderd/database/queries.sql.go @@ -10239,16 +10239,22 @@ WHERE last_seen_at >= $6 ELSE true END + -- Filter by created_at + AND CASE + WHEN $7 :: timestamp with time zone != '0001-01-01 00:00:00Z' THEN + DATE(created_at) = DATE($7) + ELSE true + END -- End of filters -- Authorize Filter clause will be injected below in GetAuthorizedUsers -- @authorize_filter ORDER BY -- Deterministic and consistent ordering of all users. This is to ensure consistent pagination. - LOWER(username) ASC OFFSET $7 + LOWER(username) ASC OFFSET $8 LIMIT -- A null limit means "no limit", so 0 means return all - NULLIF($8 :: int, 0) + NULLIF($9 :: int, 0) ` type GetUsersParams struct { @@ -10258,6 +10264,7 @@ type GetUsersParams struct { RbacRole []string `db:"rbac_role" json:"rbac_role"` LastSeenBefore time.Time `db:"last_seen_before" json:"last_seen_before"` LastSeenAfter time.Time `db:"last_seen_after" json:"last_seen_after"` + CreatedAt time.Time `db:"created_at" json:"created_at"` OffsetOpt int32 `db:"offset_opt" json:"offset_opt"` LimitOpt int32 `db:"limit_opt" json:"limit_opt"` } @@ -10293,6 +10300,7 @@ func (q *sqlQuerier) GetUsers(ctx context.Context, arg GetUsersParams) ([]GetUse pq.Array(arg.RbacRole), arg.LastSeenBefore, arg.LastSeenAfter, + arg.CreatedAt, arg.OffsetOpt, arg.LimitOpt, ) diff --git a/coderd/database/queries/users.sql b/coderd/database/queries/users.sql index a4f8844fd2db5..604c625d73bc8 100644 --- a/coderd/database/queries/users.sql +++ b/coderd/database/queries/users.sql @@ -199,6 +199,12 @@ WHERE last_seen_at >= @last_seen_after ELSE true END + -- Filter by created_at + AND CASE + WHEN @created_at :: timestamp with time zone != '0001-01-01 00:00:00Z' THEN + DATE(created_at) = DATE(@created_at) + ELSE true + END -- End of filters -- Authorize Filter clause will be injected below in GetAuthorizedUsers diff --git a/coderd/searchquery/search.go b/coderd/searchquery/search.go index 707f32bfc7d32..104f83c5380e7 100644 --- a/coderd/searchquery/search.go +++ b/coderd/searchquery/search.go @@ -70,6 +70,7 @@ func Users(query string) (database.GetUsersParams, []codersdk.ValidationError) { RbacRole: parser.Strings(values, []string{}, "role"), LastSeenAfter: parser.Time3339Nano(values, time.Time{}, "last_seen_after"), LastSeenBefore: parser.Time3339Nano(values, time.Time{}, "last_seen_before"), + CreatedAt: parser.Time3339Nano(values, time.Time{}, "created_at"), } parser.ErrorExcessParams(values) return filter, parser.Errors diff --git a/coderd/users.go b/coderd/users.go index 2fccef83f2013..b494e248dfc0e 100644 --- a/coderd/users.go +++ b/coderd/users.go @@ -317,6 +317,7 @@ func (api *API) GetUsers(rw http.ResponseWriter, r *http.Request) ([]database.Us RbacRole: params.RbacRole, LastSeenBefore: params.LastSeenBefore, LastSeenAfter: params.LastSeenAfter, + CreatedAt: params.CreatedAt, OffsetOpt: int32(paginationParams.Offset), LimitOpt: int32(paginationParams.Limit), }) diff --git a/coderd/users_test.go b/coderd/users_test.go index c9038c7418034..3c04c1d770011 100644 --- a/coderd/users_test.go +++ b/coderd/users_test.go @@ -1467,6 +1467,7 @@ func TestUsersFilter(t *testing.T) { firstUser, err := client.User(ctx, codersdk.Me) require.NoError(t, err, "fetch me") + createdAt := firstUser.CreatedAt // Noon on Jan 18 is the "now" for this test for last_seen timestamps. // All these values are equal @@ -1655,6 +1656,16 @@ func TestUsersFilter(t *testing.T) { return u.LastSeenAt.Before(end) && u.LastSeenAt.After(start) }, }, + { + Name: "CreatedAt", + Filter: codersdk.UsersRequest{ + SearchQuery: fmt.Sprintf(`created_at:%q`, createdAt.Format(time.RFC3339)), + }, + FilterF: func(_ codersdk.UsersRequest, u codersdk.User) bool { + target := time.Date(2023, 1, 18, 12, 0, 0, 0, time.UTC) + return u.CreatedAt.Equal(target) + }, + }, } for _, c := range testCases { diff --git a/docs/admin/users/index.md b/docs/admin/users/index.md index a00030a514f05..482b940285d95 100644 --- a/docs/admin/users/index.md +++ b/docs/admin/users/index.md @@ -185,8 +185,9 @@ to use the Coder's filter query: - To find active users, use the filter `status:active`. - To find admin users, use the filter `role:admin`. -- To find users have not been active since July 2023: +- To find users who have not been active since July 2023: `status:active last_seen_before:"2023-07-01T00:00:00Z"` +- To find users created at January 18, 2023: `created_at:"2023-01-18T00:00:00Z"` The following filters are supported: @@ -195,6 +196,7 @@ The following filters are supported: - `role` - Represents the role of the user. You can refer to the [TemplateRole documentation](https://pkg.go.dev/github.com/coder/coder/v2/codersdk#TemplateRole) for a list of supported user roles. -- `last_seen_before` and `last_seen_after` - The last time a used has used the +- `last_seen_before` and `last_seen_after` - The last time a user has used the platform (e.g. logging in, any API requests, connecting to workspaces). Uses the RFC3339Nano format. +- `created_at` - The time a user was created. Uses the RFC3339Nano format. From e29a208749c1d72bd7f4abb3a27bb4a57201d43c Mon Sep 17 00:00:00 2001 From: joobisb Date: Thu, 5 Dec 2024 01:47:24 +0530 Subject: [PATCH 02/14] update the query to created_at_before and created_at_after --- coderd/database/dbmem/dbmem.go | 14 +++++++++++-- coderd/database/modelqueries.go | 3 ++- coderd/database/queries.sql.go | 33 +++++++++++++++++++------------ coderd/database/queries/users.sql | 9 +++++++-- coderd/searchquery/search.go | 13 ++++++------ coderd/users.go | 19 +++++++++--------- coderd/users_test.go | 30 +++++++++++++++++++++++----- docs/admin/users/index.md | 6 ++++-- 8 files changed, 87 insertions(+), 40 deletions(-) diff --git a/coderd/database/dbmem/dbmem.go b/coderd/database/dbmem/dbmem.go index 90da06e09320a..5d33d944014e0 100644 --- a/coderd/database/dbmem/dbmem.go +++ b/coderd/database/dbmem/dbmem.go @@ -5648,10 +5648,20 @@ func (q *FakeQuerier) GetUsers(_ context.Context, params database.GetUsersParams users = usersFilteredByRole } - if !params.CreatedAt.IsZero() { + if !params.CreatedAtBefore.IsZero() { usersFilteredByCreatedAt := make([]database.User, 0, len(users)) for i, user := range users { - if user.CreatedAt.Equal(params.CreatedAt) { + if user.CreatedAt.Before(params.CreatedAtBefore) { + usersFilteredByCreatedAt = append(usersFilteredByCreatedAt, users[i]) + } + } + users = usersFilteredByCreatedAt + } + + if !params.CreatedAtAfter.IsZero() { + usersFilteredByCreatedAt := make([]database.User, 0, len(users)) + for i, user := range users { + if user.CreatedAt.After(params.CreatedAtAfter) { usersFilteredByCreatedAt = append(usersFilteredByCreatedAt, users[i]) } } diff --git a/coderd/database/modelqueries.go b/coderd/database/modelqueries.go index 58d0f9cbb3bba..0eb29dbcb54b9 100644 --- a/coderd/database/modelqueries.go +++ b/coderd/database/modelqueries.go @@ -390,7 +390,8 @@ func (q *sqlQuerier) GetAuthorizedUsers(ctx context.Context, arg GetUsersParams, pq.Array(arg.RbacRole), arg.LastSeenBefore, arg.LastSeenAfter, - arg.CreatedAt, + arg.CreatedAtBefore, + arg.CreatedAtAfter, arg.OffsetOpt, arg.LimitOpt, ) diff --git a/coderd/database/queries.sql.go b/coderd/database/queries.sql.go index 40cfe4579436e..5879ef798f978 100644 --- a/coderd/database/queries.sql.go +++ b/coderd/database/queries.sql.go @@ -10242,7 +10242,12 @@ WHERE -- Filter by created_at AND CASE WHEN $7 :: timestamp with time zone != '0001-01-01 00:00:00Z' THEN - DATE(created_at) = DATE($7) + created_at <= $7 + ELSE true + END + AND CASE + WHEN $8 :: timestamp with time zone != '0001-01-01 00:00:00Z' THEN + created_at >= $8 ELSE true END -- End of filters @@ -10251,22 +10256,23 @@ WHERE -- @authorize_filter ORDER BY -- Deterministic and consistent ordering of all users. This is to ensure consistent pagination. - LOWER(username) ASC OFFSET $8 + LOWER(username) ASC OFFSET $9 LIMIT -- A null limit means "no limit", so 0 means return all - NULLIF($9 :: int, 0) + NULLIF($10 :: int, 0) ` type GetUsersParams struct { - AfterID uuid.UUID `db:"after_id" json:"after_id"` - Search string `db:"search" json:"search"` - Status []UserStatus `db:"status" json:"status"` - RbacRole []string `db:"rbac_role" json:"rbac_role"` - LastSeenBefore time.Time `db:"last_seen_before" json:"last_seen_before"` - LastSeenAfter time.Time `db:"last_seen_after" json:"last_seen_after"` - CreatedAt time.Time `db:"created_at" json:"created_at"` - OffsetOpt int32 `db:"offset_opt" json:"offset_opt"` - LimitOpt int32 `db:"limit_opt" json:"limit_opt"` + AfterID uuid.UUID `db:"after_id" json:"after_id"` + Search string `db:"search" json:"search"` + Status []UserStatus `db:"status" json:"status"` + RbacRole []string `db:"rbac_role" json:"rbac_role"` + LastSeenBefore time.Time `db:"last_seen_before" json:"last_seen_before"` + LastSeenAfter time.Time `db:"last_seen_after" json:"last_seen_after"` + CreatedAtBefore time.Time `db:"created_at_before" json:"created_at_before"` + CreatedAtAfter time.Time `db:"created_at_after" json:"created_at_after"` + OffsetOpt int32 `db:"offset_opt" json:"offset_opt"` + LimitOpt int32 `db:"limit_opt" json:"limit_opt"` } type GetUsersRow struct { @@ -10300,7 +10306,8 @@ func (q *sqlQuerier) GetUsers(ctx context.Context, arg GetUsersParams) ([]GetUse pq.Array(arg.RbacRole), arg.LastSeenBefore, arg.LastSeenAfter, - arg.CreatedAt, + arg.CreatedAtBefore, + arg.CreatedAtAfter, arg.OffsetOpt, arg.LimitOpt, ) diff --git a/coderd/database/queries/users.sql b/coderd/database/queries/users.sql index 604c625d73bc8..dce51d45bb907 100644 --- a/coderd/database/queries/users.sql +++ b/coderd/database/queries/users.sql @@ -201,8 +201,13 @@ WHERE END -- Filter by created_at AND CASE - WHEN @created_at :: timestamp with time zone != '0001-01-01 00:00:00Z' THEN - DATE(created_at) = DATE(@created_at) + WHEN @created_at_before :: timestamp with time zone != '0001-01-01 00:00:00Z' THEN + created_at <= @created_at_before + ELSE true + END + AND CASE + WHEN @created_at_after :: timestamp with time zone != '0001-01-01 00:00:00Z' THEN + created_at >= @created_at_after ELSE true END -- End of filters diff --git a/coderd/searchquery/search.go b/coderd/searchquery/search.go index 104f83c5380e7..970ccb287a5ec 100644 --- a/coderd/searchquery/search.go +++ b/coderd/searchquery/search.go @@ -65,12 +65,13 @@ func Users(query string) (database.GetUsersParams, []codersdk.ValidationError) { parser := httpapi.NewQueryParamParser() filter := database.GetUsersParams{ - Search: parser.String(values, "", "search"), - Status: httpapi.ParseCustomList(parser, values, []database.UserStatus{}, "status", httpapi.ParseEnum[database.UserStatus]), - RbacRole: parser.Strings(values, []string{}, "role"), - LastSeenAfter: parser.Time3339Nano(values, time.Time{}, "last_seen_after"), - LastSeenBefore: parser.Time3339Nano(values, time.Time{}, "last_seen_before"), - CreatedAt: parser.Time3339Nano(values, time.Time{}, "created_at"), + Search: parser.String(values, "", "search"), + Status: httpapi.ParseCustomList(parser, values, []database.UserStatus{}, "status", httpapi.ParseEnum[database.UserStatus]), + RbacRole: parser.Strings(values, []string{}, "role"), + LastSeenAfter: parser.Time3339Nano(values, time.Time{}, "last_seen_after"), + LastSeenBefore: parser.Time3339Nano(values, time.Time{}, "last_seen_before"), + CreatedAtAfter: parser.Time3339Nano(values, time.Time{}, "created_at_after"), + CreatedAtBefore: parser.Time3339Nano(values, time.Time{}, "created_at_before"), } parser.ErrorExcessParams(values) return filter, parser.Errors diff --git a/coderd/users.go b/coderd/users.go index b494e248dfc0e..959579ffbadcb 100644 --- a/coderd/users.go +++ b/coderd/users.go @@ -311,15 +311,16 @@ func (api *API) GetUsers(rw http.ResponseWriter, r *http.Request) ([]database.Us } userRows, err := api.Database.GetUsers(ctx, database.GetUsersParams{ - AfterID: paginationParams.AfterID, - Search: params.Search, - Status: params.Status, - RbacRole: params.RbacRole, - LastSeenBefore: params.LastSeenBefore, - LastSeenAfter: params.LastSeenAfter, - CreatedAt: params.CreatedAt, - OffsetOpt: int32(paginationParams.Offset), - LimitOpt: int32(paginationParams.Limit), + AfterID: paginationParams.AfterID, + Search: params.Search, + Status: params.Status, + RbacRole: params.RbacRole, + LastSeenBefore: params.LastSeenBefore, + LastSeenAfter: params.LastSeenAfter, + CreatedAtAfter: params.CreatedAtAfter, + CreatedAtBefore: params.CreatedAtBefore, + OffsetOpt: int32(paginationParams.Offset), + LimitOpt: int32(paginationParams.Limit), }) if err != nil { httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{ diff --git a/coderd/users_test.go b/coderd/users_test.go index 3c04c1d770011..324c4c57756e1 100644 --- a/coderd/users_test.go +++ b/coderd/users_test.go @@ -1467,7 +1467,6 @@ func TestUsersFilter(t *testing.T) { firstUser, err := client.User(ctx, codersdk.Me) require.NoError(t, err, "fetch me") - createdAt := firstUser.CreatedAt // Noon on Jan 18 is the "now" for this test for last_seen timestamps. // All these values are equal @@ -1657,13 +1656,34 @@ func TestUsersFilter(t *testing.T) { }, }, { - Name: "CreatedAt", + Name: "CreatedAtBefore", Filter: codersdk.UsersRequest{ - SearchQuery: fmt.Sprintf(`created_at:%q`, createdAt.Format(time.RFC3339)), + SearchQuery: `created_at_before:"2023-01-31T23:59:59Z"`, }, FilterF: func(_ codersdk.UsersRequest, u codersdk.User) bool { - target := time.Date(2023, 1, 18, 12, 0, 0, 0, time.UTC) - return u.CreatedAt.Equal(target) + end := time.Date(2023, 1, 31, 23, 59, 59, 0, time.UTC) + return u.CreatedAt.Before(end) + }, + }, + { + Name: "CreatedAtAfter", + Filter: codersdk.UsersRequest{ + SearchQuery: `created_at_after:"2023-01-01T00:00:00Z"`, + }, + FilterF: func(_ codersdk.UsersRequest, u codersdk.User) bool { + start := time.Date(2023, 1, 1, 0, 0, 0, 0, time.UTC) + return u.CreatedAt.After(start) + }, + }, + { + Name: "CreatedAtRange", + Filter: codersdk.UsersRequest{ + SearchQuery: `created_at_after:"2023-01-01T00:00:00Z" created_at_before:"2023-01-31T23:59:59Z"`, + }, + FilterF: func(_ codersdk.UsersRequest, u codersdk.User) bool { + start := time.Date(2023, 1, 1, 0, 0, 0, 0, time.UTC) + end := time.Date(2023, 1, 31, 23, 59, 59, 0, time.UTC) + return u.CreatedAt.After(start) && u.CreatedAt.Before(end) }, }, } diff --git a/docs/admin/users/index.md b/docs/admin/users/index.md index 482b940285d95..f51ea88e45372 100644 --- a/docs/admin/users/index.md +++ b/docs/admin/users/index.md @@ -187,7 +187,8 @@ to use the Coder's filter query: - To find admin users, use the filter `role:admin`. - To find users who have not been active since July 2023: `status:active last_seen_before:"2023-07-01T00:00:00Z"` -- To find users created at January 18, 2023: `created_at:"2023-01-18T00:00:00Z"` +- To find users who were created between January 1 and January 18, 2023: + `created_at_before:"2023-01-18T00:00:00Z" created_at_after:"2023-01-01T23:59:59Z"` The following filters are supported: @@ -199,4 +200,5 @@ The following filters are supported: - `last_seen_before` and `last_seen_after` - The last time a user has used the platform (e.g. logging in, any API requests, connecting to workspaces). Uses the RFC3339Nano format. -- `created_at` - The time a user was created. Uses the RFC3339Nano format. +- `created_at_before` and `created_at_after` - The time a user was created. Uses + the RFC3339Nano format. From 8f246c50bbc9352a897c38eb08543855d8edb6b8 Mon Sep 17 00:00:00 2001 From: joobisb Date: Thu, 5 Dec 2024 19:16:41 +0530 Subject: [PATCH 03/14] rename the created fields --- coderd/database/dbmem/dbmem.go | 8 ++++---- coderd/database/modelqueries.go | 4 ++-- coderd/database/queries.sql.go | 24 ++++++++++++------------ coderd/database/queries/users.sql | 8 ++++---- coderd/searchquery/search.go | 14 +++++++------- coderd/users.go | 20 ++++++++++---------- coderd/users_test.go | 6 +++--- docs/admin/users/index.md | 4 ++-- 8 files changed, 44 insertions(+), 44 deletions(-) diff --git a/coderd/database/dbmem/dbmem.go b/coderd/database/dbmem/dbmem.go index 5d33d944014e0..4ea18cbd05033 100644 --- a/coderd/database/dbmem/dbmem.go +++ b/coderd/database/dbmem/dbmem.go @@ -5648,20 +5648,20 @@ func (q *FakeQuerier) GetUsers(_ context.Context, params database.GetUsersParams users = usersFilteredByRole } - if !params.CreatedAtBefore.IsZero() { + if !params.CreatedBefore.IsZero() { usersFilteredByCreatedAt := make([]database.User, 0, len(users)) for i, user := range users { - if user.CreatedAt.Before(params.CreatedAtBefore) { + if user.CreatedAt.Before(params.CreatedBefore) { usersFilteredByCreatedAt = append(usersFilteredByCreatedAt, users[i]) } } users = usersFilteredByCreatedAt } - if !params.CreatedAtAfter.IsZero() { + if !params.CreatedAfter.IsZero() { usersFilteredByCreatedAt := make([]database.User, 0, len(users)) for i, user := range users { - if user.CreatedAt.After(params.CreatedAtAfter) { + if user.CreatedAt.After(params.CreatedAfter) { usersFilteredByCreatedAt = append(usersFilteredByCreatedAt, users[i]) } } diff --git a/coderd/database/modelqueries.go b/coderd/database/modelqueries.go index 0eb29dbcb54b9..0929cb9962f74 100644 --- a/coderd/database/modelqueries.go +++ b/coderd/database/modelqueries.go @@ -390,8 +390,8 @@ func (q *sqlQuerier) GetAuthorizedUsers(ctx context.Context, arg GetUsersParams, pq.Array(arg.RbacRole), arg.LastSeenBefore, arg.LastSeenAfter, - arg.CreatedAtBefore, - arg.CreatedAtAfter, + arg.CreatedBefore, + arg.CreatedAfter, arg.OffsetOpt, arg.LimitOpt, ) diff --git a/coderd/database/queries.sql.go b/coderd/database/queries.sql.go index 5879ef798f978..9239a383d70df 100644 --- a/coderd/database/queries.sql.go +++ b/coderd/database/queries.sql.go @@ -10263,16 +10263,16 @@ LIMIT ` type GetUsersParams struct { - AfterID uuid.UUID `db:"after_id" json:"after_id"` - Search string `db:"search" json:"search"` - Status []UserStatus `db:"status" json:"status"` - RbacRole []string `db:"rbac_role" json:"rbac_role"` - LastSeenBefore time.Time `db:"last_seen_before" json:"last_seen_before"` - LastSeenAfter time.Time `db:"last_seen_after" json:"last_seen_after"` - CreatedAtBefore time.Time `db:"created_at_before" json:"created_at_before"` - CreatedAtAfter time.Time `db:"created_at_after" json:"created_at_after"` - OffsetOpt int32 `db:"offset_opt" json:"offset_opt"` - LimitOpt int32 `db:"limit_opt" json:"limit_opt"` + AfterID uuid.UUID `db:"after_id" json:"after_id"` + Search string `db:"search" json:"search"` + Status []UserStatus `db:"status" json:"status"` + RbacRole []string `db:"rbac_role" json:"rbac_role"` + LastSeenBefore time.Time `db:"last_seen_before" json:"last_seen_before"` + LastSeenAfter time.Time `db:"last_seen_after" json:"last_seen_after"` + CreatedBefore time.Time `db:"created_before" json:"created_before"` + CreatedAfter time.Time `db:"created_after" json:"created_after"` + OffsetOpt int32 `db:"offset_opt" json:"offset_opt"` + LimitOpt int32 `db:"limit_opt" json:"limit_opt"` } type GetUsersRow struct { @@ -10306,8 +10306,8 @@ func (q *sqlQuerier) GetUsers(ctx context.Context, arg GetUsersParams) ([]GetUse pq.Array(arg.RbacRole), arg.LastSeenBefore, arg.LastSeenAfter, - arg.CreatedAtBefore, - arg.CreatedAtAfter, + arg.CreatedBefore, + arg.CreatedAfter, arg.OffsetOpt, arg.LimitOpt, ) diff --git a/coderd/database/queries/users.sql b/coderd/database/queries/users.sql index dce51d45bb907..1f30a2c2c1d24 100644 --- a/coderd/database/queries/users.sql +++ b/coderd/database/queries/users.sql @@ -201,13 +201,13 @@ WHERE END -- Filter by created_at AND CASE - WHEN @created_at_before :: timestamp with time zone != '0001-01-01 00:00:00Z' THEN - created_at <= @created_at_before + WHEN @created_before :: timestamp with time zone != '0001-01-01 00:00:00Z' THEN + created_at <= @created_before ELSE true END AND CASE - WHEN @created_at_after :: timestamp with time zone != '0001-01-01 00:00:00Z' THEN - created_at >= @created_at_after + WHEN @created_after :: timestamp with time zone != '0001-01-01 00:00:00Z' THEN + created_at >= @created_after ELSE true END -- End of filters diff --git a/coderd/searchquery/search.go b/coderd/searchquery/search.go index 970ccb287a5ec..a4fe5d4775d6c 100644 --- a/coderd/searchquery/search.go +++ b/coderd/searchquery/search.go @@ -65,13 +65,13 @@ func Users(query string) (database.GetUsersParams, []codersdk.ValidationError) { parser := httpapi.NewQueryParamParser() filter := database.GetUsersParams{ - Search: parser.String(values, "", "search"), - Status: httpapi.ParseCustomList(parser, values, []database.UserStatus{}, "status", httpapi.ParseEnum[database.UserStatus]), - RbacRole: parser.Strings(values, []string{}, "role"), - LastSeenAfter: parser.Time3339Nano(values, time.Time{}, "last_seen_after"), - LastSeenBefore: parser.Time3339Nano(values, time.Time{}, "last_seen_before"), - CreatedAtAfter: parser.Time3339Nano(values, time.Time{}, "created_at_after"), - CreatedAtBefore: parser.Time3339Nano(values, time.Time{}, "created_at_before"), + Search: parser.String(values, "", "search"), + Status: httpapi.ParseCustomList(parser, values, []database.UserStatus{}, "status", httpapi.ParseEnum[database.UserStatus]), + RbacRole: parser.Strings(values, []string{}, "role"), + LastSeenAfter: parser.Time3339Nano(values, time.Time{}, "last_seen_after"), + LastSeenBefore: parser.Time3339Nano(values, time.Time{}, "last_seen_before"), + CreatedAfter: parser.Time3339Nano(values, time.Time{}, "created_after"), + CreatedBefore: parser.Time3339Nano(values, time.Time{}, "created_before"), } parser.ErrorExcessParams(values) return filter, parser.Errors diff --git a/coderd/users.go b/coderd/users.go index 959579ffbadcb..56f295986859c 100644 --- a/coderd/users.go +++ b/coderd/users.go @@ -311,16 +311,16 @@ func (api *API) GetUsers(rw http.ResponseWriter, r *http.Request) ([]database.Us } userRows, err := api.Database.GetUsers(ctx, database.GetUsersParams{ - AfterID: paginationParams.AfterID, - Search: params.Search, - Status: params.Status, - RbacRole: params.RbacRole, - LastSeenBefore: params.LastSeenBefore, - LastSeenAfter: params.LastSeenAfter, - CreatedAtAfter: params.CreatedAtAfter, - CreatedAtBefore: params.CreatedAtBefore, - OffsetOpt: int32(paginationParams.Offset), - LimitOpt: int32(paginationParams.Limit), + AfterID: paginationParams.AfterID, + Search: params.Search, + Status: params.Status, + RbacRole: params.RbacRole, + LastSeenBefore: params.LastSeenBefore, + LastSeenAfter: params.LastSeenAfter, + CreatedAfter: params.CreatedAfter, + CreatedBefore: params.CreatedBefore, + OffsetOpt: int32(paginationParams.Offset), + LimitOpt: int32(paginationParams.Limit), }) if err != nil { httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{ diff --git a/coderd/users_test.go b/coderd/users_test.go index 324c4c57756e1..cd73357a15987 100644 --- a/coderd/users_test.go +++ b/coderd/users_test.go @@ -1658,7 +1658,7 @@ func TestUsersFilter(t *testing.T) { { Name: "CreatedAtBefore", Filter: codersdk.UsersRequest{ - SearchQuery: `created_at_before:"2023-01-31T23:59:59Z"`, + SearchQuery: `created_before:"2023-01-31T23:59:59Z"`, }, FilterF: func(_ codersdk.UsersRequest, u codersdk.User) bool { end := time.Date(2023, 1, 31, 23, 59, 59, 0, time.UTC) @@ -1668,7 +1668,7 @@ func TestUsersFilter(t *testing.T) { { Name: "CreatedAtAfter", Filter: codersdk.UsersRequest{ - SearchQuery: `created_at_after:"2023-01-01T00:00:00Z"`, + SearchQuery: `created_after:"2023-01-01T00:00:00Z"`, }, FilterF: func(_ codersdk.UsersRequest, u codersdk.User) bool { start := time.Date(2023, 1, 1, 0, 0, 0, 0, time.UTC) @@ -1678,7 +1678,7 @@ func TestUsersFilter(t *testing.T) { { Name: "CreatedAtRange", Filter: codersdk.UsersRequest{ - SearchQuery: `created_at_after:"2023-01-01T00:00:00Z" created_at_before:"2023-01-31T23:59:59Z"`, + SearchQuery: `created_after:"2023-01-01T00:00:00Z" created_before:"2023-01-31T23:59:59Z"`, }, FilterF: func(_ codersdk.UsersRequest, u codersdk.User) bool { start := time.Date(2023, 1, 1, 0, 0, 0, 0, time.UTC) diff --git a/docs/admin/users/index.md b/docs/admin/users/index.md index f51ea88e45372..2dc7a56010a08 100644 --- a/docs/admin/users/index.md +++ b/docs/admin/users/index.md @@ -188,7 +188,7 @@ to use the Coder's filter query: - To find users who have not been active since July 2023: `status:active last_seen_before:"2023-07-01T00:00:00Z"` - To find users who were created between January 1 and January 18, 2023: - `created_at_before:"2023-01-18T00:00:00Z" created_at_after:"2023-01-01T23:59:59Z"` + `created_before:"2023-01-18T00:00:00Z" created_after:"2023-01-01T23:59:59Z"` The following filters are supported: @@ -200,5 +200,5 @@ The following filters are supported: - `last_seen_before` and `last_seen_after` - The last time a user has used the platform (e.g. logging in, any API requests, connecting to workspaces). Uses the RFC3339Nano format. -- `created_at_before` and `created_at_after` - The time a user was created. Uses +- `created_before` and `created_after` - The time a user was created. Uses the RFC3339Nano format. From 70895a4bcf3c047eb398ada997f5156c7e353f46 Mon Sep 17 00:00:00 2001 From: joobisb Date: Tue, 10 Dec 2024 17:20:36 +0530 Subject: [PATCH 04/14] chore: update tests to handle user created filters --- coderd/users_test.go | 69 ++++++++++++++++++++++++++++++++++++--- docs/admin/users/index.md | 4 +-- 2 files changed, 66 insertions(+), 7 deletions(-) diff --git a/coderd/users_test.go b/coderd/users_test.go index cd73357a15987..fe88ebe08a6b3 100644 --- a/coderd/users_test.go +++ b/coderd/users_test.go @@ -1513,6 +1513,45 @@ func TestUsersFilter(t *testing.T) { users = append(users, user) } + // Add users with different creation dates for testing date filters + for i := 0; i < 3; i++ { + // nolint:gocritic + user1, err := api.Database.InsertUser(dbauthz.AsSystemRestricted(ctx), database.InsertUserParams{ + ID: uuid.New(), + Email: fmt.Sprintf("before%d@coder.com", i), + Username: fmt.Sprintf("before%d", i), + LoginType: database.LoginTypeNone, + CreatedAt: time.Date(2022, 12, 15+i, 12, 0, 0, 0, time.UTC), + UpdatedAt: dbtime.Now(), + }) + require.NoError(t, err) + users = append(users, dbUserToSDKUser(user1)) + + // nolint:gocritic + user2, err := api.Database.InsertUser(dbauthz.AsSystemRestricted(ctx), database.InsertUserParams{ + ID: uuid.New(), + Email: fmt.Sprintf("during%d@coder.com", i), + Username: fmt.Sprintf("during%d", i), + LoginType: database.LoginTypeNone, + CreatedAt: time.Date(2023, 1, 15+i, 12, 0, 0, 0, time.UTC), + UpdatedAt: dbtime.Now(), + }) + require.NoError(t, err) + users = append(users, dbUserToSDKUser(user2)) + + // nolint:gocritic + user3, err := api.Database.InsertUser(dbauthz.AsSystemRestricted(ctx), database.InsertUserParams{ + ID: uuid.New(), + Email: fmt.Sprintf("after%d@coder.com", i), + Username: fmt.Sprintf("after%d", i), + LoginType: database.LoginTypeNone, + CreatedAt: time.Date(2023, 2, 15+i, 12, 0, 0, 0, time.UTC), + UpdatedAt: dbtime.Now(), + }) + require.NoError(t, err) + users = append(users, dbUserToSDKUser(user3)) + } + // --- Setup done --- testCases := []struct { Name string @@ -1521,10 +1560,8 @@ func TestUsersFilter(t *testing.T) { FilterF func(f codersdk.UsersRequest, user codersdk.User) bool }{ { - Name: "All", - Filter: codersdk.UsersRequest{ - Status: codersdk.UserStatusSuspended + "," + codersdk.UserStatusActive, - }, + Name: "All", + Filter: codersdk.UsersRequest{}, FilterF: func(_ codersdk.UsersRequest, u codersdk.User) bool { return true }, @@ -1602,7 +1639,7 @@ func TestUsersFilter(t *testing.T) { Status: codersdk.UserStatusSuspended + "," + codersdk.UserStatusActive, }, FilterF: func(_ codersdk.UsersRequest, u codersdk.User) bool { - return true + return u.Status == codersdk.UserStatusSuspended || u.Status == codersdk.UserStatusActive }, }, { @@ -2304,6 +2341,28 @@ func onlyUsernames[U codersdk.User | database.User](users []U) []string { return out } +// dbUserToSDKUser converts database.User to codersdk.User +func dbUserToSDKUser(dbUser database.User) codersdk.User { + return codersdk.User{ + ReducedUser: codersdk.ReducedUser{ + MinimalUser: codersdk.MinimalUser{ + ID: dbUser.ID, + Username: dbUser.Username, + AvatarURL: dbUser.AvatarURL, + }, + Email: dbUser.Email, + CreatedAt: dbUser.CreatedAt, + UpdatedAt: dbUser.UpdatedAt, + LastSeenAt: dbUser.LastSeenAt, + Status: codersdk.UserStatus(dbUser.Status), + LoginType: codersdk.LoginType(dbUser.LoginType), + ThemePreference: dbUser.ThemePreference, + }, + OrganizationIDs: make([]uuid.UUID, 0), + Roles: make([]codersdk.SlimRole, 0), + } +} + func BenchmarkUsersMe(b *testing.B) { client := coderdtest.New(b, nil) _ = coderdtest.CreateFirstUser(b, client) diff --git a/docs/admin/users/index.md b/docs/admin/users/index.md index 2dc7a56010a08..9dcdb237eb764 100644 --- a/docs/admin/users/index.md +++ b/docs/admin/users/index.md @@ -200,5 +200,5 @@ The following filters are supported: - `last_seen_before` and `last_seen_after` - The last time a user has used the platform (e.g. logging in, any API requests, connecting to workspaces). Uses the RFC3339Nano format. -- `created_before` and `created_after` - The time a user was created. Uses - the RFC3339Nano format. +- `created_before` and `created_after` - The time a user was created. Uses the + RFC3339Nano format. From daf481e83f2cc9524137dea3dbcdfa15182babad Mon Sep 17 00:00:00 2001 From: joobisb Date: Wed, 11 Dec 2024 00:27:32 +0530 Subject: [PATCH 05/14] addressed comments --- coderd/users_test.go | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/coderd/users_test.go b/coderd/users_test.go index fe88ebe08a6b3..8fb39a905f961 100644 --- a/coderd/users_test.go +++ b/coderd/users_test.go @@ -1515,35 +1515,38 @@ func TestUsersFilter(t *testing.T) { // Add users with different creation dates for testing date filters for i := 0; i < 3; i++ { - // nolint:gocritic + // nolint:gocritic // Using system context is necessary to seed data in tests user1, err := api.Database.InsertUser(dbauthz.AsSystemRestricted(ctx), database.InsertUserParams{ ID: uuid.New(), Email: fmt.Sprintf("before%d@coder.com", i), Username: fmt.Sprintf("before%d", i), LoginType: database.LoginTypeNone, + Status: string(codersdk.UserStatusActive), CreatedAt: time.Date(2022, 12, 15+i, 12, 0, 0, 0, time.UTC), UpdatedAt: dbtime.Now(), }) require.NoError(t, err) users = append(users, dbUserToSDKUser(user1)) - // nolint:gocritic + // nolint:gocritic //Using system context is necessary to seed data in tests user2, err := api.Database.InsertUser(dbauthz.AsSystemRestricted(ctx), database.InsertUserParams{ ID: uuid.New(), Email: fmt.Sprintf("during%d@coder.com", i), Username: fmt.Sprintf("during%d", i), LoginType: database.LoginTypeNone, + Status: string(codersdk.UserStatusActive), CreatedAt: time.Date(2023, 1, 15+i, 12, 0, 0, 0, time.UTC), UpdatedAt: dbtime.Now(), }) require.NoError(t, err) users = append(users, dbUserToSDKUser(user2)) - // nolint:gocritic + // nolint:gocritic // Using system context is necessary to seed data in tests user3, err := api.Database.InsertUser(dbauthz.AsSystemRestricted(ctx), database.InsertUserParams{ ID: uuid.New(), Email: fmt.Sprintf("after%d@coder.com", i), Username: fmt.Sprintf("after%d", i), + Status: string(codersdk.UserStatusActive), LoginType: database.LoginTypeNone, CreatedAt: time.Date(2023, 2, 15+i, 12, 0, 0, 0, time.UTC), UpdatedAt: dbtime.Now(), @@ -1560,8 +1563,10 @@ func TestUsersFilter(t *testing.T) { FilterF func(f codersdk.UsersRequest, user codersdk.User) bool }{ { - Name: "All", - Filter: codersdk.UsersRequest{}, + Name: "All", + Filter: codersdk.UsersRequest{ + Status: codersdk.UserStatusSuspended + "," + codersdk.UserStatusActive, + }, FilterF: func(_ codersdk.UsersRequest, u codersdk.User) bool { return true }, @@ -1639,7 +1644,7 @@ func TestUsersFilter(t *testing.T) { Status: codersdk.UserStatusSuspended + "," + codersdk.UserStatusActive, }, FilterF: func(_ codersdk.UsersRequest, u codersdk.User) bool { - return u.Status == codersdk.UserStatusSuspended || u.Status == codersdk.UserStatusActive + return true }, }, { From 81932341a7ce5a0c3bbd5da801840c1071a49fc1 Mon Sep 17 00:00:00 2001 From: joobisb Date: Wed, 11 Dec 2024 15:06:52 +0530 Subject: [PATCH 06/14] use existing db2sdk method --- coderd/users_test.go | 31 ++++++------------------------- 1 file changed, 6 insertions(+), 25 deletions(-) diff --git a/coderd/users_test.go b/coderd/users_test.go index 8fb39a905f961..8400dbab4e3cc 100644 --- a/coderd/users_test.go +++ b/coderd/users_test.go @@ -26,6 +26,7 @@ import ( "github.com/coder/coder/v2/coderd/audit" "github.com/coder/coder/v2/coderd/coderdtest" "github.com/coder/coder/v2/coderd/database" + "github.com/coder/coder/v2/coderd/database/db2sdk" "github.com/coder/coder/v2/coderd/database/dbauthz" "github.com/coder/coder/v2/coderd/database/dbfake" "github.com/coder/coder/v2/coderd/database/dbgen" @@ -1526,7 +1527,7 @@ func TestUsersFilter(t *testing.T) { UpdatedAt: dbtime.Now(), }) require.NoError(t, err) - users = append(users, dbUserToSDKUser(user1)) + users = append(users, db2sdk.User(user1, []uuid.UUID{})) // nolint:gocritic //Using system context is necessary to seed data in tests user2, err := api.Database.InsertUser(dbauthz.AsSystemRestricted(ctx), database.InsertUserParams{ @@ -1539,7 +1540,7 @@ func TestUsersFilter(t *testing.T) { UpdatedAt: dbtime.Now(), }) require.NoError(t, err) - users = append(users, dbUserToSDKUser(user2)) + users = append(users, db2sdk.User(user2, []uuid.UUID{})) // nolint:gocritic // Using system context is necessary to seed data in tests user3, err := api.Database.InsertUser(dbauthz.AsSystemRestricted(ctx), database.InsertUserParams{ @@ -1552,7 +1553,7 @@ func TestUsersFilter(t *testing.T) { UpdatedAt: dbtime.Now(), }) require.NoError(t, err) - users = append(users, dbUserToSDKUser(user3)) + users = append(users, db2sdk.User(user3, []uuid.UUID{})) } // --- Setup done --- @@ -1748,6 +1749,8 @@ func TestUsersFilter(t *testing.T) { exp = append(exp, made) } } + fmt.Printf("expexp: %+v\n", exp) + fmt.Printf("matched.Usersmatched.Users: %+v\n", matched.Users) require.ElementsMatch(t, exp, matched.Users, "expected users returned") }) } @@ -2346,28 +2349,6 @@ func onlyUsernames[U codersdk.User | database.User](users []U) []string { return out } -// dbUserToSDKUser converts database.User to codersdk.User -func dbUserToSDKUser(dbUser database.User) codersdk.User { - return codersdk.User{ - ReducedUser: codersdk.ReducedUser{ - MinimalUser: codersdk.MinimalUser{ - ID: dbUser.ID, - Username: dbUser.Username, - AvatarURL: dbUser.AvatarURL, - }, - Email: dbUser.Email, - CreatedAt: dbUser.CreatedAt, - UpdatedAt: dbUser.UpdatedAt, - LastSeenAt: dbUser.LastSeenAt, - Status: codersdk.UserStatus(dbUser.Status), - LoginType: codersdk.LoginType(dbUser.LoginType), - ThemePreference: dbUser.ThemePreference, - }, - OrganizationIDs: make([]uuid.UUID, 0), - Roles: make([]codersdk.SlimRole, 0), - } -} - func BenchmarkUsersMe(b *testing.B) { client := coderdtest.New(b, nil) _ = coderdtest.CreateFirstUser(b, client) From 67a7113874d205a8a45bf433cea893bac94f521c Mon Sep 17 00:00:00 2001 From: joobisb Date: Wed, 11 Dec 2024 16:27:09 +0530 Subject: [PATCH 07/14] remove unnecessary print statements --- coderd/users_test.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/coderd/users_test.go b/coderd/users_test.go index 8400dbab4e3cc..8a7758f7bfc79 100644 --- a/coderd/users_test.go +++ b/coderd/users_test.go @@ -1749,8 +1749,6 @@ func TestUsersFilter(t *testing.T) { exp = append(exp, made) } } - fmt.Printf("expexp: %+v\n", exp) - fmt.Printf("matched.Usersmatched.Users: %+v\n", matched.Users) require.ElementsMatch(t, exp, matched.Users, "expected users returned") }) } From 20ed862c5114f06b83cf5a1434045b7d52690a66 Mon Sep 17 00:00:00 2001 From: joobisb Date: Wed, 11 Dec 2024 18:21:02 +0530 Subject: [PATCH 08/14] fix tests --- coderd/users_test.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/coderd/users_test.go b/coderd/users_test.go index 8a7758f7bfc79..a6d7f3c82ebd4 100644 --- a/coderd/users_test.go +++ b/coderd/users_test.go @@ -1523,6 +1523,7 @@ func TestUsersFilter(t *testing.T) { Username: fmt.Sprintf("before%d", i), LoginType: database.LoginTypeNone, Status: string(codersdk.UserStatusActive), + RBACRoles: []string{codersdk.RoleOwner}, CreatedAt: time.Date(2022, 12, 15+i, 12, 0, 0, 0, time.UTC), UpdatedAt: dbtime.Now(), }) @@ -1536,6 +1537,7 @@ func TestUsersFilter(t *testing.T) { Username: fmt.Sprintf("during%d", i), LoginType: database.LoginTypeNone, Status: string(codersdk.UserStatusActive), + RBACRoles: []string{codersdk.RoleOwner}, CreatedAt: time.Date(2023, 1, 15+i, 12, 0, 0, 0, time.UTC), UpdatedAt: dbtime.Now(), }) @@ -1549,6 +1551,7 @@ func TestUsersFilter(t *testing.T) { Username: fmt.Sprintf("after%d", i), Status: string(codersdk.UserStatusActive), LoginType: database.LoginTypeNone, + RBACRoles: []string{codersdk.RoleOwner}, CreatedAt: time.Date(2023, 2, 15+i, 12, 0, 0, 0, time.UTC), UpdatedAt: dbtime.Now(), }) From f2d1a0220294bfb1090e386c0bc96297e715f023 Mon Sep 17 00:00:00 2001 From: joobisb Date: Sat, 14 Dec 2024 13:10:44 +0530 Subject: [PATCH 09/14] fix: test failing on postgres --- coderd/users_test.go | 66 ++++++++++++++++++++++++++++++++++---------- 1 file changed, 52 insertions(+), 14 deletions(-) diff --git a/coderd/users_test.go b/coderd/users_test.go index a6d7f3c82ebd4..a8de254257145 100644 --- a/coderd/users_test.go +++ b/coderd/users_test.go @@ -26,7 +26,6 @@ import ( "github.com/coder/coder/v2/coderd/audit" "github.com/coder/coder/v2/coderd/coderdtest" "github.com/coder/coder/v2/coderd/database" - "github.com/coder/coder/v2/coderd/database/db2sdk" "github.com/coder/coder/v2/coderd/database/dbauthz" "github.com/coder/coder/v2/coderd/database/dbfake" "github.com/coder/coder/v2/coderd/database/dbgen" @@ -1522,13 +1521,26 @@ func TestUsersFilter(t *testing.T) { Email: fmt.Sprintf("before%d@coder.com", i), Username: fmt.Sprintf("before%d", i), LoginType: database.LoginTypeNone, - Status: string(codersdk.UserStatusActive), - RBACRoles: []string{codersdk.RoleOwner}, - CreatedAt: time.Date(2022, 12, 15+i, 12, 0, 0, 0, time.UTC), - UpdatedAt: dbtime.Now(), + RBACRoles: []string{codersdk.RoleMember}, + CreatedAt: dbtime.Time(time.Date(2022, 12, 15+i, 12, 0, 0, 0, time.UTC)), }) require.NoError(t, err) - users = append(users, db2sdk.User(user1, []uuid.UUID{})) + + orgMember, err := api.Database.InsertOrganizationMember(dbauthz.AsSystemRestricted(ctx), database.InsertOrganizationMemberParams{ + OrganizationID: firstUser.OrganizationIDs[0], + UserID: user1.ID, + Roles: []string{}, + CreatedAt: dbtime.Now(), + }) + require.NoError(t, err) + + // hack: Call UpdateUserStatus to get API-formatted timestamps (without timezones) + // instead of database-formatted timestamps (with timezones) for comparison + sdkUser1, err := client.UpdateUserStatus(ctx, user1.ID.String(), codersdk.UserStatusActive) + require.NoError(t, err) + + sdkUser1.OrganizationIDs = []uuid.UUID{orgMember.OrganizationID} + users = append(users, sdkUser1) // nolint:gocritic //Using system context is necessary to seed data in tests user2, err := api.Database.InsertUser(dbauthz.AsSystemRestricted(ctx), database.InsertUserParams{ @@ -1536,27 +1548,53 @@ func TestUsersFilter(t *testing.T) { Email: fmt.Sprintf("during%d@coder.com", i), Username: fmt.Sprintf("during%d", i), LoginType: database.LoginTypeNone, - Status: string(codersdk.UserStatusActive), RBACRoles: []string{codersdk.RoleOwner}, - CreatedAt: time.Date(2023, 1, 15+i, 12, 0, 0, 0, time.UTC), - UpdatedAt: dbtime.Now(), + CreatedAt: dbtime.Time(time.Date(2023, 1, 15+i, 12, 0, 0, 0, time.UTC)), }) require.NoError(t, err) - users = append(users, db2sdk.User(user2, []uuid.UUID{})) + + // hack: Call UpdateUserStatus to get API-formatted timestamps (without timezones) + // instead of database-formatted timestamps (with timezones) for comparison + sdkUser2, err := client.UpdateUserStatus(ctx, user2.ID.String(), codersdk.UserStatusActive) + require.NoError(t, err) + + orgMember, err = api.Database.InsertOrganizationMember(dbauthz.AsSystemRestricted(ctx), database.InsertOrganizationMemberParams{ + OrganizationID: firstUser.OrganizationIDs[0], + UserID: user2.ID, + Roles: []string{}, + CreatedAt: dbtime.Now(), + }) + require.NoError(t, err) + + sdkUser2.OrganizationIDs = []uuid.UUID{orgMember.OrganizationID} + users = append(users, sdkUser2) // nolint:gocritic // Using system context is necessary to seed data in tests user3, err := api.Database.InsertUser(dbauthz.AsSystemRestricted(ctx), database.InsertUserParams{ ID: uuid.New(), Email: fmt.Sprintf("after%d@coder.com", i), Username: fmt.Sprintf("after%d", i), - Status: string(codersdk.UserStatusActive), LoginType: database.LoginTypeNone, RBACRoles: []string{codersdk.RoleOwner}, - CreatedAt: time.Date(2023, 2, 15+i, 12, 0, 0, 0, time.UTC), - UpdatedAt: dbtime.Now(), + CreatedAt: dbtime.Time(time.Date(2023, 2, 15+i, 12, 0, 0, 0, time.UTC)), }) require.NoError(t, err) - users = append(users, db2sdk.User(user3, []uuid.UUID{})) + + // hack: Call UpdateUserStatus to get API-formatted timestamps (without timezones) + // instead of database-formatted timestamps (with timezones) for comparison + sdkUser3, err := client.UpdateUserStatus(ctx, user3.ID.String(), codersdk.UserStatusActive) + require.NoError(t, err) + + orgMember, err = api.Database.InsertOrganizationMember(dbauthz.AsSystemRestricted(ctx), database.InsertOrganizationMemberParams{ + OrganizationID: firstUser.OrganizationIDs[0], + UserID: user3.ID, + Roles: []string{}, + CreatedAt: dbtime.Now(), + }) + require.NoError(t, err) + + sdkUser3.OrganizationIDs = []uuid.UUID{orgMember.OrganizationID} + users = append(users, sdkUser3) } // --- Setup done --- From 0f2104d489cadffcf9c3a9bcf14208478d547ada Mon Sep 17 00:00:00 2001 From: joobisb Date: Mon, 16 Dec 2024 13:56:48 +0530 Subject: [PATCH 10/14] fix lint --- coderd/users_test.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/coderd/users_test.go b/coderd/users_test.go index a8de254257145..bb990ae027f44 100644 --- a/coderd/users_test.go +++ b/coderd/users_test.go @@ -1526,6 +1526,7 @@ func TestUsersFilter(t *testing.T) { }) require.NoError(t, err) + // nolint:gocritic //Using system context is necessary to seed data in tests orgMember, err := api.Database.InsertOrganizationMember(dbauthz.AsSystemRestricted(ctx), database.InsertOrganizationMemberParams{ OrganizationID: firstUser.OrganizationIDs[0], UserID: user1.ID, @@ -1558,6 +1559,7 @@ func TestUsersFilter(t *testing.T) { sdkUser2, err := client.UpdateUserStatus(ctx, user2.ID.String(), codersdk.UserStatusActive) require.NoError(t, err) + // nolint:gocritic //Using system context is necessary to seed data in tests orgMember, err = api.Database.InsertOrganizationMember(dbauthz.AsSystemRestricted(ctx), database.InsertOrganizationMemberParams{ OrganizationID: firstUser.OrganizationIDs[0], UserID: user2.ID, @@ -1585,6 +1587,7 @@ func TestUsersFilter(t *testing.T) { sdkUser3, err := client.UpdateUserStatus(ctx, user3.ID.String(), codersdk.UserStatusActive) require.NoError(t, err) + // nolint:gocritic //Using system context is necessary to seed data in tests orgMember, err = api.Database.InsertOrganizationMember(dbauthz.AsSystemRestricted(ctx), database.InsertOrganizationMemberParams{ OrganizationID: firstUser.OrganizationIDs[0], UserID: user3.ID, From f00b0d568564c0ab3c50b661051056434e23d982 Mon Sep 17 00:00:00 2001 From: joobisb Date: Mon, 16 Dec 2024 23:42:11 +0530 Subject: [PATCH 11/14] fix timezone --- coderd/users_test.go | 58 +++++++++++++++----------------------------- 1 file changed, 19 insertions(+), 39 deletions(-) diff --git a/coderd/users_test.go b/coderd/users_test.go index a28d1b3d9d512..ccfd5c1c8cb31 100644 --- a/coderd/users_test.go +++ b/coderd/users_test.go @@ -26,6 +26,7 @@ import ( "github.com/coder/coder/v2/coderd/audit" "github.com/coder/coder/v2/coderd/coderdtest" "github.com/coder/coder/v2/coderd/database" + "github.com/coder/coder/v2/coderd/database/db2sdk" "github.com/coder/coder/v2/coderd/database/dbauthz" "github.com/coder/coder/v2/coderd/database/dbfake" "github.com/coder/coder/v2/coderd/database/dbgen" @@ -1523,26 +1524,19 @@ func TestUsersFilter(t *testing.T) { Email: fmt.Sprintf("before%d@coder.com", i), Username: fmt.Sprintf("before%d", i), LoginType: database.LoginTypeNone, + Status: string(codersdk.UserStatusActive), RBACRoles: []string{codersdk.RoleMember}, CreatedAt: dbtime.Time(time.Date(2022, 12, 15+i, 12, 0, 0, 0, time.UTC)), }) require.NoError(t, err) - // nolint:gocritic //Using system context is necessary to seed data in tests - orgMember, err := api.Database.InsertOrganizationMember(dbauthz.AsSystemRestricted(ctx), database.InsertOrganizationMemberParams{ - OrganizationID: firstUser.OrganizationIDs[0], - UserID: user1.ID, - Roles: []string{}, - CreatedAt: dbtime.Now(), - }) + sdkUser1 := db2sdk.User(user1, []uuid.UUID{}) + sdkUser1.CreatedAt, err = time.Parse(time.RFC3339, sdkUser1.CreatedAt.Format(time.RFC3339)) require.NoError(t, err) - - // hack: Call UpdateUserStatus to get API-formatted timestamps (without timezones) - // instead of database-formatted timestamps (with timezones) for comparison - sdkUser1, err := client.UpdateUserStatus(ctx, user1.ID.String(), codersdk.UserStatusActive) + sdkUser1.UpdatedAt, err = time.Parse(time.RFC3339, sdkUser1.UpdatedAt.Format(time.RFC3339)) + require.NoError(t, err) + sdkUser1.LastSeenAt, err = time.Parse(time.RFC3339, sdkUser1.LastSeenAt.Format(time.RFC3339)) require.NoError(t, err) - - sdkUser1.OrganizationIDs = []uuid.UUID{orgMember.OrganizationID} users = append(users, sdkUser1) // nolint:gocritic //Using system context is necessary to seed data in tests @@ -1551,26 +1545,19 @@ func TestUsersFilter(t *testing.T) { Email: fmt.Sprintf("during%d@coder.com", i), Username: fmt.Sprintf("during%d", i), LoginType: database.LoginTypeNone, + Status: string(codersdk.UserStatusActive), RBACRoles: []string{codersdk.RoleOwner}, CreatedAt: dbtime.Time(time.Date(2023, 1, 15+i, 12, 0, 0, 0, time.UTC)), }) require.NoError(t, err) - // hack: Call UpdateUserStatus to get API-formatted timestamps (without timezones) - // instead of database-formatted timestamps (with timezones) for comparison - sdkUser2, err := client.UpdateUserStatus(ctx, user2.ID.String(), codersdk.UserStatusActive) + sdkUser2 := db2sdk.User(user2, []uuid.UUID{}) + sdkUser2.CreatedAt, err = time.Parse(time.RFC3339, sdkUser2.CreatedAt.Format(time.RFC3339)) require.NoError(t, err) - - // nolint:gocritic //Using system context is necessary to seed data in tests - orgMember, err = api.Database.InsertOrganizationMember(dbauthz.AsSystemRestricted(ctx), database.InsertOrganizationMemberParams{ - OrganizationID: firstUser.OrganizationIDs[0], - UserID: user2.ID, - Roles: []string{}, - CreatedAt: dbtime.Now(), - }) + sdkUser2.UpdatedAt, err = time.Parse(time.RFC3339, sdkUser2.UpdatedAt.Format(time.RFC3339)) + require.NoError(t, err) + sdkUser2.LastSeenAt, err = time.Parse(time.RFC3339, sdkUser2.LastSeenAt.Format(time.RFC3339)) require.NoError(t, err) - - sdkUser2.OrganizationIDs = []uuid.UUID{orgMember.OrganizationID} users = append(users, sdkUser2) // nolint:gocritic // Using system context is necessary to seed data in tests @@ -1579,26 +1566,19 @@ func TestUsersFilter(t *testing.T) { Email: fmt.Sprintf("after%d@coder.com", i), Username: fmt.Sprintf("after%d", i), LoginType: database.LoginTypeNone, + Status: string(codersdk.UserStatusActive), RBACRoles: []string{codersdk.RoleOwner}, CreatedAt: dbtime.Time(time.Date(2023, 2, 15+i, 12, 0, 0, 0, time.UTC)), }) require.NoError(t, err) - // hack: Call UpdateUserStatus to get API-formatted timestamps (without timezones) - // instead of database-formatted timestamps (with timezones) for comparison - sdkUser3, err := client.UpdateUserStatus(ctx, user3.ID.String(), codersdk.UserStatusActive) + sdkUser3 := db2sdk.User(user3, []uuid.UUID{}) + sdkUser3.CreatedAt, err = time.Parse(time.RFC3339, sdkUser3.CreatedAt.Format(time.RFC3339)) require.NoError(t, err) - - // nolint:gocritic //Using system context is necessary to seed data in tests - orgMember, err = api.Database.InsertOrganizationMember(dbauthz.AsSystemRestricted(ctx), database.InsertOrganizationMemberParams{ - OrganizationID: firstUser.OrganizationIDs[0], - UserID: user3.ID, - Roles: []string{}, - CreatedAt: dbtime.Now(), - }) + sdkUser3.UpdatedAt, err = time.Parse(time.RFC3339, sdkUser3.UpdatedAt.Format(time.RFC3339)) + require.NoError(t, err) + sdkUser3.LastSeenAt, err = time.Parse(time.RFC3339, sdkUser3.LastSeenAt.Format(time.RFC3339)) require.NoError(t, err) - - sdkUser3.OrganizationIDs = []uuid.UUID{orgMember.OrganizationID} users = append(users, sdkUser3) } From 049aafad8b4fe7023da96b2fdb17f71710c5cb60 Mon Sep 17 00:00:00 2001 From: Ethan Dickson Date: Tue, 17 Dec 2024 03:00:31 +0000 Subject: [PATCH 12/14] use nil org ID slice to compare equal --- coderd/users_test.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/coderd/users_test.go b/coderd/users_test.go index ccfd5c1c8cb31..78563a406e184 100644 --- a/coderd/users_test.go +++ b/coderd/users_test.go @@ -1530,7 +1530,8 @@ func TestUsersFilter(t *testing.T) { }) require.NoError(t, err) - sdkUser1 := db2sdk.User(user1, []uuid.UUID{}) + // The expected timestamps must be parsed from strings to compare equal during `ElementsMatch` + sdkUser1 := db2sdk.User(user1, nil) sdkUser1.CreatedAt, err = time.Parse(time.RFC3339, sdkUser1.CreatedAt.Format(time.RFC3339)) require.NoError(t, err) sdkUser1.UpdatedAt, err = time.Parse(time.RFC3339, sdkUser1.UpdatedAt.Format(time.RFC3339)) @@ -1551,7 +1552,7 @@ func TestUsersFilter(t *testing.T) { }) require.NoError(t, err) - sdkUser2 := db2sdk.User(user2, []uuid.UUID{}) + sdkUser2 := db2sdk.User(user2, nil) sdkUser2.CreatedAt, err = time.Parse(time.RFC3339, sdkUser2.CreatedAt.Format(time.RFC3339)) require.NoError(t, err) sdkUser2.UpdatedAt, err = time.Parse(time.RFC3339, sdkUser2.UpdatedAt.Format(time.RFC3339)) @@ -1572,7 +1573,7 @@ func TestUsersFilter(t *testing.T) { }) require.NoError(t, err) - sdkUser3 := db2sdk.User(user3, []uuid.UUID{}) + sdkUser3 := db2sdk.User(user3, nil) sdkUser3.CreatedAt, err = time.Parse(time.RFC3339, sdkUser3.CreatedAt.Format(time.RFC3339)) require.NoError(t, err) sdkUser3.UpdatedAt, err = time.Parse(time.RFC3339, sdkUser3.UpdatedAt.Format(time.RFC3339)) From 981656f12fe18f90d6ddf7667add9cbfc54061f7 Mon Sep 17 00:00:00 2001 From: Ethan Dickson Date: Tue, 17 Dec 2024 03:26:42 +0000 Subject: [PATCH 13/14] use nil org ID slice on dbmem API to compare equal --- coderd/users_test.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/coderd/users_test.go b/coderd/users_test.go index 78563a406e184..0ccffb4b729bd 100644 --- a/coderd/users_test.go +++ b/coderd/users_test.go @@ -30,6 +30,7 @@ import ( "github.com/coder/coder/v2/coderd/database/dbauthz" "github.com/coder/coder/v2/coderd/database/dbfake" "github.com/coder/coder/v2/coderd/database/dbgen" + "github.com/coder/coder/v2/coderd/database/dbtestutil" "github.com/coder/coder/v2/coderd/database/dbtime" "github.com/coder/coder/v2/coderd/rbac" "github.com/coder/coder/v2/coderd/util/ptr" @@ -1776,6 +1777,17 @@ func TestUsersFilter(t *testing.T) { exp = append(exp, made) } } + + // TODO: This can be removed with dbmem + if !dbtestutil.WillUsePostgres() { + for i := range matched.Users { + if len(matched.Users[i].OrganizationIDs) == 0 { + matched.Users[i].OrganizationIDs = nil + } + + } + } + require.ElementsMatch(t, exp, matched.Users, "expected users returned") }) } From de63832c042258e1eb29a3440d5b0dfe023e46d5 Mon Sep 17 00:00:00 2001 From: Ethan Dickson Date: Tue, 17 Dec 2024 03:37:33 +0000 Subject: [PATCH 14/14] fmt --- coderd/users_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/coderd/users_test.go b/coderd/users_test.go index 0ccffb4b729bd..1386d76f3e0bf 100644 --- a/coderd/users_test.go +++ b/coderd/users_test.go @@ -1784,7 +1784,6 @@ func TestUsersFilter(t *testing.T) { if len(matched.Users[i].OrganizationIDs) == 0 { matched.Users[i].OrganizationIDs = nil } - } } pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy