feat(coderd): allow workspace owners to mark workspaces as favorite (#11791)

- Adds column `favorite` to workspaces table
- Adds API endpoints to favorite/unfavorite workspaces
- Modifies sorting order to return owners' favorite workspaces first
This commit is contained in:
Cian Johnston
2024-01-24 13:39:19 +00:00
committed by GitHub
parent 6145da8a9e
commit f92336c4d5
27 changed files with 646 additions and 50 deletions
+14
View File
@@ -891,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, id uuid.UUID) error {
fetch := func(ctx context.Context, id uuid.UUID) (database.Workspace, error) {
return q.db.GetWorkspaceByID(ctx, id)
}
return update(q.log, q.auth, fetch, q.db.FavoriteWorkspace)(ctx, id)
}
func (q *querier) GetAPIKeyByID(ctx context.Context, id string) (database.APIKey, error) {
return fetch(q.log, q.auth, q.db.GetAPIKeyByID)(ctx, id)
}
@@ -2509,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, id uuid.UUID) error {
fetch := func(ctx context.Context, id uuid.UUID) (database.Workspace, error) {
return q.db.GetWorkspaceByID(ctx, id)
}
return update(q.log, q.auth, fetch, q.db.UnfavoriteWorkspace)(ctx, id)
}
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)
+10
View File
@@ -1578,6 +1578,16 @@ func (s *MethodTestSuite) TestWorkspace() {
WorkspaceID: ws.ID,
}).Asserts(ws, rbac.ActionUpdate).Returns()
}))
s.Run("FavoriteWorkspace", s.Subtest(func(db database.Store, check *expects) {
u := dbgen.User(s.T(), db, database.User{})
ws := dbgen.Workspace(s.T(), db, database.Workspace{OwnerID: u.ID})
check.Args(ws.ID).Asserts(ws, rbac.ActionUpdate).Returns()
}))
s.Run("UnfavoriteWorkspace", s.Subtest(func(db database.Store, check *expects) {
u := dbgen.User(s.T(), db, database.User{})
ws := dbgen.Workspace(s.T(), db, database.Workspace{OwnerID: u.ID})
check.Args(ws.ID).Asserts(ws, rbac.ActionUpdate).Returns()
}))
}
func (s *MethodTestSuite) TestExtraMethods() {
+52 -4
View File
@@ -359,6 +359,7 @@ func (q *FakeQuerier) convertToWorkspaceRowsNoLock(ctx context.Context, workspac
DeletingAt: w.DeletingAt,
Count: count,
AutomaticUpdates: w.AutomaticUpdates,
Favorite: w.Favorite,
}
for _, t := range q.templates {
@@ -1315,6 +1316,25 @@ func (*FakeQuerier) DeleteTailnetTunnel(_ context.Context, arg database.DeleteTa
return database.DeleteTailnetTunnelRow{}, ErrUnimplemented
}
func (q *FakeQuerier) FavoriteWorkspace(_ context.Context, arg uuid.UUID) error {
err := validateDatabaseType(arg)
if err != nil {
return err
}
q.mutex.Lock()
defer q.mutex.Unlock()
for i := 0; i < len(q.workspaces); i++ {
if q.workspaces[i].ID != arg {
continue
}
q.workspaces[i].Favorite = true
return nil
}
return nil
}
func (q *FakeQuerier) GetAPIKeyByID(_ context.Context, id string) (database.APIKey, error) {
q.mutex.RLock()
defer q.mutex.RUnlock()
@@ -5984,6 +6004,26 @@ func (q *FakeQuerier) UnarchiveTemplateVersion(_ context.Context, arg database.U
return sql.ErrNoRows
}
func (q *FakeQuerier) UnfavoriteWorkspace(_ context.Context, arg uuid.UUID) error {
err := validateDatabaseType(arg)
if err != nil {
return err
}
q.mutex.Lock()
defer q.mutex.Unlock()
for i := 0; i < len(q.workspaces); i++ {
if q.workspaces[i].ID != arg {
continue
}
q.workspaces[i].Favorite = false
return nil
}
return nil
}
func (q *FakeQuerier) UpdateAPIKeyByID(_ context.Context, arg database.UpdateAPIKeyByIDParams) error {
if err := validateDatabaseType(arg); err != nil {
return err
@@ -7713,7 +7753,15 @@ func (q *FakeQuerier) GetAuthorizedWorkspaces(ctx context.Context, arg database.
w1 := workspaces[i]
w2 := workspaces[j]
// Order by: running first
// Order by: favorite first
if arg.RequesterID == w1.OwnerID && w1.Favorite {
return true
}
if arg.RequesterID == w2.OwnerID && w2.Favorite {
return false
}
// Order by: running
w1IsRunning := isRunning(preloadedWorkspaceBuilds[w1.ID], preloadedProvisionerJobs[w1.ID])
w2IsRunning := isRunning(preloadedWorkspaceBuilds[w2.ID], preloadedProvisionerJobs[w2.ID])
@@ -7726,12 +7774,12 @@ func (q *FakeQuerier) GetAuthorizedWorkspaces(ctx context.Context, arg database.
}
// Order by: usernames
if w1.ID != w2.ID {
return sort.StringsAreSorted([]string{preloadedUsers[w1.ID].Username, preloadedUsers[w2.ID].Username})
if strings.Compare(preloadedUsers[w1.ID].Username, preloadedUsers[w2.ID].Username) < 0 {
return true
}
// Order by: workspace names
return sort.StringsAreSorted([]string{w1.Name, w2.Name})
return strings.Compare(w1.Name, w2.Name) < 0
})
beforePageCount := len(workspaces)
+14
View File
@@ -300,6 +300,13 @@ func (m metricsStore) DeleteTailnetTunnel(ctx context.Context, arg database.Dele
return r0, r1
}
func (m metricsStore) FavoriteWorkspace(ctx context.Context, arg uuid.UUID) error {
start := time.Now()
r0 := m.s.FavoriteWorkspace(ctx, arg)
m.queryLatencies.WithLabelValues("FavoriteWorkspace").Observe(time.Since(start).Seconds())
return r0
}
func (m metricsStore) GetAPIKeyByID(ctx context.Context, id string) (database.APIKey, error) {
start := time.Now()
apiKey, err := m.s.GetAPIKeyByID(ctx, id)
@@ -1614,6 +1621,13 @@ func (m metricsStore) UnarchiveTemplateVersion(ctx context.Context, arg database
return r0
}
func (m metricsStore) UnfavoriteWorkspace(ctx context.Context, arg uuid.UUID) error {
start := time.Now()
r0 := m.s.UnfavoriteWorkspace(ctx, arg)
m.queryLatencies.WithLabelValues("UnfavoriteWorkspace").Observe(time.Since(start).Seconds())
return r0
}
func (m metricsStore) UpdateAPIKeyByID(ctx context.Context, arg database.UpdateAPIKeyByIDParams) error {
start := time.Now()
err := m.s.UpdateAPIKeyByID(ctx, arg)
+28
View File
@@ -500,6 +500,20 @@ func (mr *MockStoreMockRecorder) DeleteTailnetTunnel(arg0, arg1 any) *gomock.Cal
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteTailnetTunnel", reflect.TypeOf((*MockStore)(nil).DeleteTailnetTunnel), arg0, arg1)
}
// FavoriteWorkspace mocks base method.
func (m *MockStore) FavoriteWorkspace(arg0 context.Context, arg1 uuid.UUID) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "FavoriteWorkspace", arg0, arg1)
ret0, _ := ret[0].(error)
return ret0
}
// FavoriteWorkspace indicates an expected call of FavoriteWorkspace.
func (mr *MockStoreMockRecorder) FavoriteWorkspace(arg0, arg1 any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FavoriteWorkspace", reflect.TypeOf((*MockStore)(nil).FavoriteWorkspace), arg0, arg1)
}
// GetAPIKeyByID mocks base method.
func (m *MockStore) GetAPIKeyByID(arg0 context.Context, arg1 string) (database.APIKey, error) {
m.ctrl.T.Helper()
@@ -3410,6 +3424,20 @@ func (mr *MockStoreMockRecorder) UnarchiveTemplateVersion(arg0, arg1 any) *gomoc
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UnarchiveTemplateVersion", reflect.TypeOf((*MockStore)(nil).UnarchiveTemplateVersion), arg0, arg1)
}
// UnfavoriteWorkspace mocks base method.
func (m *MockStore) UnfavoriteWorkspace(arg0 context.Context, arg1 uuid.UUID) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "UnfavoriteWorkspace", arg0, arg1)
ret0, _ := ret[0].(error)
return ret0
}
// UnfavoriteWorkspace indicates an expected call of UnfavoriteWorkspace.
func (mr *MockStoreMockRecorder) UnfavoriteWorkspace(arg0, arg1 any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UnfavoriteWorkspace", reflect.TypeOf((*MockStore)(nil).UnfavoriteWorkspace), arg0, arg1)
}
// UpdateAPIKeyByID mocks base method.
func (m *MockStore) UpdateAPIKeyByID(arg0 context.Context, arg1 database.UpdateAPIKeyByIDParams) error {
m.ctrl.T.Helper()
+4 -1
View File
@@ -1235,9 +1235,12 @@ CREATE TABLE workspaces (
last_used_at timestamp with time zone DEFAULT '0001-01-01 00:00:00+00'::timestamp with time zone NOT NULL,
dormant_at timestamp with time zone,
deleting_at timestamp with time zone,
automatic_updates automatic_updates DEFAULT 'never'::automatic_updates NOT NULL
automatic_updates automatic_updates DEFAULT 'never'::automatic_updates NOT NULL,
favorite boolean DEFAULT false NOT NULL
);
COMMENT ON COLUMN workspaces.favorite IS 'Favorite is true if the workspace owner has favorited the workspace.';
ALTER TABLE ONLY licenses ALTER COLUMN id SET DEFAULT nextval('licenses_id_seq'::regclass);
ALTER TABLE ONLY provisioner_job_logs ALTER COLUMN id SET DEFAULT nextval('provisioner_job_logs_id_seq'::regclass);
@@ -0,0 +1 @@
ALTER TABLE ONLY workspaces DROP COLUMN favorite;
@@ -0,0 +1,3 @@
ALTER TABLE ONLY workspaces
ADD COLUMN favorite boolean NOT NULL DEFAULT false;
COMMENT ON COLUMN workspaces.favorite IS 'Favorite is true if the workspace owner has favorited the workspace.';
+1
View File
@@ -373,6 +373,7 @@ func ConvertWorkspaceRows(rows []GetWorkspacesRow) []Workspace {
DormantAt: r.DormantAt,
DeletingAt: r.DeletingAt,
AutomaticUpdates: r.AutomaticUpdates,
Favorite: r.Favorite,
}
}
+2
View File
@@ -226,6 +226,7 @@ func (q *sqlQuerier) GetAuthorizedWorkspaces(ctx context.Context, arg GetWorkspa
arg.LastUsedBefore,
arg.LastUsedAfter,
arg.UsingActive,
arg.RequesterID,
arg.Offset,
arg.Limit,
)
@@ -251,6 +252,7 @@ func (q *sqlQuerier) GetAuthorizedWorkspaces(ctx context.Context, arg GetWorkspa
&i.DormantAt,
&i.DeletingAt,
&i.AutomaticUpdates,
&i.Favorite,
&i.TemplateName,
&i.TemplateVersionID,
&i.TemplateVersionName,
+2
View File
@@ -2185,6 +2185,8 @@ type Workspace struct {
DormantAt sql.NullTime `db:"dormant_at" json:"dormant_at"`
DeletingAt sql.NullTime `db:"deleting_at" json:"deleting_at"`
AutomaticUpdates AutomaticUpdates `db:"automatic_updates" json:"automatic_updates"`
// Favorite is true if the workspace owner has favorited the workspace.
Favorite bool `db:"favorite" json:"favorite"`
}
type WorkspaceAgent struct {
+2
View File
@@ -75,6 +75,7 @@ type sqlcQuerier interface {
DeleteTailnetClientSubscription(ctx context.Context, arg DeleteTailnetClientSubscriptionParams) error
DeleteTailnetPeer(ctx context.Context, arg DeleteTailnetPeerParams) (DeleteTailnetPeerRow, error)
DeleteTailnetTunnel(ctx context.Context, arg DeleteTailnetTunnelParams) (DeleteTailnetTunnelRow, error)
FavoriteWorkspace(ctx context.Context, id uuid.UUID) error
GetAPIKeyByID(ctx context.Context, id string) (APIKey, error)
// there is no unique constraint on empty token names
GetAPIKeyByName(ctx context.Context, arg GetAPIKeyByNameParams) (APIKey, error)
@@ -321,6 +322,7 @@ type sqlcQuerier interface {
TryAcquireLock(ctx context.Context, pgTryAdvisoryXactLock int64) (bool, error)
// This will always work regardless of the current state of the template version.
UnarchiveTemplateVersion(ctx context.Context, arg UnarchiveTemplateVersionParams) error
UnfavoriteWorkspace(ctx context.Context, id uuid.UUID) error
UpdateAPIKeyByID(ctx context.Context, arg UpdateAPIKeyByIDParams) error
UpdateExternalAuthLink(ctx context.Context, arg UpdateExternalAuthLinkParams) (ExternalAuthLink, error)
UpdateGitSSHKey(ctx context.Context, arg UpdateGitSSHKeyParams) (GitSSHKey, error)
+44 -12
View File
@@ -10849,6 +10849,15 @@ func (q *sqlQuerier) BatchUpdateWorkspaceLastUsedAt(ctx context.Context, arg Bat
return err
}
const favoriteWorkspace = `-- name: FavoriteWorkspace :exec
UPDATE workspaces SET favorite = true WHERE id = $1
`
func (q *sqlQuerier) FavoriteWorkspace(ctx context.Context, id uuid.UUID) error {
_, err := q.db.ExecContext(ctx, favoriteWorkspace, id)
return err
}
const getDeploymentWorkspaceStats = `-- name: GetDeploymentWorkspaceStats :one
WITH workspaces_with_jobs AS (
SELECT
@@ -10935,7 +10944,7 @@ func (q *sqlQuerier) GetDeploymentWorkspaceStats(ctx context.Context) (GetDeploy
const getWorkspaceByAgentID = `-- name: GetWorkspaceByAgentID :one
SELECT
workspaces.id, workspaces.created_at, workspaces.updated_at, workspaces.owner_id, workspaces.organization_id, workspaces.template_id, workspaces.deleted, workspaces.name, workspaces.autostart_schedule, workspaces.ttl, workspaces.last_used_at, workspaces.dormant_at, workspaces.deleting_at, workspaces.automatic_updates,
workspaces.id, workspaces.created_at, workspaces.updated_at, workspaces.owner_id, workspaces.organization_id, workspaces.template_id, workspaces.deleted, workspaces.name, workspaces.autostart_schedule, workspaces.ttl, workspaces.last_used_at, workspaces.dormant_at, workspaces.deleting_at, workspaces.automatic_updates, workspaces.favorite,
templates.name as template_name
FROM
workspaces
@@ -10989,6 +10998,7 @@ func (q *sqlQuerier) GetWorkspaceByAgentID(ctx context.Context, agentID uuid.UUI
&i.Workspace.DormantAt,
&i.Workspace.DeletingAt,
&i.Workspace.AutomaticUpdates,
&i.Workspace.Favorite,
&i.TemplateName,
)
return i, err
@@ -10996,7 +11006,7 @@ func (q *sqlQuerier) GetWorkspaceByAgentID(ctx context.Context, agentID uuid.UUI
const getWorkspaceByID = `-- name: GetWorkspaceByID :one
SELECT
id, created_at, updated_at, owner_id, organization_id, template_id, deleted, name, autostart_schedule, ttl, last_used_at, dormant_at, deleting_at, automatic_updates
id, created_at, updated_at, owner_id, organization_id, template_id, deleted, name, autostart_schedule, ttl, last_used_at, dormant_at, deleting_at, automatic_updates, favorite
FROM
workspaces
WHERE
@@ -11023,13 +11033,14 @@ func (q *sqlQuerier) GetWorkspaceByID(ctx context.Context, id uuid.UUID) (Worksp
&i.DormantAt,
&i.DeletingAt,
&i.AutomaticUpdates,
&i.Favorite,
)
return i, err
}
const getWorkspaceByOwnerIDAndName = `-- name: GetWorkspaceByOwnerIDAndName :one
SELECT
id, created_at, updated_at, owner_id, organization_id, template_id, deleted, name, autostart_schedule, ttl, last_used_at, dormant_at, deleting_at, automatic_updates
id, created_at, updated_at, owner_id, organization_id, template_id, deleted, name, autostart_schedule, ttl, last_used_at, dormant_at, deleting_at, automatic_updates, favorite
FROM
workspaces
WHERE
@@ -11063,13 +11074,14 @@ func (q *sqlQuerier) GetWorkspaceByOwnerIDAndName(ctx context.Context, arg GetWo
&i.DormantAt,
&i.DeletingAt,
&i.AutomaticUpdates,
&i.Favorite,
)
return i, err
}
const getWorkspaceByWorkspaceAppID = `-- name: GetWorkspaceByWorkspaceAppID :one
SELECT
id, created_at, updated_at, owner_id, organization_id, template_id, deleted, name, autostart_schedule, ttl, last_used_at, dormant_at, deleting_at, automatic_updates
id, created_at, updated_at, owner_id, organization_id, template_id, deleted, name, autostart_schedule, ttl, last_used_at, dormant_at, deleting_at, automatic_updates, favorite
FROM
workspaces
WHERE
@@ -11122,6 +11134,7 @@ func (q *sqlQuerier) GetWorkspaceByWorkspaceAppID(ctx context.Context, workspace
&i.DormantAt,
&i.DeletingAt,
&i.AutomaticUpdates,
&i.Favorite,
)
return i, err
}
@@ -11166,7 +11179,7 @@ func (q *sqlQuerier) GetWorkspaceUniqueOwnerCountByTemplateIDs(ctx context.Conte
const getWorkspaces = `-- name: GetWorkspaces :many
SELECT
workspaces.id, workspaces.created_at, workspaces.updated_at, workspaces.owner_id, workspaces.organization_id, workspaces.template_id, workspaces.deleted, workspaces.name, workspaces.autostart_schedule, workspaces.ttl, workspaces.last_used_at, workspaces.dormant_at, workspaces.deleting_at, workspaces.automatic_updates,
workspaces.id, workspaces.created_at, workspaces.updated_at, workspaces.owner_id, workspaces.organization_id, workspaces.template_id, workspaces.deleted, workspaces.name, workspaces.autostart_schedule, workspaces.ttl, workspaces.last_used_at, workspaces.dormant_at, workspaces.deleting_at, workspaces.automatic_updates, workspaces.favorite,
COALESCE(template.name, 'unknown') as template_name,
latest_build.template_version_id,
latest_build.template_version_name,
@@ -11355,6 +11368,8 @@ WHERE
-- Authorize Filter clause will be injected below in GetAuthorizedWorkspaces
-- @authorize_filter
ORDER BY
-- To ensure that 'favorite' workspaces show up first in the list only for their owner.
CASE WHEN workspaces.owner_id = $14 AND workspaces.favorite THEN 0 ELSE 1 END ASC,
(latest_build.completed_at IS NOT NULL AND
latest_build.canceled_at IS NULL AND
latest_build.error IS NULL AND
@@ -11363,11 +11378,11 @@ ORDER BY
LOWER(workspaces.name) ASC
LIMIT
CASE
WHEN $15 :: integer > 0 THEN
$15
WHEN $16 :: integer > 0 THEN
$16
END
OFFSET
$14
$15
`
type GetWorkspacesParams struct {
@@ -11384,6 +11399,7 @@ type GetWorkspacesParams struct {
LastUsedBefore time.Time `db:"last_used_before" json:"last_used_before"`
LastUsedAfter time.Time `db:"last_used_after" json:"last_used_after"`
UsingActive sql.NullBool `db:"using_active" json:"using_active"`
RequesterID uuid.UUID `db:"requester_id" json:"requester_id"`
Offset int32 `db:"offset_" json:"offset_"`
Limit int32 `db:"limit_" json:"limit_"`
}
@@ -11403,6 +11419,7 @@ type GetWorkspacesRow struct {
DormantAt sql.NullTime `db:"dormant_at" json:"dormant_at"`
DeletingAt sql.NullTime `db:"deleting_at" json:"deleting_at"`
AutomaticUpdates AutomaticUpdates `db:"automatic_updates" json:"automatic_updates"`
Favorite bool `db:"favorite" json:"favorite"`
TemplateName string `db:"template_name" json:"template_name"`
TemplateVersionID uuid.UUID `db:"template_version_id" json:"template_version_id"`
TemplateVersionName sql.NullString `db:"template_version_name" json:"template_version_name"`
@@ -11424,6 +11441,7 @@ func (q *sqlQuerier) GetWorkspaces(ctx context.Context, arg GetWorkspacesParams)
arg.LastUsedBefore,
arg.LastUsedAfter,
arg.UsingActive,
arg.RequesterID,
arg.Offset,
arg.Limit,
)
@@ -11449,6 +11467,7 @@ func (q *sqlQuerier) GetWorkspaces(ctx context.Context, arg GetWorkspacesParams)
&i.DormantAt,
&i.DeletingAt,
&i.AutomaticUpdates,
&i.Favorite,
&i.TemplateName,
&i.TemplateVersionID,
&i.TemplateVersionName,
@@ -11469,7 +11488,7 @@ func (q *sqlQuerier) GetWorkspaces(ctx context.Context, arg GetWorkspacesParams)
const getWorkspacesEligibleForTransition = `-- name: GetWorkspacesEligibleForTransition :many
SELECT
workspaces.id, workspaces.created_at, workspaces.updated_at, workspaces.owner_id, workspaces.organization_id, workspaces.template_id, workspaces.deleted, workspaces.name, workspaces.autostart_schedule, workspaces.ttl, workspaces.last_used_at, workspaces.dormant_at, workspaces.deleting_at, workspaces.automatic_updates
workspaces.id, workspaces.created_at, workspaces.updated_at, workspaces.owner_id, workspaces.organization_id, workspaces.template_id, workspaces.deleted, workspaces.name, workspaces.autostart_schedule, workspaces.ttl, workspaces.last_used_at, workspaces.dormant_at, workspaces.deleting_at, workspaces.automatic_updates, workspaces.favorite
FROM
workspaces
LEFT JOIN
@@ -11557,6 +11576,7 @@ func (q *sqlQuerier) GetWorkspacesEligibleForTransition(ctx context.Context, now
&i.DormantAt,
&i.DeletingAt,
&i.AutomaticUpdates,
&i.Favorite,
); err != nil {
return nil, err
}
@@ -11587,7 +11607,7 @@ INSERT INTO
automatic_updates
)
VALUES
($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11) RETURNING id, created_at, updated_at, owner_id, organization_id, template_id, deleted, name, autostart_schedule, ttl, last_used_at, dormant_at, deleting_at, automatic_updates
($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11) RETURNING id, created_at, updated_at, owner_id, organization_id, template_id, deleted, name, autostart_schedule, ttl, last_used_at, dormant_at, deleting_at, automatic_updates, favorite
`
type InsertWorkspaceParams struct {
@@ -11634,10 +11654,20 @@ func (q *sqlQuerier) InsertWorkspace(ctx context.Context, arg InsertWorkspacePar
&i.DormantAt,
&i.DeletingAt,
&i.AutomaticUpdates,
&i.Favorite,
)
return i, err
}
const unfavoriteWorkspace = `-- name: UnfavoriteWorkspace :exec
UPDATE workspaces SET favorite = false WHERE id = $1
`
func (q *sqlQuerier) UnfavoriteWorkspace(ctx context.Context, id uuid.UUID) error {
_, err := q.db.ExecContext(ctx, unfavoriteWorkspace, id)
return err
}
const updateTemplateWorkspacesLastUsedAt = `-- name: UpdateTemplateWorkspacesLastUsedAt :exec
UPDATE workspaces
SET
@@ -11664,7 +11694,7 @@ SET
WHERE
id = $1
AND deleted = false
RETURNING id, created_at, updated_at, owner_id, organization_id, template_id, deleted, name, autostart_schedule, ttl, last_used_at, dormant_at, deleting_at, automatic_updates
RETURNING id, created_at, updated_at, owner_id, organization_id, template_id, deleted, name, autostart_schedule, ttl, last_used_at, dormant_at, deleting_at, automatic_updates, favorite
`
type UpdateWorkspaceParams struct {
@@ -11690,6 +11720,7 @@ func (q *sqlQuerier) UpdateWorkspace(ctx context.Context, arg UpdateWorkspacePar
&i.DormantAt,
&i.DeletingAt,
&i.AutomaticUpdates,
&i.Favorite,
)
return i, err
}
@@ -11776,7 +11807,7 @@ WHERE
workspaces.id = $1
AND templates.id = workspaces.template_id
RETURNING
workspaces.id, workspaces.created_at, workspaces.updated_at, workspaces.owner_id, workspaces.organization_id, workspaces.template_id, workspaces.deleted, workspaces.name, workspaces.autostart_schedule, workspaces.ttl, workspaces.last_used_at, workspaces.dormant_at, workspaces.deleting_at, workspaces.automatic_updates
workspaces.id, workspaces.created_at, workspaces.updated_at, workspaces.owner_id, workspaces.organization_id, workspaces.template_id, workspaces.deleted, workspaces.name, workspaces.autostart_schedule, workspaces.ttl, workspaces.last_used_at, workspaces.dormant_at, workspaces.deleting_at, workspaces.automatic_updates, workspaces.favorite
`
type UpdateWorkspaceDormantDeletingAtParams struct {
@@ -11802,6 +11833,7 @@ func (q *sqlQuerier) UpdateWorkspaceDormantDeletingAt(ctx context.Context, arg U
&i.DormantAt,
&i.DeletingAt,
&i.AutomaticUpdates,
&i.Favorite,
)
return i, err
}
+8
View File
@@ -267,6 +267,8 @@ WHERE
-- Authorize Filter clause will be injected below in GetAuthorizedWorkspaces
-- @authorize_filter
ORDER BY
-- To ensure that 'favorite' workspaces show up first in the list only for their owner.
CASE WHEN workspaces.owner_id = @requester_id AND workspaces.favorite THEN 0 ELSE 1 END ASC,
(latest_build.completed_at IS NOT NULL AND
latest_build.canceled_at IS NULL AND
latest_build.error IS NULL AND
@@ -552,3 +554,9 @@ SET
automatic_updates = $2
WHERE
id = $1;
-- name: FavoriteWorkspace :exec
UPDATE workspaces SET favorite = true WHERE id = @id;
-- name: UnfavoriteWorkspace :exec
UPDATE workspaces SET favorite = false WHERE id = @id;