feat(coderd): support multi-value status, owner, and template workspace filters - #27909
Draft
jakehwll wants to merge 1 commit into
Draft
Conversation
…pace filters Change the Workspaces list filter parsing, GetWorkspaces query, struct, and authorized query args so status, owner, and template accept multiple values (e.g. status:running status:stopped). Previously parser.String kept only the first value, silently dropping the rest. Status uses ParseCustomList to validate each value; owner and template use parser.Strings. Repeated keys and CSV both work. Single-value URLs continue to behave as before. Backend only: the frontend multiSelect flip depends on FilterSearchField (DEVEX-422), which is not yet on main. Link: https://linear.app/codercom/issue/DEVEX-424
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Resolves DEVEX-424.
Problem
When the frontend sends multiple chips for the same filter key (e.g.
status:running status:stopped), Go's URL parser produces{"status": ["running", "stopped"]}butparser.Stringsilently keeps only the first value. The Workspaces list endpoint therefore drops the extra values, making multi-select filters appear broken even though the client serializes correctly.Changes
The
status,owner, andtemplatefilters on the Workspaces list endpoint now accept multiple values.coderd/database/queries/workspaces.sqlstatus: the single-value@status :: textCASEbecomes an@statuses :: text[]membership check. Each requested status is OR'd using the existing transition/job-status mapping. The raw job-status fallback excludes therunning/succeededjob statuses so single-value semantics are preserved (e.g.runningstill means succeeded+start, not an in-progress build).owner:@owner_username :: textbecomes@owner_usernames :: text[]viaowner_id = ANY(SELECT id FROM users WHERE lower(username) = ANY(@owner_usernames) AND deleted = false).template:@template_name :: textbecomes@template_names :: text[]viatemplate_id = ANY(SELECT id FROM templates WHERE lower(name) = ANY(@template_names) AND deleted = false).coderd/searchquery/search.go:statusparses withParseCustomList(..., ParseEnum[WorkspaceStatus])(keeps per-value validation);owner/templateuseparser.Strings. Repeated keys and CSV both work.coderd/workspaces.go: theowner:meshortcut now resolves anymeentry to the requesting user's username so it composes with other owners viaANY(...).coderd/database/modelqueries.go: wrap the three params withpq.Array(...)inGetAuthorizedWorkspaces.queries.sql.goviamake gen.GetWorkspacesParamsfields becomeStatuses []string,OwnerUsernames []string,TemplateNames []string(WorkspaceStatusis a Go string type, not a Postgres enum, so sqlc emits[]string).Scope note (backend only)
The ticket's Definition of Done includes a frontend
multiSelect: trueflip inWorkspacesPageView.tsx. That change depends onFilterSearchField, introduced by the blocker DEVEX-422, which is not yet onmain(no component, no category definitions). This PR delivers the backend half named in the ticket title /need-backendlabel; the one-line frontend flip lands once DEVEX-422 merges.dbmemwas removed from the codebase (#18803), so the ticket'sdbmem.gostep no longer applies. Filter tests run against real Postgres.Testing
coderd/searchquery/search_test.go: migrated expected values to slices; added multi-value owner/template/status cases (repeated keys + CSV) and an invalid-status case; normalized nil vs empty slices.coderd/workspaces_test.go: added end-to-endMultipleStatuses,MultipleOwners,MultipleTemplatessubtests (frontend → API → correct results), and kept single-value assertions to confirm no breaking change.make gen,make fmt,golangci-lint, and the full pre-commit suite pass.Implementation plan
Problem
When the frontend sends multiple chips for the same filter key (e.g.
status:running status:stopped), Go's URL parser produces{"status": ["running", "stopped"]}butparser.Stringsilently keeps only the first value. The Workspaces list endpoint therefore drops the extra values, making multi-select filters appear broken.Scope (backend only)
The ticket's DoD includes a frontend
multiSelect: truechange inWorkspacesPageView.tsx, which depends onFilterSearchFieldfrom the blocker DEVEX-422 and is not yet onmain. This PR delivers the backend half.dbmemwas removed (#18803), so that ticket step no longer applies.Changes
coderd/database/queries/workspaces.sql(GetWorkspaces)@status :: textsingle-value CASE ->@statuses :: text[]membership; per-status OR using existing transition/job-status mapping; raw job-status fallback excludesrunning/succeededso single-value semantics hold.@owner_username->@owner_usernameswithowner_id = ANY(SELECT id FROM users WHERE lower(username) = ANY(...)).@template_name->@template_nameswithtemplate_id = ANY(SELECT id FROM templates WHERE lower(name) = ANY(...)).make genregeneratesqueries.sql.go; struct fields becomeStatuses/OwnerUsernames/TemplateNames []string.coderd/database/modelqueries.go: wrap the three params withpq.Array(...).coderd/searchquery/search.go:ParseCustomListfor status (validated) +parser.Stringsfor owner/template.coderd/workspaces.go: resolveowner:meentries to the requester's username.Validation
make gen,make fmt lint,go test ./coderd/searchquery/..., targetedgo test ./coderd/ -run TestWorkspaceFilter....