@@ -227,6 +227,7 @@ func Workspaces(ctx context.Context, db database.Store, query string, page coder
227
227
filter .OrganizationID = parseOrganization (ctx ,db ,parser ,values ,"organization" )
228
228
filter .Shared = parser .NullableBoolean (values , sql.NullBool {},"shared" )
229
229
filter .SharedWithUserID = parseUser (ctx ,db ,parser ,values ,"shared_with_user" )
230
+ filter .SharedWithGroupID = parseGroup (ctx ,db ,parser ,values ,"shared_with_group" )
230
231
231
232
type paramMatch struct {
232
233
name string
@@ -383,6 +384,68 @@ func parseUser(ctx context.Context, db database.Store, parser *httpapi.QueryPara
383
384
})
384
385
}
385
386
387
+ // Parse a group filter value into a group UUID.
388
+ // Supported formats:
389
+ // - <group-uuid>
390
+ // - <organization-name>/<group-name>
391
+ // - <group-name> (resolved in the default organization)
392
+ //
393
+ // Returns the group's UUID or an error.
394
+ func parseGroup (ctx context.Context ,db database.Store ,parser * httpapi.QueryParamParser ,vals url.Values ,queryParam string ) uuid.UUID {
395
+ return httpapi .ParseCustom (parser ,vals ,uuid .Nil ,queryParam ,func (v string ) (uuid.UUID ,error ) {
396
+ if v == "" {
397
+ return uuid .Nil ,nil
398
+ }
399
+ groupID ,err := uuid .Parse (v )
400
+ if err == nil {
401
+ return groupID ,nil
402
+ }
403
+
404
+ var groupName string
405
+ var org database.Organization
406
+ parts := strings .Split (v ,"/" )
407
+ switch len (parts ) {
408
+ case 1 :
409
+ dbOrg ,err := db .GetDefaultOrganization (ctx )
410
+ if err != nil {
411
+ return uuid .Nil ,xerrors .New ("fetching default organization" )
412
+ }
413
+ org = dbOrg
414
+ groupName = parts [0 ]
415
+ case 2 :
416
+ orgName := parts [0 ]
417
+ if err := codersdk .NameValid (orgName );err != nil {
418
+ return uuid .Nil ,xerrors .Errorf ("invalid organization name %w" ,err )
419
+ }
420
+ dbOrg ,err := db .GetOrganizationByName (ctx , database.GetOrganizationByNameParams {
421
+ Name :orgName ,
422
+ })
423
+ if err != nil {
424
+ return uuid .Nil ,xerrors .Errorf ("organization %q either does not exist, or you are unauthorized to view it" ,orgName )
425
+ }
426
+ org = dbOrg
427
+
428
+ groupName = parts [1 ]
429
+
430
+ default :
431
+ return uuid .Nil ,xerrors .New ("invalid organization or group name, the filter must be in the pattern of <organization name>/<group name>" )
432
+ }
433
+
434
+ if err := codersdk .GroupNameValid (groupName );err != nil {
435
+ return uuid .Nil ,xerrors .Errorf ("invalid group name %w" ,err )
436
+ }
437
+
438
+ group ,err := db .GetGroupByOrgAndName (ctx , database.GetGroupByOrgAndNameParams {
439
+ OrganizationID :org .ID ,
440
+ Name :groupName ,
441
+ })
442
+ if err != nil {
443
+ return uuid .Nil ,xerrors .Errorf ("group %q either does not exist, does not belong to the organization %q, or you are unauthorized to view it" ,groupName ,org .Name )
444
+ }
445
+ return group .ID ,nil
446
+ })
447
+ }
448
+
386
449
// splitQueryParameterByDelimiter takes a query string and splits it into the individual elements
387
450
// of the query. Each element is separated by a delimiter. All quoted strings are
388
451
// kept as a single element.