mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
chore: add organization search query to workspaces (#14474)
* chore: add organization search query to workspaces
This commit is contained in:
@@ -39,6 +39,7 @@ func AuditLogs(ctx context.Context, db database.Store, query string) (database.G
|
||||
Email: parser.String(values, "", "email"),
|
||||
DateFrom: parser.Time(values, time.Time{}, "date_from", dateLayout),
|
||||
DateTo: parser.Time(values, time.Time{}, "date_to", dateLayout),
|
||||
OrganizationID: parseOrganization(ctx, db, parser, values, "organization"),
|
||||
ResourceType: string(httpapi.ParseCustom(parser, values, "", "resource_type", httpapi.ParseEnum[database.ResourceType])),
|
||||
Action: string(httpapi.ParseCustom(parser, values, "", "action", httpapi.ParseEnum[database.AuditAction])),
|
||||
BuildReason: string(httpapi.ParseCustom(parser, values, "", "build_reason", httpapi.ParseEnum[database.BuildReason])),
|
||||
@@ -47,27 +48,6 @@ func AuditLogs(ctx context.Context, db database.Store, query string) (database.G
|
||||
filter.DateTo = filter.DateTo.Add(23*time.Hour + 59*time.Minute + 59*time.Second)
|
||||
}
|
||||
|
||||
// Convert the "organization" parameter to an organization uuid. This can require
|
||||
// a database lookup.
|
||||
organizationArg := parser.String(values, "", "organization")
|
||||
if organizationArg != "" {
|
||||
organizationID, err := uuid.Parse(organizationArg)
|
||||
if err == nil {
|
||||
filter.OrganizationID = organizationID
|
||||
} else {
|
||||
// Organization could be a name
|
||||
organization, err := db.GetOrganizationByName(ctx, organizationArg)
|
||||
if err != nil {
|
||||
parser.Errors = append(parser.Errors, codersdk.ValidationError{
|
||||
Field: "organization",
|
||||
Detail: fmt.Sprintf("Organization %q either does not exist, or you are unauthorized to view it", organizationArg),
|
||||
})
|
||||
} else {
|
||||
filter.OrganizationID = organization.ID
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
parser.ErrorExcessParams(values)
|
||||
return filter, parser.Errors
|
||||
}
|
||||
@@ -95,7 +75,7 @@ func Users(query string) (database.GetUsersParams, []codersdk.ValidationError) {
|
||||
return filter, parser.Errors
|
||||
}
|
||||
|
||||
func Workspaces(query string, page codersdk.Pagination, agentInactiveDisconnectTimeout time.Duration) (database.GetWorkspacesParams, []codersdk.ValidationError) {
|
||||
func Workspaces(ctx context.Context, db database.Store, query string, page codersdk.Pagination, agentInactiveDisconnectTimeout time.Duration) (database.GetWorkspacesParams, []codersdk.ValidationError) {
|
||||
filter := database.GetWorkspacesParams{
|
||||
AgentInactiveDisconnectTimeoutSeconds: int64(agentInactiveDisconnectTimeout.Seconds()),
|
||||
|
||||
@@ -145,6 +125,7 @@ func Workspaces(query string, page codersdk.Pagination, agentInactiveDisconnectT
|
||||
// which will return all workspaces.
|
||||
Valid: values.Has("outdated"),
|
||||
}
|
||||
filter.OrganizationID = parseOrganization(ctx, db, parser, values, "organization")
|
||||
|
||||
type paramMatch struct {
|
||||
name string
|
||||
@@ -198,32 +179,12 @@ func Templates(ctx context.Context, db database.Store, query string) (database.G
|
||||
|
||||
parser := httpapi.NewQueryParamParser()
|
||||
filter := database.GetTemplatesWithFilterParams{
|
||||
Deleted: parser.Boolean(values, false, "deleted"),
|
||||
ExactName: parser.String(values, "", "exact_name"),
|
||||
FuzzyName: parser.String(values, "", "name"),
|
||||
IDs: parser.UUIDs(values, []uuid.UUID{}, "ids"),
|
||||
Deprecated: parser.NullableBoolean(values, sql.NullBool{}, "deprecated"),
|
||||
}
|
||||
|
||||
// Convert the "organization" parameter to an organization uuid. This can require
|
||||
// a database lookup.
|
||||
organizationArg := parser.String(values, "", "organization")
|
||||
if organizationArg != "" {
|
||||
organizationID, err := uuid.Parse(organizationArg)
|
||||
if err == nil {
|
||||
filter.OrganizationID = organizationID
|
||||
} else {
|
||||
// Organization could be a name
|
||||
organization, err := db.GetOrganizationByName(ctx, organizationArg)
|
||||
if err != nil {
|
||||
parser.Errors = append(parser.Errors, codersdk.ValidationError{
|
||||
Field: "organization",
|
||||
Detail: fmt.Sprintf("Organization %q either does not exist, or you are unauthorized to view it", organizationArg),
|
||||
})
|
||||
} else {
|
||||
filter.OrganizationID = organization.ID
|
||||
}
|
||||
}
|
||||
Deleted: parser.Boolean(values, false, "deleted"),
|
||||
ExactName: parser.String(values, "", "exact_name"),
|
||||
FuzzyName: parser.String(values, "", "name"),
|
||||
IDs: parser.UUIDs(values, []uuid.UUID{}, "ids"),
|
||||
Deprecated: parser.NullableBoolean(values, sql.NullBool{}, "deprecated"),
|
||||
OrganizationID: parseOrganization(ctx, db, parser, values, "organization"),
|
||||
}
|
||||
|
||||
parser.ErrorExcessParams(values)
|
||||
@@ -271,6 +232,23 @@ func searchTerms(query string, defaultKey func(term string, values url.Values) e
|
||||
return searchValues, nil
|
||||
}
|
||||
|
||||
func parseOrganization(ctx context.Context, db database.Store, parser *httpapi.QueryParamParser, vals url.Values, queryParam string) uuid.UUID {
|
||||
return httpapi.ParseCustom(parser, vals, uuid.Nil, queryParam, func(v string) (uuid.UUID, error) {
|
||||
if v == "" {
|
||||
return uuid.Nil, nil
|
||||
}
|
||||
organizationID, err := uuid.Parse(v)
|
||||
if err == nil {
|
||||
return organizationID, nil
|
||||
}
|
||||
organization, err := db.GetOrganizationByName(ctx, v)
|
||||
if err != nil {
|
||||
return uuid.Nil, xerrors.Errorf("organization %q either does not exist, or you are unauthorized to view it", v)
|
||||
}
|
||||
return organization.ID, nil
|
||||
})
|
||||
}
|
||||
|
||||
// splitQueryParameterByDelimiter takes a query string and splits it into the individual elements
|
||||
// of the query. Each element is separated by a delimiter. All quoted strings are
|
||||
// kept as a single element.
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/coder/coder/v2/coderd/database"
|
||||
"github.com/coder/coder/v2/coderd/database/dbgen"
|
||||
"github.com/coder/coder/v2/coderd/database/dbmem"
|
||||
"github.com/coder/coder/v2/coderd/searchquery"
|
||||
"github.com/coder/coder/v2/codersdk"
|
||||
@@ -25,6 +26,7 @@ func TestSearchWorkspace(t *testing.T) {
|
||||
Query string
|
||||
Expected database.GetWorkspacesParams
|
||||
ExpectedErrorContains string
|
||||
Setup func(t *testing.T, db database.Store)
|
||||
}{
|
||||
{
|
||||
Name: "Empty",
|
||||
@@ -195,6 +197,31 @@ func TestSearchWorkspace(t *testing.T) {
|
||||
ParamValues: []string{"bar"},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "Organization",
|
||||
Query: `organization:4fe722f0-49bc-4a90-a3eb-4ac439bfce20`,
|
||||
Setup: func(t *testing.T, db database.Store) {
|
||||
dbgen.Organization(t, db, database.Organization{
|
||||
ID: uuid.MustParse("4fe722f0-49bc-4a90-a3eb-4ac439bfce20"),
|
||||
})
|
||||
},
|
||||
Expected: database.GetWorkspacesParams{
|
||||
OrganizationID: uuid.MustParse("4fe722f0-49bc-4a90-a3eb-4ac439bfce20"),
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "OrganizationByName",
|
||||
Query: `organization:foobar`,
|
||||
Setup: func(t *testing.T, db database.Store) {
|
||||
dbgen.Organization(t, db, database.Organization{
|
||||
ID: uuid.MustParse("08eb6715-02f8-45c5-b86d-03786fcfbb4e"),
|
||||
Name: "foobar",
|
||||
})
|
||||
},
|
||||
Expected: database.GetWorkspacesParams{
|
||||
OrganizationID: uuid.MustParse("08eb6715-02f8-45c5-b86d-03786fcfbb4e"),
|
||||
},
|
||||
},
|
||||
|
||||
// Failures
|
||||
{
|
||||
@@ -243,7 +270,12 @@ func TestSearchWorkspace(t *testing.T) {
|
||||
c := c
|
||||
t.Run(c.Name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
values, errs := searchquery.Workspaces(c.Query, codersdk.Pagination{}, 0)
|
||||
// TODO: Replace this with the mock database.
|
||||
db := dbmem.New()
|
||||
if c.Setup != nil {
|
||||
c.Setup(t, db)
|
||||
}
|
||||
values, errs := searchquery.Workspaces(context.Background(), db, c.Query, codersdk.Pagination{}, 0)
|
||||
if c.ExpectedErrorContains != "" {
|
||||
assert.True(t, len(errs) > 0, "expect some errors")
|
||||
var s strings.Builder
|
||||
@@ -270,7 +302,7 @@ func TestSearchWorkspace(t *testing.T) {
|
||||
|
||||
query := ``
|
||||
timeout := 1337 * time.Second
|
||||
values, errs := searchquery.Workspaces(query, codersdk.Pagination{}, timeout)
|
||||
values, errs := searchquery.Workspaces(context.Background(), dbmem.New(), query, codersdk.Pagination{}, timeout)
|
||||
require.Empty(t, errs)
|
||||
require.Equal(t, int64(timeout.Seconds()), values.AgentInactiveDisconnectTimeoutSeconds)
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user