Skip to content

feat(coderd): add ability to mark workspaces as favorite #11673

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

Closed
wants to merge 27 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
46ca00d
feat(coderd): add user_pinned_workspaces table
johnstcn Jan 16, 2024
015aabb
add unique index
johnstcn Jan 17, 2024
0b0dce6
rename migration
johnstcn Jan 17, 2024
9878da7
add queries to pin/unpin workspace
johnstcn Jan 17, 2024
4fba238
add pinned status to GetWorkspaces/GetAuthorizedWorkspaces queries
johnstcn Jan 17, 2024
83b03d9
add API endpoints and test (currently fails)
johnstcn Jan 18, 2024
0961298
rename to favorite workspace
johnstcn Jan 18, 2024
a814b65
move to top-level test
johnstcn Jan 22, 2024
89d618d
s/favored/favorite/g
johnstcn Jan 22, 2024
7238b95
move favorite status to workspaces table to avoid sqlc join sadness
johnstcn Jan 22, 2024
4eef59a
more wiring
johnstcn Jan 22, 2024
0f8904d
improve test to include sort order of favorites
johnstcn Jan 22, 2024
d921aa0
fix swagger summary
johnstcn Jan 22, 2024
b68c18e
make gen
johnstcn Jan 22, 2024
3d0545a
use dbfake for testing sort order
johnstcn Jan 22, 2024
46103a4
reduce scope of TestWorkspaceFavoriteUnfavorite to not include sortin…
johnstcn Jan 22, 2024
f6c0361
beef up sort order test
johnstcn Jan 22, 2024
89f0f50
rm unnecessary fixture
johnstcn Jan 22, 2024
c9ed43c
fix sort ordering, dbfake still TODO
johnstcn Jan 22, 2024
ff3cde2
remove unnecessary change to CreateFirstUser
johnstcn Jan 23, 2024
f735b69
best-effort fix but testing dbmem sort order is a waste of time
johnstcn Jan 23, 2024
f6e9531
requestor->requester
johnstcn Jan 23, 2024
34f2902
fixup! remove unnecessary change to CreateFirstUser
johnstcn Jan 23, 2024
99c7f96
remove unused resource
johnstcn Jan 23, 2024
d530546
fix sort ordering bug in dmem
johnstcn Jan 23, 2024
6392db9
add test case
johnstcn Jan 23, 2024
9979c53
remove need for conditional ordering
johnstcn Jan 23, 2024
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
Prev Previous commit
Next Next commit
s/favored/favorite/g
  • Loading branch information
johnstcn committed Jan 23, 2024
commit 89d618df8641237efaefe60f7dded5e44e7f4765
6 changes: 3 additions & 3 deletions coderd/apidoc/docs.go

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

6 changes: 3 additions & 3 deletions coderd/apidoc/swagger.json

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

28 changes: 14 additions & 14 deletions coderd/database/dbauthz/dbauthz.go
Original file line number Diff line number Diff line change
Expand Up @@ -656,20 +656,6 @@ func authorizedTemplateVersionFromJob(ctx context.Context, q *querier, job datab
}
}

func (q *querier) FavoriteWorkspace(ctx context.Context, arg database.FavoriteWorkspaceParams) error {
if _, err := q.GetWorkspaceByID(ctx, arg.WorkspaceID); err != nil {
return err
}
return q.db.FavoriteWorkspace(ctx, arg)
}

func (q *querier) UnfavoriteWorkspace(ctx context.Context, arg database.UnfavoriteWorkspaceParams) error {
if _, err := q.GetWorkspaceByID(ctx, arg.WorkspaceID); err != nil {
return err
}
return q.db.UnfavoriteWorkspace(ctx, arg)
}

func (q *querier) AcquireLock(ctx context.Context, id int64) error {
return q.db.AcquireLock(ctx, id)
}
Expand Down Expand Up @@ -905,6 +891,13 @@ func (q *querier) DeleteTailnetTunnel(ctx context.Context, arg database.DeleteTa
return q.db.DeleteTailnetTunnel(ctx, arg)
}

func (q *querier) FavoriteWorkspace(ctx context.Context, arg database.FavoriteWorkspaceParams) error {
if _, err := q.GetWorkspaceByID(ctx, arg.WorkspaceID); err != nil {
return err
}
return q.db.FavoriteWorkspace(ctx, arg)
}

func (q *querier) GetAPIKeyByID(ctx context.Context, id string) (database.APIKey, error) {
return fetch(q.log, q.auth, q.db.GetAPIKeyByID)(ctx, id)
}
Expand Down Expand Up @@ -2523,6 +2516,13 @@ func (q *querier) UnarchiveTemplateVersion(ctx context.Context, arg database.Una
return q.db.UnarchiveTemplateVersion(ctx, arg)
}

func (q *querier) UnfavoriteWorkspace(ctx context.Context, arg database.UnfavoriteWorkspaceParams) error {
if _, err := q.GetWorkspaceByID(ctx, arg.WorkspaceID); err != nil {
return err
}
return q.db.UnfavoriteWorkspace(ctx, arg)
}

func (q *querier) UpdateAPIKeyByID(ctx context.Context, arg database.UpdateAPIKeyByIDParams) error {
fetch := func(ctx context.Context, arg database.UpdateAPIKeyByIDParams) (database.APIKey, error) {
return q.db.GetAPIKeyByID(ctx, arg.ID)
Expand Down
82 changes: 41 additions & 41 deletions coderd/database/dbmem/dbmem.go
Original file line number Diff line number Diff line change
Expand Up @@ -732,47 +732,6 @@ func isNotNull(v interface{}) bool {
return reflect.ValueOf(v).FieldByName("Valid").Bool()
}

func (q *FakeQuerier) FavoriteWorkspace(_ context.Context, arg database.FavoriteWorkspaceParams) error {
err := validateDatabaseType(arg)
if err != nil {
return err
}

q.mutex.Lock()
defer q.mutex.Unlock()

for _, upw := range q.favoriteWorkspaces {
if arg.UserID == upw.UserID && arg.WorkspaceID == upw.WorkspaceID {
return errDuplicateKey
}
}
return nil
}

func (q *FakeQuerier) UnfavoriteWorkspace(_ context.Context, arg database.UnfavoriteWorkspaceParams) error {
err := validateDatabaseType(arg)
if err != nil {
return err
}

q.mutex.Lock()
defer q.mutex.Unlock()

for index, upw := range q.favoriteWorkspaces {
if upw.UserID != arg.UserID {
continue
}
if upw.WorkspaceID != arg.WorkspaceID {
continue
}
q.favoriteWorkspaces[index] = q.favoriteWorkspaces[len(q.apiKeys)-1]
q.favoriteWorkspaces = q.favoriteWorkspaces[:len(q.favoriteWorkspaces)-1]
return nil
}

return nil
}

func (*FakeQuerier) AcquireLock(_ context.Context, _ int64) error {
return xerrors.New("AcquireLock must only be called within a transaction")
}
Expand Down Expand Up @@ -1358,6 +1317,23 @@ func (*FakeQuerier) DeleteTailnetTunnel(_ context.Context, arg database.DeleteTa
return database.DeleteTailnetTunnelRow{}, ErrUnimplemented
}

func (q *FakeQuerier) FavoriteWorkspace(_ context.Context, arg database.FavoriteWorkspaceParams) error {
err := validateDatabaseType(arg)
if err != nil {
return err
}

q.mutex.Lock()
defer q.mutex.Unlock()

for _, upw := range q.favoriteWorkspaces {
if arg.UserID == upw.UserID && arg.WorkspaceID == upw.WorkspaceID {
return errDuplicateKey
}
}
return nil
}

func (q *FakeQuerier) GetAPIKeyByID(_ context.Context, id string) (database.APIKey, error) {
q.mutex.RLock()
defer q.mutex.RUnlock()
Expand Down Expand Up @@ -6027,6 +6003,30 @@ func (q *FakeQuerier) UnarchiveTemplateVersion(_ context.Context, arg database.U
return sql.ErrNoRows
}

func (q *FakeQuerier) UnfavoriteWorkspace(_ context.Context, arg database.UnfavoriteWorkspaceParams) error {
err := validateDatabaseType(arg)
if err != nil {
return err
}

q.mutex.Lock()
defer q.mutex.Unlock()

for index, upw := range q.favoriteWorkspaces {
if upw.UserID != arg.UserID {
continue
}
if upw.WorkspaceID != arg.WorkspaceID {
continue
}
q.favoriteWorkspaces[index] = q.favoriteWorkspaces[len(q.apiKeys)-1]
q.favoriteWorkspaces = q.favoriteWorkspaces[:len(q.favoriteWorkspaces)-1]
return nil
}

return nil
}

func (q *FakeQuerier) UpdateAPIKeyByID(_ context.Context, arg database.UpdateAPIKeyByIDParams) error {
if err := validateDatabaseType(arg); err != nil {
return err
Expand Down
2 changes: 1 addition & 1 deletion coderd/database/modelqueries.go
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,7 @@ func (q *sqlQuerier) GetAuthorizedWorkspaces(ctx context.Context, arg GetWorkspa
&i.TemplateName,
&i.TemplateVersionID,
&i.TemplateVersionName,
&i.Favored,
&i.Favorite,
&i.Count,
); err != nil {
return nil, err
Expand Down
8 changes: 4 additions & 4 deletions coderd/database/queries.sql.go

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

4 changes: 2 additions & 2 deletions coderd/database/queries/workspaces.sql
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ SELECT
COALESCE(template_name.template_name, 'unknown') as template_name,
latest_build.template_version_id,
latest_build.template_version_name,
(fws.user_id IS NOT NULL)::boolean AS favored,
(fws.user_id IS NOT NULL)::boolean AS favorite,
COUNT(*) OVER () as count
FROM
workspaces
Expand Down Expand Up @@ -276,7 +276,7 @@ WHERE
-- Authorize Filter clause will be injected below in GetAuthorizedWorkspaces
-- @authorize_filter
ORDER BY
favored DESC,
favorite DESC,
(latest_build.completed_at IS NOT NULL AND
latest_build.canceled_at IS NULL AND
latest_build.error IS NULL AND
Expand Down
28 changes: 14 additions & 14 deletions coderd/workspaces_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2950,17 +2950,17 @@ func TestWorkspaceFavoriteUnfavorite(t *testing.T) {
workspaces, err := memberClient.Workspaces(ctx, codersdk.WorkspaceFilter{})
require.NoError(t, err)
require.Len(t, workspaces.Workspaces, 1)
require.False(t, workspaces.Workspaces[0].Favored)
require.False(t, workspaces.Workspaces[0].Favorite)
ws, err := memberClient.Workspace(ctx, wsb1.Workspace.ID)
require.NoError(t, err)
require.False(t, ws.Favored)
require.False(t, ws.Favorite)

// Also not for owner.
workspaces, err = client.Workspaces(ctx, codersdk.WorkspaceFilter{})
require.NoError(t, err)
require.Len(t, workspaces.Workspaces, 2)
require.False(t, workspaces.Workspaces[0].Favored)
require.False(t, workspaces.Workspaces[1].Favored)
require.False(t, workspaces.Workspaces[0].Favorite)
require.False(t, workspaces.Workspaces[1].Favorite)

// When member pins workspace
err = memberClient.FavoriteWorkspace(ctx, wsb1.Workspace.ID)
Expand All @@ -2970,20 +2970,20 @@ func TestWorkspaceFavoriteUnfavorite(t *testing.T) {
workspaces, err = memberClient.Workspaces(ctx, codersdk.WorkspaceFilter{})
require.NoError(t, err)
require.Len(t, workspaces.Workspaces, 1)
require.True(t, workspaces.Workspaces[0].Favored)
require.True(t, workspaces.Workspaces[0].Favorite)
ws, err = memberClient.Workspace(ctx, wsb1.Workspace.ID)
require.NoError(t, err)
require.True(t, ws.Favored)
require.True(t, ws.Favorite)

// But not for someone else
workspaces, err = client.Workspaces(ctx, codersdk.WorkspaceFilter{})
require.NoError(t, err)
require.Len(t, workspaces.Workspaces, 2)
require.False(t, workspaces.Workspaces[0].Favored)
require.False(t, workspaces.Workspaces[1].Favored)
require.False(t, workspaces.Workspaces[0].Favorite)
require.False(t, workspaces.Workspaces[1].Favorite)
ws, err = client.Workspace(ctx, wsb1.Workspace.ID)
require.NoError(t, err)
require.False(t, ws.Favored)
require.False(t, ws.Favorite)

// When member unpins workspace
err = memberClient.UnfavoriteWorkspace(ctx, wsb1.Workspace.ID)
Expand All @@ -2993,18 +2993,18 @@ func TestWorkspaceFavoriteUnfavorite(t *testing.T) {
workspaces, err = memberClient.Workspaces(ctx, codersdk.WorkspaceFilter{})
require.NoError(t, err)
require.Len(t, workspaces.Workspaces, 1)
require.False(t, workspaces.Workspaces[0].Favored)
require.False(t, workspaces.Workspaces[0].Favorite)
ws, err = memberClient.Workspace(ctx, wsb1.Workspace.ID)
require.NoError(t, err)
require.False(t, ws.Favored)
require.False(t, ws.Favorite)

// Assert invariant: workspace should remain unpinned for a different user
workspaces, err = client.Workspaces(ctx, codersdk.WorkspaceFilter{})
require.NoError(t, err)
require.Len(t, workspaces.Workspaces, 2)
require.False(t, workspaces.Workspaces[0].Favored)
require.False(t, workspaces.Workspaces[1].Favored)
require.False(t, workspaces.Workspaces[0].Favorite)
require.False(t, workspaces.Workspaces[1].Favorite)
ws, err = client.Workspace(ctx, wsb1.Workspace.ID)
require.NoError(t, err)
require.False(t, ws.Favored)
require.False(t, ws.Favorite)
}
2 changes: 1 addition & 1 deletion codersdk/workspaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ type Workspace struct {
Health WorkspaceHealth `json:"health"`
AutomaticUpdates AutomaticUpdates `json:"automatic_updates" enums:"always,never"`
AllowRenames bool `json:"allow_renames"`
Favored bool `json:"favored"`
Favorite bool `json:"favorite"`
}

func (w Workspace) FullName() string {
Expand Down
6 changes: 3 additions & 3 deletions docs/api/schemas.md

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

Loading