Skip to content

feat: add "dormant" user state #8644

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 80 commits into from
Aug 2, 2023
Merged
Show file tree
Hide file tree
Changes from 44 commits
Commits
Show all changes
80 commits
Select commit Hold shift + click to select a range
30b71d0
WIP
mtojek Jul 21, 2023
0091738
generated
mtojek Jul 21, 2023
6771255
make gen
mtojek Jul 21, 2023
57547ae
Dormant API
mtojek Jul 21, 2023
f699e6f
WIP
mtojek Jul 21, 2023
3376a0f
make gen
mtojek Jul 21, 2023
4fbc5d9
userauth
mtojek Jul 21, 2023
67c4e96
fix: lint
mtojek Jul 21, 2023
daa025f
UsersFilter.tsx
mtojek Jul 21, 2023
b86f636
Site UI changes
mtojek Jul 21, 2023
05cd577
make fmt
mtojek Jul 21, 2023
7d4ad5c
Merge branch 'main' into 8128-new-user-state-dormant
mtojek Jul 24, 2023
738441b
CLI changes
mtojek Jul 24, 2023
3ff2962
CLI fix
mtojek Jul 25, 2023
08b9cbb
UI fixes
mtojek Jul 25, 2023
a3b3f65
FIXME pubsub
mtojek Jul 25, 2023
e63f7a8
Add learn more link2
mtojek Jul 25, 2023
d6d9dac
Merge branch 'main' into 8128-new-user-state-dormant
mtojek Jul 25, 2023
1454cb0
Merge branch 'main' into 8128-new-user-state-dormant
mtojek Jul 25, 2023
b7a870e
Fix: migrations
mtojek Jul 25, 2023
d90ea27
dump.sql
mtojek Jul 26, 2023
83b8f63
FIXME
mtojek Jul 26, 2023
1553871
CLI tests
mtojek Jul 26, 2023
6a959d6
oss
mtojek Jul 26, 2023
f7d3061
next fixes
mtojek Jul 26, 2023
4a530ae
Unit test: dormant to active after login
mtojek Jul 26, 2023
c359d9f
More tests
mtojek Jul 26, 2023
5b4e76f
Merge branch 'main' into 8128-new-user-state-dormant
mtojek Jul 26, 2023
972c807
Merge branch 'main' into 8128-new-user-state-dormant
mtojek Jul 27, 2023
a771d35
More tests fixed
mtojek Jul 27, 2023
5694c08
Fix migrations
mtojek Jul 27, 2023
157b6bf
Fix: OIDC callback
mtojek Jul 27, 2023
41ac4d5
Fix: refresh account status
mtojek Jul 27, 2023
bd87560
Another fix
mtojek Jul 27, 2023
9b7c40e
More fixes
mtojek Jul 27, 2023
09b7a18
docs
mtojek Jul 27, 2023
ac43fcf
make gen
mtojek Jul 27, 2023
7b02cee
Fix: dbgen
mtojek Jul 27, 2023
c3dc08d
More fixes
mtojek Jul 27, 2023
4997479
Fix: entitlements tests
mtojek Jul 27, 2023
8f1ef7b
Fix
mtojek Jul 27, 2023
ec6e294
Users Table
mtojek Jul 27, 2023
d04c6f2
TS tests
mtojek Jul 27, 2023
4f2cdc3
Docs
mtojek Jul 27, 2023
87645cb
Merge branch 'main' into 8128-new-user-state-dormant
mtojek Jul 28, 2023
63eadcb
Fix: apikey
mtojek Jul 28, 2023
699ff5a
lint
mtojek Jul 28, 2023
e0b33b3
fix?
mtojek Jul 28, 2023
db9769f
Merge branch 'main' into 8128-new-user-state-dormant
mtojek Jul 31, 2023
0eb910a
Merge branch 'main' into 8128-new-user-state-dormant
mtojek Aug 1, 2023
eb1aaa0
fix
mtojek Aug 1, 2023
6fcec6c
Rip: update user status to dormant
mtojek Aug 1, 2023
2b20a26
More trimming
mtojek Aug 1, 2023
adaecab
More trimming
mtojek Aug 1, 2023
e0a6b2f
User story
mtojek Aug 1, 2023
8765449
Merge branch 'main' into 8128-new-user-state-dormant
mtojek Aug 1, 2023
74c70a1
make update-golden-files
mtojek Aug 1, 2023
b010271
cleanup
mtojek Aug 1, 2023
8add519
bring back coder.env
mtojek Aug 1, 2023
37f9e8f
fix: migration down: update dormant users to active
mtojek Aug 1, 2023
d9da865
fix
mtojek Aug 1, 2023
2749c91
Fix: migration
mtojek Aug 1, 2023
cdbc50e
Implement job
mtojek Aug 1, 2023
916d807
fix
mtojek Aug 1, 2023
32723cf
WIP
mtojek Aug 2, 2023
f9ae3c9
Merge branch 'main' into 8128-new-user-state-dormant
mtojek Aug 2, 2023
e145b74
Write unit test for job
mtojek Aug 2, 2023
187e866
Fix
mtojek Aug 2, 2023
82b791a
info
mtojek Aug 2, 2023
4545cb6
Address PR feedback
mtojek Aug 2, 2023
85d6fae
t.Helper
mtojek Aug 2, 2023
d71ba7f
Address PR comments
mtojek Aug 2, 2023
f7ab39d
Fix: populate UpdatedAt
mtojek Aug 2, 2023
76d24c7
Update interval
mtojek Aug 2, 2023
f6890db
or dormant
mtojek Aug 2, 2023
811c637
Merge branch 'main' into 8128-new-user-state-dormant
mtojek Aug 2, 2023
d34e06c
update docs
mtojek Aug 2, 2023
67f3b17
docs fix
mtojek Aug 2, 2023
67c5500
fix
mtojek Aug 2, 2023
11143ca
docs rephrase
mtojek Aug 2, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 10 additions & 8 deletions cli/testdata/coder_users_--help.golden
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,16 @@ Manage users
Aliases: user

Subcommands
activate Update a user's status to 'active'. Active users can fully
interact with the platform
create
list
show Show a single user. Use 'me' to indicate the currently
authenticated user.
suspend Update a user's status to 'suspended'. A suspended user cannot
log into the platform
activate Update a user's status to 'active'. Active users can
fully interact with the platform
create
list
mark-as-dormant Update a user's status to 'dormant'. Dormant users are
not counted in the license plan
show Show a single user. Use 'me' to indicate the currently
authenticated user.
suspend Update a user's status to 'suspended'. A suspended user
cannot log into the platform

---
Run `coder --help` for a list of global options.
2 changes: 1 addition & 1 deletion cli/testdata/coder_users_list_--output_json.golden
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
"email": "testuser2@coder.com",
"created_at": "[timestamp]",
"last_seen_at": "[timestamp]",
"status": "active",
"status": "dormant",
"organization_ids": [
"[first org ID]"
],
Expand Down
15 changes: 15 additions & 0 deletions cli/testdata/coder_users_mark-as-dormant_--help.golden
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
Usage: coder users mark-as-dormant [flags] <username|user_id>

Update a user's status to 'dormant'. Dormant users are not counted in the
license plan

Aliases: dormant

 $ coder users mark-as-dormant example_user 

Options
-c, --column string-array (default: username,email,created_at,status)
Specify a column to filter in the table.

---
Run `coder --help` for a list of global options.
1 change: 1 addition & 0 deletions cli/users.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ func (r *RootCmd) users() *clibase.Cmd {
r.userList(),
r.userSingle(),
r.createUserStatusCommand(codersdk.UserStatusActive),
r.createUserStatusCommand(codersdk.UserStatusDormant),
r.createUserStatusCommand(codersdk.UserStatusSuspended),
},
}
Expand Down
17 changes: 14 additions & 3 deletions cli/userstatus.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ func (r *RootCmd) createUserStatusCommand(sdkStatus codersdk.UserStatus) *clibas
pastVerb = "activated"
aliases = []string{"active"}
short = "Update a user's status to 'active'. Active users can fully interact with the platform"
case codersdk.UserStatusDormant:
verb = "mark as dormant"
pastVerb = "marked as dormant"
aliases = []string{"dormant"}
short = "Update a user's status to 'dormant'. Dormant users are not counted in the license plan"
case codersdk.UserStatusSuspended:
verb = "suspend"
pastVerb = "suspended"
Expand All @@ -36,12 +41,12 @@ func (r *RootCmd) createUserStatusCommand(sdkStatus codersdk.UserStatus) *clibas

var columns []string
cmd := &clibase.Cmd{
Use: fmt.Sprintf("%s <username|user_id>", verb),
Use: fmt.Sprintf("%s <username|user_id>", strings.ReplaceAll(verb, " ", "-")),
Short: short,
Aliases: aliases,
Long: formatExamples(
example{
Command: fmt.Sprintf("coder users %s example_user", verb),
Command: fmt.Sprintf("coder users %s example_user", strings.ReplaceAll(verb, " ", "-")),
},
),
Middleware: clibase.Chain(
Expand Down Expand Up @@ -75,8 +80,14 @@ func (r *RootCmd) createUserStatusCommand(sdkStatus codersdk.UserStatus) *clibas
}

// Prompt to confirm the action
var question string
if sdkStatus == codersdk.UserStatusDormant {
question = "Are you sure you want to mark this user as dormant?"
} else {
question = fmt.Sprintf("Are you sure you want to %s this user?", verb)
}
_, err = cliui.Prompt(inv, cliui.PromptOptions{
Text: fmt.Sprintf("Are you sure you want to %s this user?", verb),
Text: question,
IsConfirm: true,
Default: cliui.ConfirmYes,
})
Expand Down
53 changes: 46 additions & 7 deletions cli/userstatus_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,13 @@ import (

func TestUserStatus(t *testing.T) {
t.Parallel()
client := coderdtest.New(t, nil)
admin := coderdtest.CreateFirstUser(t, client)
other, _ := coderdtest.CreateAnotherUser(t, client, admin.OrganizationID)
otherUser, err := other.User(context.Background(), codersdk.Me)
require.NoError(t, err, "fetch user")

t.Run("StatusSelf", func(t *testing.T) {
t.Parallel()

client := coderdtest.New(t, nil)
coderdtest.CreateFirstUser(t, client)

inv, root := clitest.New(t, "users", "suspend", "me")
clitest.SetupConfig(t, client, root)
// Yes to the prompt
Expand All @@ -34,13 +33,18 @@ func TestUserStatus(t *testing.T) {

t.Run("StatusOther", func(t *testing.T) {
t.Parallel()
require.Equal(t, codersdk.UserStatusActive, otherUser.Status, "start as active")

client := coderdtest.New(t, nil)
admin := coderdtest.CreateFirstUser(t, client)
other, _ := coderdtest.CreateAnotherUser(t, client, admin.OrganizationID)
otherUser, err := other.User(context.Background(), codersdk.Me)
require.NoError(t, err, "fetch user")

inv, root := clitest.New(t, "users", "suspend", otherUser.Username)
clitest.SetupConfig(t, client, root)
// Yes to the prompt
inv.Stdin = bytes.NewReader([]byte("yes\n"))
err := inv.Run()
err = inv.Run()
require.NoError(t, err, "suspend user")

// Check the user status
Expand All @@ -61,4 +65,39 @@ func TestUserStatus(t *testing.T) {
require.NoError(t, err, "fetch active user")
require.Equal(t, codersdk.UserStatusActive, otherUser.Status, "active user")
})

t.Run("StatusDormant", func(t *testing.T) {
t.Parallel()

client := coderdtest.New(t, nil)
admin := coderdtest.CreateFirstUser(t, client)
other, _ := coderdtest.CreateAnotherUser(t, client, admin.OrganizationID)
otherUser, err := other.User(context.Background(), codersdk.Me)
require.NoError(t, err, "fetch user")

inv, root := clitest.New(t, "users", "dormant", otherUser.Username)
clitest.SetupConfig(t, client, root)
// Yes to the prompt
inv.Stdin = bytes.NewReader([]byte("yes\n"))
err = inv.Run()
require.NoError(t, err, "mark as dormant")

// Check the user status
otherUser, err = client.User(context.Background(), otherUser.Username)
require.NoError(t, err, "fetch dormant user")
require.Equal(t, codersdk.UserStatusDormant, otherUser.Status, "marked as dormant")

// Set back to active. Try using a uuid as well
inv, root = clitest.New(t, "users", "activate", otherUser.ID.String())
clitest.SetupConfig(t, client, root)
// Yes to the prompt
inv.Stdin = bytes.NewReader([]byte("yes\n"))
err = inv.Run()
require.NoError(t, err, "suspend user")

// Check the user status
otherUser, err = client.User(context.Background(), otherUser.ID.String())
require.NoError(t, err, "fetch active user")
require.Equal(t, codersdk.UserStatusActive, otherUser.Status, "active user")
})
}
36 changes: 36 additions & 0 deletions coderd/apidoc/docs.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

38 changes: 36 additions & 2 deletions coderd/apidoc/swagger.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions coderd/coderd.go
Original file line number Diff line number Diff line change
Expand Up @@ -717,6 +717,7 @@ func New(options *Options) *API {
r.Route("/status", func(r chi.Router) {
r.Put("/suspend", api.putSuspendUserAccount())
r.Put("/activate", api.putActivateUserAccount())
r.Put("/dormant", api.putDormantUserAccount())
})
r.Route("/password", func(r chi.Router) {
r.Put("/", api.putUserPassword)
Expand Down
8 changes: 8 additions & 0 deletions coderd/coderdtest/coderdtest.go
Original file line number Diff line number Diff line change
Expand Up @@ -575,6 +575,14 @@ func createAnotherUserRetry(t *testing.T, client *codersdk.Client, organizationI
sessionToken = token.Key
}

if user.Status == codersdk.UserStatusDormant {
// Use admin client so that user's LastSeenAt is not updated.
// In general we need to refresh the user status, which should
// transition from "dormant" to "active".
user, err = client.User(context.Background(), user.Username)
require.NoError(t, err)
}

other := codersdk.New(client.URL)
other.SetSessionToken(sessionToken)
t.Cleanup(func() {
Expand Down
2 changes: 1 addition & 1 deletion coderd/database/dbfake/dbfake.go
Original file line number Diff line number Diff line change
Expand Up @@ -3854,7 +3854,7 @@ func (q *FakeQuerier) InsertUser(_ context.Context, arg database.InsertUserParam
CreatedAt: arg.CreatedAt,
UpdatedAt: arg.UpdatedAt,
Username: arg.Username,
Status: database.UserStatusActive,
Status: database.UserStatusDormant,
RBACRoles: arg.RBACRoles,
LoginType: arg.LoginType,
}
Expand Down
7 changes: 7 additions & 0 deletions coderd/database/dbgen/dbgen.go
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,13 @@ func User(t testing.TB, db database.Store, orig database.User) database.User {
})
require.NoError(t, err, "insert user")

user, err = db.UpdateUserStatus(genCtx, database.UpdateUserStatusParams{
ID: user.ID,
Status: database.UserStatusActive,
UpdatedAt: database.Now(),
})
require.NoError(t, err, "insert user")

if !orig.LastSeenAt.IsZero() {
user, err = db.UpdateUserLastSeenAt(genCtx, database.UpdateUserLastSeenAtParams{
ID: user.ID,
Expand Down
7 changes: 5 additions & 2 deletions coderd/database/dump.sql

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
-- It's not possible to drop enum values from enum types, so the UP has "IF NOT EXISTS"
2 changes: 2 additions & 0 deletions coderd/database/migrations/000143_user_status_dormant.up.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
ALTER TYPE user_status ADD VALUE IF NOT EXISTS 'dormant';
COMMENT ON TYPE user_status IS 'Defines the user status: active, dormant, or suspended.';
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ALTER TABLE users ALTER COLUMN status SET DEFAULT 'active'::user_status;
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ALTER TABLE users ALTER COLUMN status SET DEFAULT 'dormant'::user_status;
6 changes: 5 additions & 1 deletion coderd/database/models.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading