feat: Workspace filters case insensitive (#2646)

This commit is contained in:
Steven Masley 2022-06-25 06:22:59 -05:00 committed by GitHub
parent 90815e5119
commit 3312c814bd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 90 additions and 37 deletions

View File

@ -289,7 +289,9 @@ func (q *fakeQuerier) GetUsers(_ context.Context, params database.GetUsersParams
if len(params.Status) > 0 {
usersFilteredByStatus := make([]database.User, 0, len(users))
for i, user := range users {
if slice.Contains(params.Status, user.Status) {
if slice.ContainsCompare(params.Status, user.Status, func(a, b database.UserStatus) bool {
return strings.EqualFold(string(a), string(b))
}) {
usersFilteredByStatus = append(usersFilteredByStatus, users[i])
}
}
@ -299,7 +301,7 @@ func (q *fakeQuerier) GetUsers(_ context.Context, params database.GetUsersParams
if len(params.RbacRole) > 0 && !slice.Contains(params.RbacRole, rbac.RoleMember()) {
usersFilteredByRole := make([]database.User, 0, len(users))
for i, user := range users {
if slice.Overlap(params.RbacRole, user.RBACRoles) {
if slice.OverlapCompare(params.RbacRole, user.RBACRoles, strings.EqualFold) {
usersFilteredByRole = append(usersFilteredByRole, users[i])
}
}
@ -385,25 +387,21 @@ func (q *fakeQuerier) GetWorkspaces(_ context.Context, arg database.GetWorkspace
}
if arg.OwnerUsername != "" {
owner, err := q.GetUserByID(context.Background(), workspace.OwnerID)
if err == nil && arg.OwnerUsername != owner.Username {
if err == nil && !strings.EqualFold(arg.OwnerUsername, owner.Username) {
continue
}
}
if arg.TemplateName != "" {
templates, err := q.GetTemplatesWithFilter(context.Background(), database.GetTemplatesWithFilterParams{
ExactName: arg.TemplateName,
})
// Add to later param
if err == nil {
for _, t := range templates {
arg.TemplateIds = append(arg.TemplateIds, t.ID)
}
template, err := q.GetTemplateByID(context.Background(), workspace.TemplateID)
if err == nil && !strings.EqualFold(arg.TemplateName, template.Name) {
continue
}
}
if !arg.Deleted && workspace.Deleted {
continue
}
if arg.Name != "" && !strings.Contains(workspace.Name, arg.Name) {
if arg.Name != "" && !strings.Contains(strings.ToLower(workspace.Name), strings.ToLower(arg.Name)) {
continue
}
if len(arg.TemplateIds) > 0 {

View File

@ -4094,7 +4094,7 @@ WHERE
-- Filter by owner_name
AND CASE
WHEN $3 :: text != '' THEN
owner_id = (SELECT id FROM users WHERE username = $3)
owner_id = (SELECT id FROM users WHERE lower(username) = lower($3))
ELSE true
END
-- Filter by template_name
@ -4102,7 +4102,7 @@ WHERE
-- Use the organization filter to restrict to 1 org if needed.
AND CASE
WHEN $4 :: text != '' THEN
template_id = ANY(SELECT id FROM templates WHERE name = $4)
template_id = ANY(SELECT id FROM templates WHERE lower(name) = lower($4))
ELSE true
END
-- Filter by template_ids
@ -4114,7 +4114,7 @@ WHERE
-- Filter by name, matching on substring
AND CASE
WHEN $6 :: text != '' THEN
LOWER(name) LIKE '%' || LOWER($6) || '%'
name ILIKE '%' || $6 || '%'
ELSE true
END
`

View File

@ -25,7 +25,7 @@ WHERE
-- Filter by owner_name
AND CASE
WHEN @owner_username :: text != '' THEN
owner_id = (SELECT id FROM users WHERE username = @owner_username)
owner_id = (SELECT id FROM users WHERE lower(username) = lower(@owner_username))
ELSE true
END
-- Filter by template_name
@ -33,7 +33,7 @@ WHERE
-- Use the organization filter to restrict to 1 org if needed.
AND CASE
WHEN @template_name :: text != '' THEN
template_id = ANY(SELECT id FROM templates WHERE name = @template_name)
template_id = ANY(SELECT id FROM templates WHERE lower(name) = lower(@template_name))
ELSE true
END
-- Filter by template_ids
@ -45,7 +45,7 @@ WHERE
-- Filter by name, matching on substring
AND CASE
WHEN @name :: text != '' THEN
LOWER(name) LIKE '%' || LOWER(@name) || '%'
name ILIKE '%' || @name || '%'
ELSE true
END
;

View File

@ -1,20 +1,32 @@
package slice
func Contains[T comparable](haystack []T, needle T) bool {
func ContainsCompare[T any](haystack []T, needle T, equal func(a, b T) bool) bool {
for _, hay := range haystack {
if needle == hay {
if equal(needle, hay) {
return true
}
}
return false
}
func Contains[T comparable](haystack []T, needle T) bool {
return ContainsCompare(haystack, needle, func(a, b T) bool {
return a == b
})
}
// Overlap returns if the 2 sets have any overlap (element(s) in common)
func Overlap[T comparable](a []T, b []T) bool {
return OverlapCompare(a, b, func(a, b T) bool {
return a == b
})
}
func OverlapCompare[T any](a []T, b []T, equal func(a, b T) bool) bool {
// For each element in b, if at least 1 is contained in 'a',
// return true.
for _, element := range b {
if Contains(a, element) {
if ContainsCompare(a, element, equal) {
return true
}
}

View File

@ -959,6 +959,7 @@ func workspaceSearchQuery(query string) (database.GetWorkspacesParams, []httpapi
// No filter
return database.GetWorkspacesParams{}, nil
}
query = strings.ToLower(query)
// 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.

View File

@ -27,8 +27,8 @@ func TestSearchWorkspace(t *testing.T) {
Name: "Owner/Name",
Query: "Foo/Bar",
Expected: database.GetWorkspacesParams{
OwnerUsername: "Foo",
Name: "Bar",
OwnerUsername: "foo",
Name: "bar",
},
},
{
@ -40,7 +40,7 @@ func TestSearchWorkspace(t *testing.T) {
},
{
Name: "Name+Param",
Query: "workspace-name template:docker",
Query: "workspace-name TEMPLATE:docker",
Expected: database.GetWorkspacesParams{
Name: "workspace-name",
TemplateName: "docker",
@ -48,7 +48,7 @@ func TestSearchWorkspace(t *testing.T) {
},
{
Name: "OnlyParams",
Query: "name:workspace-name template:docker owner:alice",
Query: "name:workspace-name template:docker OWNER:Alice",
Expected: database.GetWorkspacesParams{
Name: "workspace-name",
TemplateName: "docker",

View File

@ -358,6 +358,13 @@ func TestWorkspaceFilter(t *testing.T) {
user, err := userClient.User(context.Background(), codersdk.Me)
require.NoError(t, err, "fetch me")
if i%3 == 0 {
user, err = client.UpdateUserProfile(context.Background(), user.ID.String(), codersdk.UpdateUserProfileRequest{
Username: strings.ToUpper(user.Username),
})
require.NoError(t, err, "uppercase username")
}
org, err := userClient.CreateOrganization(context.Background(), codersdk.CreateOrganizationRequest{
Name: user.Username + "-org",
})
@ -378,16 +385,32 @@ func TestWorkspaceFilter(t *testing.T) {
availTemplates := make([]codersdk.Template, 0)
allWorkspaces := make([]madeWorkspace, 0)
upperTemplates := make([]string, 0)
// Create some random workspaces
for _, user := range users {
var count int
for i, user := range users {
version := coderdtest.CreateTemplateVersion(t, client, user.Org.ID, nil)
// Create a template & workspace in the user's org
coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
template := coderdtest.CreateTemplate(t, client, user.Org.ID, version.ID)
var template codersdk.Template
if i%3 == 0 {
template = coderdtest.CreateTemplate(t, client, user.Org.ID, version.ID, func(request *codersdk.CreateTemplateRequest) {
request.Name = strings.ToUpper(request.Name)
})
upperTemplates = append(upperTemplates, template.Name)
} else {
template = coderdtest.CreateTemplate(t, client, user.Org.ID, version.ID)
}
availTemplates = append(availTemplates, template)
workspace := coderdtest.CreateWorkspace(t, user.Client, template.OrganizationID, template.ID)
workspace := coderdtest.CreateWorkspace(t, user.Client, template.OrganizationID, template.ID, func(request *codersdk.CreateWorkspaceRequest) {
if count%3 == 0 {
request.Name = strings.ToUpper(request.Name)
}
})
allWorkspaces = append(allWorkspaces, madeWorkspace{
Workspace: workspace,
Template: template,
@ -428,19 +451,28 @@ func TestWorkspaceFilter(t *testing.T) {
{
Name: "Owner",
Filter: codersdk.WorkspaceFilter{
Owner: users[2].User.Username,
Owner: strings.ToUpper(users[2].User.Username),
},
FilterF: func(f codersdk.WorkspaceFilter, workspace madeWorkspace) bool {
return workspace.Owner.Username == f.Owner
return strings.EqualFold(workspace.Owner.Username, f.Owner)
},
},
{
Name: "TemplateName",
Filter: codersdk.WorkspaceFilter{
Template: allWorkspaces[5].Template.Name,
Template: strings.ToUpper(allWorkspaces[5].Template.Name),
},
FilterF: func(f codersdk.WorkspaceFilter, workspace madeWorkspace) bool {
return workspace.Template.Name == f.Template
return strings.EqualFold(workspace.Template.Name, f.Template)
},
},
{
Name: "UpperTemplateName",
Filter: codersdk.WorkspaceFilter{
Template: upperTemplates[0],
},
FilterF: func(f codersdk.WorkspaceFilter, workspace madeWorkspace) bool {
return strings.EqualFold(workspace.Template.Name, f.Template)
},
},
{
@ -450,16 +482,21 @@ func TestWorkspaceFilter(t *testing.T) {
Name: "a",
},
FilterF: func(f codersdk.WorkspaceFilter, workspace madeWorkspace) bool {
return strings.Contains(workspace.Workspace.Name, f.Name)
return strings.ContainsAny(workspace.Workspace.Name, "Aa")
},
},
{
Name: "Q-Owner/Name",
Filter: codersdk.WorkspaceFilter{
FilterQuery: allWorkspaces[5].Owner.Username + "/" + allWorkspaces[5].Workspace.Name,
FilterQuery: allWorkspaces[5].Owner.Username + "/" + strings.ToUpper(allWorkspaces[5].Workspace.Name),
},
FilterF: func(_ codersdk.WorkspaceFilter, workspace madeWorkspace) bool {
return workspace.Workspace.ID == allWorkspaces[5].Workspace.ID
FilterF: func(f codersdk.WorkspaceFilter, workspace madeWorkspace) bool {
if strings.EqualFold(workspace.Owner.Username, allWorkspaces[5].Owner.Username) &&
strings.Contains(strings.ToLower(workspace.Workspace.Name), strings.ToLower(allWorkspaces[5].Workspace.Name)) {
return true
}
return false
},
},
{
@ -470,7 +507,12 @@ func TestWorkspaceFilter(t *testing.T) {
Name: allWorkspaces[3].Workspace.Name,
},
FilterF: func(f codersdk.WorkspaceFilter, workspace madeWorkspace) bool {
return workspace.Workspace.ID == allWorkspaces[3].Workspace.ID
if strings.EqualFold(workspace.Owner.Username, f.Owner) &&
strings.Contains(strings.ToLower(workspace.Workspace.Name), strings.ToLower(f.Name)) &&
strings.EqualFold(workspace.Template.Name, f.Template) {
return true
}
return false
},
},
}