Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commite6b04d1

Browse files
authored
feat: add shared filter to workspaces query (#19807)
Adds a `shared:<boolean>` search query to the `/workspaces [get]`endpointhttps://github.com/user-attachments/assets/ccf84bd9-c1fd-4085-825b-2e3176a2d488Closes[coder/internal#972](coder/internal#972)
1 parent8487216 commite6b04d1

File tree

8 files changed

+282
-5
lines changed

8 files changed

+282
-5
lines changed

‎coderd/database/modelqueries.go‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,7 @@ func (q *sqlQuerier) GetAuthorizedWorkspaces(ctx context.Context, arg GetWorkspa
275275
arg.UsingActive,
276276
arg.HasAITask,
277277
arg.HasExternalAgent,
278+
arg.Shared,
278279
arg.RequesterID,
279280
arg.Offset,
280281
arg.Limit,

‎coderd/database/queries.sql.go‎

Lines changed: 14 additions & 5 deletions
Some generated files are not rendered by default. Learn more aboutcustomizing how changed files appear on GitHub.

‎coderd/database/queries/workspaces.sql‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,13 @@ WHERE
378378
latest_build.has_external_agent=sqlc.narg('has_external_agent') ::boolean
379379
ELSE true
380380
END
381+
-- Filter by shared status
382+
AND CASE
383+
WHENsqlc.narg('shared') ::booleanIS NOT NULL THEN
384+
(workspaces.user_acl!='{}'::jsonbORworkspaces.group_acl!='{}'::jsonb)=sqlc.narg('shared') ::boolean
385+
ELSE true
386+
END
387+
381388
-- Authorize Filter clause will be injected below in GetAuthorizedWorkspaces
382389
-- @authorize_filter
383390
), filtered_workspaces_orderAS (

‎coderd/searchquery/search.go‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,7 @@ func Workspaces(ctx context.Context, db database.Store, query string, page coder
225225
filter.HasAITask=parser.NullableBoolean(values, sql.NullBool{},"has-ai-task")
226226
filter.HasExternalAgent=parser.NullableBoolean(values, sql.NullBool{},"has_external_agent")
227227
filter.OrganizationID=parseOrganization(ctx,db,parser,values,"organization")
228+
filter.Shared=parser.NullableBoolean(values, sql.NullBool{},"shared")
228229

229230
typeparamMatchstruct {
230231
namestring

‎coderd/searchquery/search_test.go‎

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,36 @@ func TestSearchWorkspace(t *testing.T) {
282282
},
283283
},
284284
},
285+
{
286+
Name:"SharedTrue",
287+
Query:"shared:true",
288+
Expected: database.GetWorkspacesParams{
289+
Shared: sql.NullBool{
290+
Bool:true,
291+
Valid:true,
292+
},
293+
},
294+
},
295+
{
296+
Name:"SharedFalse",
297+
Query:"shared:false",
298+
Expected: database.GetWorkspacesParams{
299+
Shared: sql.NullBool{
300+
Bool:false,
301+
Valid:true,
302+
},
303+
},
304+
},
305+
{
306+
Name:"SharedMissing",
307+
Query:"",
308+
Expected: database.GetWorkspacesParams{
309+
Shared: sql.NullBool{
310+
Bool:false,
311+
Valid:false,
312+
},
313+
},
314+
},
285315

286316
// Failures
287317
{

‎coderd/workspaces_test.go‎

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1812,6 +1812,82 @@ func TestWorkspaceFilter(t *testing.T) {
18121812
require.ElementsMatch(t,exp,workspaces,"expected workspaces returned")
18131813
})
18141814
}
1815+
1816+
t.Run("SharedWithUser",func(t*testing.T) {
1817+
t.Parallel()
1818+
1819+
dv:=coderdtest.DeploymentValues(t)
1820+
dv.Experiments= []string{string(codersdk.ExperimentWorkspaceSharing)}
1821+
1822+
var (
1823+
client,db=coderdtest.NewWithDatabase(t,&coderdtest.Options{
1824+
DeploymentValues:dv,
1825+
})
1826+
orgOwner=coderdtest.CreateFirstUser(t,client)
1827+
_,workspaceOwner=coderdtest.CreateAnotherUser(t,client,orgOwner.OrganizationID,rbac.ScopedRoleOrgAuditor(orgOwner.OrganizationID))
1828+
sharedWorkspace=dbfake.WorkspaceBuild(t,db, database.WorkspaceTable{
1829+
OwnerID:workspaceOwner.ID,
1830+
OrganizationID:orgOwner.OrganizationID,
1831+
}).Do().Workspace
1832+
_=dbfake.WorkspaceBuild(t,db, database.WorkspaceTable{
1833+
OwnerID:workspaceOwner.ID,
1834+
OrganizationID:orgOwner.OrganizationID,
1835+
}).Do().Workspace
1836+
_,toShareWithUser=coderdtest.CreateAnotherUser(t,client,orgOwner.OrganizationID)
1837+
ctx=testutil.Context(t,testutil.WaitMedium)
1838+
)
1839+
1840+
client.UpdateWorkspaceACL(ctx,sharedWorkspace.ID, codersdk.UpdateWorkspaceACL{
1841+
UserRoles:map[string]codersdk.WorkspaceRole{
1842+
toShareWithUser.ID.String():codersdk.WorkspaceRoleUse,
1843+
},
1844+
})
1845+
1846+
workspaces,err:=client.Workspaces(ctx, codersdk.WorkspaceFilter{
1847+
Shared:ptr.Ref(true),
1848+
})
1849+
require.NoError(t,err,"fetch workspaces")
1850+
require.Equal(t,1,workspaces.Count,"expected only one workspace")
1851+
require.Equal(t,workspaces.Workspaces[0].ID,sharedWorkspace.ID)
1852+
})
1853+
1854+
t.Run("NotSharedWithUser",func(t*testing.T) {
1855+
t.Parallel()
1856+
1857+
dv:=coderdtest.DeploymentValues(t)
1858+
dv.Experiments= []string{string(codersdk.ExperimentWorkspaceSharing)}
1859+
1860+
var (
1861+
client,db=coderdtest.NewWithDatabase(t,&coderdtest.Options{
1862+
DeploymentValues:dv,
1863+
})
1864+
orgOwner=coderdtest.CreateFirstUser(t,client)
1865+
_,workspaceOwner=coderdtest.CreateAnotherUser(t,client,orgOwner.OrganizationID,rbac.ScopedRoleOrgAuditor(orgOwner.OrganizationID))
1866+
sharedWorkspace=dbfake.WorkspaceBuild(t,db, database.WorkspaceTable{
1867+
OwnerID:workspaceOwner.ID,
1868+
OrganizationID:orgOwner.OrganizationID,
1869+
}).Do().Workspace
1870+
notSharedWorkspace=dbfake.WorkspaceBuild(t,db, database.WorkspaceTable{
1871+
OwnerID:workspaceOwner.ID,
1872+
OrganizationID:orgOwner.OrganizationID,
1873+
}).Do().Workspace
1874+
_,toShareWithUser=coderdtest.CreateAnotherUser(t,client,orgOwner.OrganizationID)
1875+
ctx=testutil.Context(t,testutil.WaitMedium)
1876+
)
1877+
1878+
client.UpdateWorkspaceACL(ctx,sharedWorkspace.ID, codersdk.UpdateWorkspaceACL{
1879+
UserRoles:map[string]codersdk.WorkspaceRole{
1880+
toShareWithUser.ID.String():codersdk.WorkspaceRoleUse,
1881+
},
1882+
})
1883+
1884+
workspaces,err:=client.Workspaces(ctx, codersdk.WorkspaceFilter{
1885+
Shared:ptr.Ref(false),
1886+
})
1887+
require.NoError(t,err,"fetch workspaces")
1888+
require.Equal(t,1,workspaces.Count,"expected only one workspace")
1889+
require.Equal(t,workspaces.Workspaces[0].ID,notSharedWorkspace.ID)
1890+
})
18151891
}
18161892

18171893
// TestWorkspaceFilterManual runs some specific setups with basic checks.

‎codersdk/workspaces.go‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -516,6 +516,8 @@ type WorkspaceFilter struct {
516516
Offsetint`json:"offset,omitempty" typescript:"-"`
517517
// Limit is a limit on the number of workspaces returned.
518518
Limitint`json:"limit,omitempty" typescript:"-"`
519+
// Shared is a whether the workspace is shared with any users or groups
520+
Shared*bool`json:"shared,omitempty" typescript:"-"`
519521
// FilterQuery supports a raw filter query string
520522
FilterQuerystring`json:"q,omitempty"`
521523
}
@@ -539,6 +541,9 @@ func (f WorkspaceFilter) asRequestOption() RequestOption {
539541
iff.Status!="" {
540542
params=append(params,fmt.Sprintf("status:%q",f.Status))
541543
}
544+
iff.Shared!=nil {
545+
params=append(params,fmt.Sprintf("shared:%v",*f.Shared))
546+
}
542547
iff.FilterQuery!="" {
543548
// If custom stuff is added, just add it on here.
544549
params=append(params,f.FilterQuery)

‎enterprise/coderd/workspaces_test.go‎

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3575,6 +3575,154 @@ func TestWorkspacesFiltering(t *testing.T) {
35753575
}
35763576
}
35773577
})
3578+
3579+
t.Run("SharedWithGroup",func(t*testing.T) {
3580+
t.Parallel()
3581+
3582+
dv:=coderdtest.DeploymentValues(t)
3583+
dv.Experiments= []string{string(codersdk.ExperimentWorkspaceSharing)}
3584+
3585+
var (
3586+
client,db,orgOwner=coderdenttest.NewWithDatabase(t,&coderdenttest.Options{
3587+
Options:&coderdtest.Options{
3588+
DeploymentValues:dv,
3589+
},
3590+
LicenseOptions:&coderdenttest.LicenseOptions{
3591+
Features: license.Features{
3592+
codersdk.FeatureTemplateRBAC:1,
3593+
},
3594+
},
3595+
})
3596+
_,workspaceOwner=coderdtest.CreateAnotherUser(t,client,orgOwner.OrganizationID,rbac.ScopedRoleOrgAuditor(orgOwner.OrganizationID))
3597+
sharedWorkspace=dbfake.WorkspaceBuild(t,db, database.WorkspaceTable{
3598+
OwnerID:workspaceOwner.ID,
3599+
OrganizationID:orgOwner.OrganizationID,
3600+
}).Do().Workspace
3601+
_=dbfake.WorkspaceBuild(t,db, database.WorkspaceTable{
3602+
OwnerID:workspaceOwner.ID,
3603+
OrganizationID:orgOwner.OrganizationID,
3604+
}).Do().Workspace
3605+
ctx=testutil.Context(t,testutil.WaitMedium)
3606+
)
3607+
3608+
group,err:=client.CreateGroup(ctx,orgOwner.OrganizationID, codersdk.CreateGroupRequest{
3609+
Name:"wibble",
3610+
})
3611+
require.NoError(t,err,"create group")
3612+
3613+
client.UpdateWorkspaceACL(ctx,sharedWorkspace.ID, codersdk.UpdateWorkspaceACL{
3614+
GroupRoles:map[string]codersdk.WorkspaceRole{
3615+
group.ID.String():codersdk.WorkspaceRoleUse,
3616+
},
3617+
})
3618+
3619+
workspaces,err:=client.Workspaces(ctx, codersdk.WorkspaceFilter{
3620+
Shared:ptr.Ref(true),
3621+
})
3622+
require.NoError(t,err,"fetch workspaces")
3623+
require.Equal(t,1,workspaces.Count,"expected only one workspace")
3624+
require.Equal(t,workspaces.Workspaces[0].ID,sharedWorkspace.ID)
3625+
})
3626+
3627+
t.Run("SharedWithUserAndGroup",func(t*testing.T) {
3628+
t.Parallel()
3629+
3630+
dv:=coderdtest.DeploymentValues(t)
3631+
dv.Experiments= []string{string(codersdk.ExperimentWorkspaceSharing)}
3632+
3633+
var (
3634+
client,db,orgOwner=coderdenttest.NewWithDatabase(t,&coderdenttest.Options{
3635+
Options:&coderdtest.Options{
3636+
DeploymentValues:dv,
3637+
},
3638+
LicenseOptions:&coderdenttest.LicenseOptions{
3639+
Features: license.Features{
3640+
codersdk.FeatureTemplateRBAC:1,
3641+
},
3642+
},
3643+
})
3644+
_,workspaceOwner=coderdtest.CreateAnotherUser(t,client,orgOwner.OrganizationID,rbac.ScopedRoleOrgAuditor(orgOwner.OrganizationID))
3645+
sharedWorkspace=dbfake.WorkspaceBuild(t,db, database.WorkspaceTable{
3646+
OwnerID:workspaceOwner.ID,
3647+
OrganizationID:orgOwner.OrganizationID,
3648+
}).Do().Workspace
3649+
_=dbfake.WorkspaceBuild(t,db, database.WorkspaceTable{
3650+
OwnerID:workspaceOwner.ID,
3651+
OrganizationID:orgOwner.OrganizationID,
3652+
}).Do().Workspace
3653+
_,toShareWithUser=coderdtest.CreateAnotherUser(t,client,orgOwner.OrganizationID)
3654+
ctx=testutil.Context(t,testutil.WaitMedium)
3655+
)
3656+
3657+
group,err:=client.CreateGroup(ctx,orgOwner.OrganizationID, codersdk.CreateGroupRequest{
3658+
Name:"wibble",
3659+
})
3660+
require.NoError(t,err,"create group")
3661+
3662+
client.UpdateWorkspaceACL(ctx,sharedWorkspace.ID, codersdk.UpdateWorkspaceACL{
3663+
UserRoles:map[string]codersdk.WorkspaceRole{
3664+
toShareWithUser.ID.String():codersdk.WorkspaceRoleUse,
3665+
},
3666+
GroupRoles:map[string]codersdk.WorkspaceRole{
3667+
group.ID.String():codersdk.WorkspaceRoleUse,
3668+
},
3669+
})
3670+
3671+
workspaces,err:=client.Workspaces(ctx, codersdk.WorkspaceFilter{
3672+
Shared:ptr.Ref(true),
3673+
})
3674+
require.NoError(t,err,"fetch workspaces")
3675+
require.Equal(t,1,workspaces.Count,"expected only one workspace")
3676+
require.Equal(t,workspaces.Workspaces[0].ID,sharedWorkspace.ID)
3677+
})
3678+
3679+
t.Run("NotSharedWithGroup",func(t*testing.T) {
3680+
t.Parallel()
3681+
3682+
dv:=coderdtest.DeploymentValues(t)
3683+
dv.Experiments= []string{string(codersdk.ExperimentWorkspaceSharing)}
3684+
3685+
var (
3686+
client,db,orgOwner=coderdenttest.NewWithDatabase(t,&coderdenttest.Options{
3687+
Options:&coderdtest.Options{
3688+
DeploymentValues:dv,
3689+
},
3690+
LicenseOptions:&coderdenttest.LicenseOptions{
3691+
Features: license.Features{
3692+
codersdk.FeatureTemplateRBAC:1,
3693+
},
3694+
},
3695+
})
3696+
_,workspaceOwner=coderdtest.CreateAnotherUser(t,client,orgOwner.OrganizationID,rbac.ScopedRoleOrgAuditor(orgOwner.OrganizationID))
3697+
sharedWorkspace=dbfake.WorkspaceBuild(t,db, database.WorkspaceTable{
3698+
OwnerID:workspaceOwner.ID,
3699+
OrganizationID:orgOwner.OrganizationID,
3700+
}).Do().Workspace
3701+
notSharedWorkspace=dbfake.WorkspaceBuild(t,db, database.WorkspaceTable{
3702+
OwnerID:workspaceOwner.ID,
3703+
OrganizationID:orgOwner.OrganizationID,
3704+
}).Do().Workspace
3705+
ctx=testutil.Context(t,testutil.WaitMedium)
3706+
)
3707+
3708+
group,err:=client.CreateGroup(ctx,orgOwner.OrganizationID, codersdk.CreateGroupRequest{
3709+
Name:"wibble",
3710+
})
3711+
require.NoError(t,err,"create group")
3712+
3713+
client.UpdateWorkspaceACL(ctx,sharedWorkspace.ID, codersdk.UpdateWorkspaceACL{
3714+
GroupRoles:map[string]codersdk.WorkspaceRole{
3715+
group.ID.String():codersdk.WorkspaceRoleUse,
3716+
},
3717+
})
3718+
3719+
workspaces,err:=client.Workspaces(ctx, codersdk.WorkspaceFilter{
3720+
Shared:ptr.Ref(false),
3721+
})
3722+
require.NoError(t,err,"fetch workspaces")
3723+
require.Equal(t,1,workspaces.Count,"expected only one workspace")
3724+
require.Equal(t,workspaces.Workspaces[0].ID,notSharedWorkspace.ID)
3725+
})
35783726
}
35793727

35803728
// TestWorkspacesWithoutTemplatePerms creates a workspace for a user, then drops

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp