mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
feat: add paginated API endpoint for groups (#27603)
backend-only changes from #27271; see that PR for summary of changes + implementation details
This commit is contained in:
@@ -174,6 +174,30 @@ func Users(query string) (database.GetUsersParams, []codersdk.ValidationError) {
|
||||
return filter, parser.Errors
|
||||
}
|
||||
|
||||
// Groups parses a group search query using the standard filter syntax shared
|
||||
// with the rest of the dashboard. Bare terms (including multi-word terms)
|
||||
// become a free-text search over group name and display name. A value that
|
||||
// contains a colon must be quoted or supplied via the explicit search key,
|
||||
// e.g. search:"team: frontend", because an unquoted colon is otherwise treated
|
||||
// as a key:value delimiter. Unknown keys are rejected, which keeps room for
|
||||
// real key:value filters in the future.
|
||||
func Groups(query string) (string, []codersdk.ValidationError) {
|
||||
// Always lowercase for all searches.
|
||||
query = strings.ToLower(query)
|
||||
values, errors := searchTerms(query, func(term string, values url.Values) error {
|
||||
values.Add("search", term)
|
||||
return nil
|
||||
})
|
||||
if len(errors) > 0 {
|
||||
return "", errors
|
||||
}
|
||||
|
||||
parser := httpapi.NewQueryParamParser()
|
||||
search := parser.String(values, "", "search")
|
||||
parser.ErrorExcessParams(values)
|
||||
return search, parser.Errors
|
||||
}
|
||||
|
||||
func Members(query string, organizationID uuid.UUID) (database.OrganizationMembersParams, []codersdk.ValidationError) {
|
||||
query = strings.TrimSpace(query)
|
||||
if query == "" {
|
||||
|
||||
@@ -1753,3 +1753,94 @@ func TestSearchChats(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSearchGroups(t *testing.T) {
|
||||
t.Parallel()
|
||||
testCases := []struct {
|
||||
Name string
|
||||
Query string
|
||||
Expected string
|
||||
ExpectedErrorContains string
|
||||
}{
|
||||
{
|
||||
Name: "Empty",
|
||||
Query: "",
|
||||
Expected: "",
|
||||
},
|
||||
{
|
||||
Name: "SingleWord",
|
||||
Query: "alpha",
|
||||
Expected: "alpha",
|
||||
},
|
||||
{
|
||||
// Groups support free-text search, so an unquoted multi-word query
|
||||
// is joined into a single search value instead of being rejected as
|
||||
// a duplicate param.
|
||||
Name: "MultiWord",
|
||||
Query: "front end",
|
||||
Expected: "front end",
|
||||
},
|
||||
{
|
||||
Name: "CaseInsensitive",
|
||||
Query: "AlPhA",
|
||||
Expected: "alpha",
|
||||
},
|
||||
{
|
||||
Name: "MultiWordCaseInsensitive",
|
||||
Query: "Front End",
|
||||
Expected: "front end",
|
||||
},
|
||||
{
|
||||
Name: "TrimsSurroundingSpaces",
|
||||
Query: " alpha ",
|
||||
Expected: "alpha",
|
||||
},
|
||||
{
|
||||
// Structured key:value queries are not supported for groups; the
|
||||
// unrecognized key surfaces as an invalid query param. Rejecting
|
||||
// unknown keys leaves room for real key:value filters later.
|
||||
Name: "StructuredKeyValueRejected",
|
||||
Query: "name:alpha",
|
||||
ExpectedErrorContains: "is not a valid query param",
|
||||
},
|
||||
{
|
||||
// The explicit search key is supported.
|
||||
Name: "SearchKey",
|
||||
Query: "search:alpha",
|
||||
Expected: "alpha",
|
||||
},
|
||||
{
|
||||
// A colon-containing name is searchable when quoted via the search
|
||||
// key, since group display names may legally contain colons.
|
||||
Name: "QuotedColonValue",
|
||||
Query: `search:"team: frontend"`,
|
||||
Expected: "team: frontend",
|
||||
},
|
||||
{
|
||||
// An unquoted colon is treated as a key:value delimiter, so a bare
|
||||
// colon term is rejected. Users must quote it (see QuotedColonValue).
|
||||
Name: "BareColonRejected",
|
||||
Query: "team: frontend",
|
||||
ExpectedErrorContains: "cannot start or end with ':'",
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range testCases {
|
||||
t.Run(c.Name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
search, errs := searchquery.Groups(c.Query)
|
||||
if c.ExpectedErrorContains != "" {
|
||||
require.True(t, len(errs) > 0, "expect some errors")
|
||||
var s strings.Builder
|
||||
for _, err := range errs {
|
||||
_, _ = s.WriteString(fmt.Sprintf("%s: %s\n", err.Field, err.Detail))
|
||||
}
|
||||
require.Contains(t, s.String(), c.ExpectedErrorContains)
|
||||
} else {
|
||||
require.Len(t, errs, 0, "expected no error")
|
||||
require.Equal(t, c.Expected, search, "expected search value")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user