mirror of
https://github.com/coder/coder.git
synced 2025-07-15 22:20:27 +00:00
feat: add user search query param on last_seen (#8139)
* feat: add sql filter for before/after on last_seen column
This commit is contained in:
@ -2688,6 +2688,26 @@ func (q *fakeQuerier) GetUsers(_ context.Context, params database.GetUsersParams
|
||||
users = usersFilteredByRole
|
||||
}
|
||||
|
||||
if !params.LastSeenBefore.IsZero() {
|
||||
usersFilteredByLastSeen := make([]database.User, 0, len(users))
|
||||
for i, user := range users {
|
||||
if user.LastSeenAt.Before(params.LastSeenBefore) {
|
||||
usersFilteredByLastSeen = append(usersFilteredByLastSeen, users[i])
|
||||
}
|
||||
}
|
||||
users = usersFilteredByLastSeen
|
||||
}
|
||||
|
||||
if !params.LastSeenAfter.IsZero() {
|
||||
usersFilteredByLastSeen := make([]database.User, 0, len(users))
|
||||
for i, user := range users {
|
||||
if user.LastSeenAt.After(params.LastSeenAfter) {
|
||||
usersFilteredByLastSeen = append(usersFilteredByLastSeen, users[i])
|
||||
}
|
||||
}
|
||||
users = usersFilteredByLastSeen
|
||||
}
|
||||
|
||||
beforePageCount := len(users)
|
||||
|
||||
if params.OffsetOpt > 0 {
|
||||
|
@ -206,6 +206,15 @@ func User(t testing.TB, db database.Store, orig database.User) database.User {
|
||||
LoginType: takeFirst(orig.LoginType, database.LoginTypePassword),
|
||||
})
|
||||
require.NoError(t, err, "insert user")
|
||||
|
||||
if !orig.LastSeenAt.IsZero() {
|
||||
user, err = db.UpdateUserLastSeenAt(genCtx, database.UpdateUserLastSeenAtParams{
|
||||
ID: user.ID,
|
||||
LastSeenAt: orig.LastSeenAt,
|
||||
UpdatedAt: user.UpdatedAt,
|
||||
})
|
||||
require.NoError(t, err, "user last seen")
|
||||
}
|
||||
return user
|
||||
}
|
||||
|
||||
|
@ -387,3 +387,61 @@ func TestQueuePosition(t *testing.T) {
|
||||
require.Equal(t, job.ProvisionerJob.ID, jobs[index].ID)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUserLastSeenFilter(t *testing.T) {
|
||||
t.Parallel()
|
||||
if testing.Short() {
|
||||
t.SkipNow()
|
||||
}
|
||||
t.Run("Before", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
sqlDB := testSQLDB(t)
|
||||
err := migrations.Up(sqlDB)
|
||||
require.NoError(t, err)
|
||||
db := database.New(sqlDB)
|
||||
ctx := context.Background()
|
||||
now := time.Now()
|
||||
|
||||
yesterday := dbgen.User(t, db, database.User{
|
||||
LastSeenAt: now.Add(time.Hour * -25),
|
||||
})
|
||||
today := dbgen.User(t, db, database.User{
|
||||
LastSeenAt: now,
|
||||
})
|
||||
lastWeek := dbgen.User(t, db, database.User{
|
||||
LastSeenAt: now.Add((time.Hour * -24 * 7) + (-1 * time.Hour)),
|
||||
})
|
||||
|
||||
beforeToday, err := db.GetUsers(ctx, database.GetUsersParams{
|
||||
LastSeenBefore: now.Add(time.Hour * -24),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
database.ConvertUserRows(beforeToday)
|
||||
|
||||
requireUsersMatch(t, []database.User{yesterday, lastWeek}, beforeToday, "before today")
|
||||
|
||||
justYesterday, err := db.GetUsers(ctx, database.GetUsersParams{
|
||||
LastSeenBefore: now.Add(time.Hour * -24),
|
||||
LastSeenAfter: now.Add(time.Hour * -24 * 2),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
requireUsersMatch(t, []database.User{yesterday}, justYesterday, "just yesterday")
|
||||
|
||||
all, err := db.GetUsers(ctx, database.GetUsersParams{
|
||||
LastSeenBefore: now.Add(time.Hour),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
requireUsersMatch(t, []database.User{today, yesterday, lastWeek}, all, "all")
|
||||
|
||||
allAfterLastWeek, err := db.GetUsers(ctx, database.GetUsersParams{
|
||||
LastSeenAfter: now.Add(time.Hour * -24 * 7),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
requireUsersMatch(t, []database.User{today, yesterday}, allAfterLastWeek, "after last week")
|
||||
})
|
||||
}
|
||||
|
||||
func requireUsersMatch(t testing.TB, expected []database.User, found []database.GetUsersRow, msg string) {
|
||||
t.Helper()
|
||||
require.ElementsMatch(t, expected, database.ConvertUserRows(found), msg)
|
||||
}
|
||||
|
@ -5190,22 +5190,35 @@ WHERE
|
||||
rbac_roles && $4 :: text[]
|
||||
ELSE true
|
||||
END
|
||||
-- Filter by last_seen
|
||||
AND CASE
|
||||
WHEN $5 :: timestamp with time zone != '0001-01-01 00:00:00Z' THEN
|
||||
last_seen_at <= $5
|
||||
ELSE true
|
||||
END
|
||||
AND CASE
|
||||
WHEN $6 :: timestamp with time zone != '0001-01-01 00:00:00Z' THEN
|
||||
last_seen_at >= $6
|
||||
ELSE true
|
||||
END
|
||||
-- End of filters
|
||||
ORDER BY
|
||||
-- Deterministic and consistent ordering of all users. This is to ensure consistent pagination.
|
||||
LOWER(username) ASC OFFSET $5
|
||||
LOWER(username) ASC OFFSET $7
|
||||
LIMIT
|
||||
-- A null limit means "no limit", so 0 means return all
|
||||
NULLIF($6 :: int, 0)
|
||||
NULLIF($8 :: 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"`
|
||||
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"`
|
||||
OffsetOpt int32 `db:"offset_opt" json:"offset_opt"`
|
||||
LimitOpt int32 `db:"limit_opt" json:"limit_opt"`
|
||||
}
|
||||
|
||||
type GetUsersRow struct {
|
||||
@ -5231,6 +5244,8 @@ func (q *sqlQuerier) GetUsers(ctx context.Context, arg GetUsersParams) ([]GetUse
|
||||
arg.Search,
|
||||
pq.Array(arg.Status),
|
||||
pq.Array(arg.RbacRole),
|
||||
arg.LastSeenBefore,
|
||||
arg.LastSeenAfter,
|
||||
arg.OffsetOpt,
|
||||
arg.LimitOpt,
|
||||
)
|
||||
|
@ -181,6 +181,17 @@ WHERE
|
||||
rbac_roles && @rbac_role :: text[]
|
||||
ELSE true
|
||||
END
|
||||
-- Filter by last_seen
|
||||
AND CASE
|
||||
WHEN @last_seen_before :: timestamp with time zone != '0001-01-01 00:00:00Z' THEN
|
||||
last_seen_at <= @last_seen_before
|
||||
ELSE true
|
||||
END
|
||||
AND CASE
|
||||
WHEN @last_seen_after :: timestamp with time zone != '0001-01-01 00:00:00Z' THEN
|
||||
last_seen_at >= @last_seen_after
|
||||
ELSE true
|
||||
END
|
||||
-- End of filters
|
||||
ORDER BY
|
||||
-- Deterministic and consistent ordering of all users. This is to ensure consistent pagination.
|
||||
|
Reference in New Issue
Block a user