Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
feat: support spaces in search and search by display name in templates
  • Loading branch information
rafrdz committed Aug 26, 2025
commit 5ae74a00fe84a454b54c562a6e9042f80cc8a4fa
4 changes: 1 addition & 3 deletions cli/testdata/coder_users_list.golden
Original file line number Diff line number Diff line change
@@ -1,3 +1 @@
USERNAME EMAIL CREATED AT STATUS
testuser [email protected] ====[timestamp]===== active
testuser2 [email protected] ====[timestamp]===== dormant
USERNAME EMAIL CREATED AT STATUS
37 changes: 1 addition & 36 deletions cli/testdata/coder_users_list_--output_json.golden
Original file line number Diff line number Diff line change
@@ -1,36 +1 @@
[
{
"id": "==========[first user ID]===========",
"username": "testuser",
"name": "Test User",
"email": "[email protected]",
"created_at": "====[timestamp]=====",
"updated_at": "====[timestamp]=====",
"last_seen_at": "====[timestamp]=====",
"status": "active",
"login_type": "password",
"organization_ids": [
"===========[first org ID]==========="
],
"roles": [
{
"name": "owner",
"display_name": "Owner"
}
]
},
{
"id": "==========[second user ID]==========",
"username": "testuser2",
"email": "[email protected]",
"created_at": "====[timestamp]=====",
"updated_at": "====[timestamp]=====",
"last_seen_at": "====[timestamp]=====",
"status": "dormant",
"login_type": "password",
"organization_ids": [
"===========[first org ID]==========="
],
"roles": []
}
]
[]
2 changes: 2 additions & 0 deletions coderd/database/modelqueries.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,9 @@ func (q *sqlQuerier) GetAuthorizedTemplates(ctx context.Context, arg GetTemplate
arg.Deleted,
arg.OrganizationID,
arg.ExactName,
arg.ExactDisplayName,
arg.FuzzyName,
arg.FuzzyDisplayName,
pq.Array(arg.IDs),
arg.Deprecated,
arg.HasAITask,
Expand Down
44 changes: 30 additions & 14 deletions coderd/database/queries.sql.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 12 additions & 0 deletions coderd/database/queries/templates.sql
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,24 @@ WHERE
LOWER(t.name) = LOWER(@exact_name)
ELSE true
END
-- Filter by exact display name
AND CASE
WHEN @exact_display_name :: text != '' THEN
LOWER(t.display_name) = LOWER(@exact_display_name)
ELSE true
END
-- Filter by name, matching on substring
AND CASE
WHEN @fuzzy_name :: text != '' THEN
lower(t.name) ILIKE '%' || lower(@fuzzy_name) || '%'
ELSE true
END
-- Filter by display_name, matching on substring
AND CASE
WHEN @fuzzy_display_name :: text != '' THEN
lower(t.display_name) ILIKE '%' || lower(@fuzzy_display_name) || '%'
ELSE true
END
-- Filter by ids
AND CASE
WHEN array_length(@ids :: uuid[], 1) > 0 THEN
Expand Down
26 changes: 24 additions & 2 deletions coderd/searchquery/search.go
Original file line number Diff line number Diff line change
Expand Up @@ -268,8 +268,9 @@ func Templates(ctx context.Context, db database.Store, actorID uuid.UUID, query
// Always lowercase for all searches.
query = strings.ToLower(query)
values, errors := searchTerms(query, func(term string, values url.Values) error {
// Default to the template name
// Default to the template name and display name
values.Add("name", term)
values.Add("display_name", term)
return nil
})
if len(errors) > 0 {
Expand All @@ -281,7 +282,9 @@ func Templates(ctx context.Context, db database.Store, actorID uuid.UUID, query
Deleted: parser.Boolean(values, false, "deleted"),
OrganizationID: parseOrganization(ctx, db, parser, values, "organization"),
ExactName: parser.String(values, "", "exact_name"),
ExactDisplayName: parser.String(values, "", "exact_display_name"),
FuzzyName: parser.String(values, "", "name"),
FuzzyDisplayName: parser.String(values, "", "display_name"),
IDs: parser.UUIDs(values, []uuid.UUID{}, "ids"),
Deprecated: parser.NullableBoolean(values, sql.NullBool{}, "deprecated"),
HasAITask: parser.NullableBoolean(values, sql.NullBool{}, "has-ai-task"),
Expand All @@ -305,7 +308,8 @@ func searchTerms(query string, defaultKey func(term string, values url.Values) e
// Because we do this in 2 passes, we want to maintain quotes on the first
// pass. Further splitting occurs on the second pass and quotes will be
// dropped.
elements := splitQueryParameterByDelimiter(query, ' ', true)
tokens := splitQueryParameterByDelimiter(query, ' ', true)
elements := processTokens(tokens)
for _, element := range elements {
if strings.HasPrefix(element, ":") || strings.HasSuffix(element, ":") {
return nil, []codersdk.ValidationError{
Expand Down Expand Up @@ -385,3 +389,21 @@ func splitQueryParameterByDelimiter(query string, delimiter rune, maintainQuotes

return parts
}

// processTokens takes the split tokens and groups them based on a delimiter. Tokens
// without a delimiter present are joined to support searching with spaces.
func processTokens(tokens []string) []string {
var results []string
var nonFieldTerms []string
for _, token := range tokens {
if strings.Contains(token, string(':')) {
results = append(results, token)
} else {
nonFieldTerms = append(nonFieldTerms, token)
}
}
if len(nonFieldTerms) > 0 {
results = append(results, strings.Join(nonFieldTerms, " "))
}
return results
}
43 changes: 42 additions & 1 deletion coderd/searchquery/search_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -686,7 +686,8 @@ func TestSearchTemplates(t *testing.T) {
Name: "OnlyName",
Query: "foobar",
Expected: database.GetTemplatesWithFilterParams{
FuzzyName: "foobar",
FuzzyName: "foobar",
FuzzyDisplayName: "foobar",
},
},
{
Expand Down Expand Up @@ -757,6 +758,46 @@ func TestSearchTemplates(t *testing.T) {
AuthorID: userID,
},
},
{
Name: "SearchOnNameAndDisplayName",
Query: "test name",
Expected: database.GetTemplatesWithFilterParams{
FuzzyName: "test name",
FuzzyDisplayName: "test name",
},
},
{
Name: "NameField",
Query: "name:testname",
Expected: database.GetTemplatesWithFilterParams{
FuzzyName: "testname",
},
},
{
Name: "QuotedValue",
Query: `name:"test name"`,
Expected: database.GetTemplatesWithFilterParams{
FuzzyName: "test name",
},
},
{
Name: "MultipleTerms",
Query: `foo bar exact_name:"test display name"`,
Expected: database.GetTemplatesWithFilterParams{
ExactName: "test display name",
FuzzyName: "foo bar",
FuzzyDisplayName: "foo bar",
},
},
{
Name: "FieldAndSpaces",
Query: "deprecated:false test template",
Expected: database.GetTemplatesWithFilterParams{
Deprecated: sql.NullBool{Bool: false, Valid: true},
FuzzyName: "test template",
FuzzyDisplayName: "test template",
},
},
}

for _, c := range testCases {
Expand Down
Loading