feat: add healthy filter for workspace queries (#21743)

Adds support for filtering workspaces by health status using
healthy:true or healthy:false in the search query.

This is done by changing `has-agent` to accept a list of statuses and
aliasing `health:true` to `has-agent:connected` and `healthy:false` to
`has-agent:timeout,disconnected`.

Fixes #21623
This commit is contained in:
Jon Ayers
2026-02-04 20:48:27 -06:00
committed by GitHub
parent 984e363180
commit 22ece10a4a
11 changed files with 209 additions and 19 deletions
+10 -1
View File
@@ -254,7 +254,7 @@ func Workspaces(ctx context.Context, db database.Store, query string, page coder
filter.TemplateName = parser.String(values, "", "template")
filter.Name = parser.String(values, "", "name")
filter.Status = string(httpapi.ParseCustom(parser, values, "", "status", httpapi.ParseEnum[database.WorkspaceStatus]))
filter.HasAgent = parser.String(values, "", "has-agent")
filter.HasAgentStatuses = parser.Strings(values, []string{}, "has-agent")
filter.Dormant = parser.Boolean(values, false, "dormant")
filter.LastUsedAfter = parser.Time3339Nano(values, time.Time{}, "last_used_after")
filter.LastUsedBefore = parser.Time3339Nano(values, time.Time{}, "last_used_before")
@@ -273,6 +273,15 @@ func Workspaces(ctx context.Context, db database.Store, query string, page coder
// TODO: support "me" by passing in the actorID
filter.SharedWithUserID = parseUser(ctx, db, parser, values, "shared_with_user", uuid.Nil)
filter.SharedWithGroupID = parseGroup(ctx, db, parser, values, "shared_with_group")
// Translate healthy filter to has-agent statuses
// healthy:true = connected, healthy:false = disconnected or timeout
if healthy := parser.NullableBoolean(values, sql.NullBool{}, "healthy"); healthy.Valid {
if healthy.Bool {
filter.HasAgentStatuses = append(filter.HasAgentStatuses, "connected")
} else {
filter.HasAgentStatuses = append(filter.HasAgentStatuses, "disconnected", "timeout")
}
}
type paramMatch struct {
name string
+32
View File
@@ -312,6 +312,34 @@ func TestSearchWorkspace(t *testing.T) {
},
},
},
{
Name: "HealthyTrue",
Query: "healthy:true",
Expected: database.GetWorkspacesParams{
HasAgentStatuses: []string{"connected"},
},
},
{
Name: "HealthyFalse",
Query: "healthy:false",
Expected: database.GetWorkspacesParams{
HasAgentStatuses: []string{"disconnected", "timeout"},
},
},
{
Name: "HealthyMissing",
Query: "",
Expected: database.GetWorkspacesParams{
HasAgentStatuses: []string{},
},
},
{
Name: "HealthyAndHasAgent",
Query: "has-agent:connecting healthy:true",
Expected: database.GetWorkspacesParams{
HasAgentStatuses: []string{"connecting", "connected"},
},
},
{
Name: "SharedWithUser",
Query: `shared_with_user:3dd8b1b8-dff5-4b22-8ae9-c243ca136ecf`,
@@ -474,6 +502,10 @@ func TestSearchWorkspace(t *testing.T) {
// nil slice vs 0 len slice is equivalent for our purposes.
c.Expected.HasParam = values.HasParam
}
if len(c.Expected.HasAgentStatuses) == len(values.HasAgentStatuses) {
// nil slice vs 0 len slice is equivalent for our purposes.
c.Expected.HasAgentStatuses = values.HasAgentStatuses
}
assert.Len(t, errs, 0, "expected no error")
assert.Equal(t, c.Expected, values, "expected values")
}