Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

feat: add pagination to getWorkspaces#4521

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
f0ssel merged 9 commits intomainfromf0ssel/pagination
Oct 13, 2022
Merged
Show file tree
Hide file tree
Changes fromall commits
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
13 changes: 13 additions & 0 deletionscoderd/database/databasefake/databasefake.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -695,6 +695,19 @@ func (q *fakeQuerier) GetAuthorizedWorkspaces(ctx context.Context, arg database.
workspaces = append(workspaces, workspace)
}

if arg.Offset > 0 {
if int(arg.Offset) > len(workspaces) {
return []database.Workspace{}, nil
}
workspaces = workspaces[arg.Offset:]
}
if arg.Limit > 0 {
if int(arg.Limit) > len(workspaces) {
return workspaces, nil
}
workspaces = workspaces[:arg.Limit]
}

return workspaces, nil
}

Expand Down
8 changes: 7 additions & 1 deletioncoderd/database/modelqueries.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,6 +4,7 @@ import (
"context"
"encoding/json"
"fmt"
"strings"

"github.com/lib/pq"

Expand DownExpand Up@@ -164,8 +165,11 @@ type workspaceQuerier interface {
// This code is copied from `GetWorkspaces` and adds the authorized filter WHERE
// clause.
func (q *sqlQuerier) GetAuthorizedWorkspaces(ctx context.Context, arg GetWorkspacesParams, authorizedFilter rbac.AuthorizeFilter) ([]Workspace, error) {
// In order to properly use ORDER BY, OFFSET, and LIMIT, we need to inject the
// authorizedFilter between the end of the where clause and those statements.
filter := strings.Replace(getWorkspaces, "-- @authorize_filter", fmt.Sprintf(" AND %s", authorizedFilter.SQLString(rbac.NoACLConfig())), 1)
// The name comment is for metric tracking
query := fmt.Sprintf("-- name: GetAuthorizedWorkspaces :many\n%s AND %s",getWorkspaces, authorizedFilter.SQLString(rbac.NoACLConfig()))
query := fmt.Sprintf("-- name: GetAuthorizedWorkspaces :many\n%s",filter)
rows, err := q.db.QueryContext(ctx, query,
arg.Deleted,
arg.Status,
Expand All@@ -174,6 +178,8 @@ func (q *sqlQuerier) GetAuthorizedWorkspaces(ctx context.Context, arg GetWorkspa
arg.TemplateName,
pq.Array(arg.TemplateIds),
arg.Name,
arg.Offset,
arg.Limit,
)
if err != nil {
return nil, xerrors.Errorf("get authorized workspaces: %w", err)
Expand Down
15 changes: 15 additions & 0 deletionscoderd/database/queries.sql.go
View file
Open in desktop

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

11 changes: 11 additions & 0 deletionscoderd/database/queries/workspaces.sql
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -132,6 +132,17 @@ WHERE
name ILIKE '%' || @name || '%'
ELSE true
END
-- Authorize Filter clause will be injected below in GetAuthorizedWorkspaces
-- @authorize_filter
ORDER BY
last_used_at DESC
LIMIT
CASE
WHEN @limit_ :: integer > 0 THEN
@limit_
END
OFFSET
@offset_
;

-- name: GetWorkspaceByOwnerIDAndName :one
Expand Down
26 changes: 16 additions & 10 deletionscoderd/workspaces.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -98,8 +98,13 @@ func (api *API) workspaces(rw http.ResponseWriter, r *http.Request) {
ctx := r.Context()
apiKey := httpmw.APIKey(r)

page, ok := parsePagination(rw, r)
if !ok {
return
}

queryStr := r.URL.Query().Get("q")
filter, errs := workspaceSearchQuery(queryStr)
filter, errs := workspaceSearchQuery(queryStr, page)
if len(errs) > 0 {
httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{
Message: "Invalid workspace search query.",
Expand DownExpand Up@@ -1072,11 +1077,15 @@ func validWorkspaceSchedule(s *string, min time.Duration) (sql.NullString, error

// workspaceSearchQuery takes a query string and returns the workspace filter.
// It also can return the list of validation errors to return to the api.
func workspaceSearchQuery(query string) (database.GetWorkspacesParams, []codersdk.ValidationError) {
func workspaceSearchQuery(query string, page codersdk.Pagination) (database.GetWorkspacesParams, []codersdk.ValidationError) {
filter := database.GetWorkspacesParams{
Offset: int32(page.Offset),
Limit: int32(page.Limit),
}
searchParams := make(url.Values)
if query == "" {
// No filter
returndatabase.GetWorkspacesParams{}, nil
returnfilter, nil
}
query = strings.ToLower(query)
// Because we do this in 2 passes, we want to maintain quotes on the first
Expand DownExpand Up@@ -1112,13 +1121,10 @@ func workspaceSearchQuery(query string) (database.GetWorkspacesParams, []codersd
// Using the query param parser here just returns consistent errors with
// other parsing.
parser := httpapi.NewQueryParamParser()
filter := database.GetWorkspacesParams{
Deleted: false,
OwnerUsername: parser.String(searchParams, "", "owner"),
TemplateName: parser.String(searchParams, "", "template"),
Name: parser.String(searchParams, "", "name"),
Status: parser.String(searchParams, "", "status"),
}
filter.OwnerUsername = parser.String(searchParams, "", "owner")
filter.TemplateName = parser.String(searchParams, "", "template")
filter.Name = parser.String(searchParams, "", "name")
filter.Status = parser.String(searchParams, "", "status")

return filter, parser.Errors
}
Expand Down
3 changes: 2 additions & 1 deletioncoderd/workspaces_internal_test.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -6,6 +6,7 @@ import (
"testing"

"github.com/coder/coder/coderd/database"
"github.com/coder/coder/codersdk"

"github.com/stretchr/testify/require"
)
Expand DownExpand Up@@ -135,7 +136,7 @@ func TestSearchWorkspace(t *testing.T) {
c := c
t.Run(c.Name, func(t *testing.T) {
t.Parallel()
values, errs := workspaceSearchQuery(c.Query)
values, errs := workspaceSearchQuery(c.Query, codersdk.Pagination{})
if c.ExpectedErrorContains != "" {
require.True(t, len(errs) > 0, "expect some errors")
var s strings.Builder
Expand Down
41 changes: 41 additions & 0 deletionscoderd/workspaces_test.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -781,6 +781,47 @@ func TestWorkspaceFilterManual(t *testing.T) {
})
}

func TestOffsetLimit(t *testing.T) {
t.Parallel()
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
defer cancel()
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true})
user := coderdtest.CreateFirstUser(t, client)
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil)
coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
_ = coderdtest.CreateWorkspace(t, client, user.OrganizationID, template.ID)
_ = coderdtest.CreateWorkspace(t, client, user.OrganizationID, template.ID)
_ = coderdtest.CreateWorkspace(t, client, user.OrganizationID, template.ID)

// empty finds all workspaces
ws, err := client.Workspaces(ctx, codersdk.WorkspaceFilter{})
require.NoError(t, err)
require.Len(t, ws, 3)

// offset 1 finds 2 workspaces
ws, err = client.Workspaces(ctx, codersdk.WorkspaceFilter{
Offset: 1,
})
require.NoError(t, err)
require.Len(t, ws, 2)

// offset 1 limit 1 finds 1 workspace
ws, err = client.Workspaces(ctx, codersdk.WorkspaceFilter{
Offset: 1,
Limit: 1,
})
require.NoError(t, err)
require.Len(t, ws, 1)

// offset 3 finds no workspaces
ws, err = client.Workspaces(ctx, codersdk.WorkspaceFilter{
Offset: 3,
})
require.NoError(t, err)
require.Len(t, ws, 0)
}

func TestPostWorkspaceBuild(t *testing.T) {
t.Parallel()
t.Run("NoTemplateVersion", func(t *testing.T) {
Expand Down
10 changes: 9 additions & 1 deletioncodersdk/workspaces.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -254,6 +254,10 @@ type WorkspaceFilter struct {
Name string `json:"name,omitempty" typescript:"-"`
// Status is a workspace status, which is really the status of the latest build
Status string `json:"status,omitempty" typescript:"-"`
// Offset is the number of workspaces to skip before returning results.
Offset int `json:"offset,omitempty" typescript:"-"`
// Limit is a limit on the number of workspaces returned.
Limit int `json:"limit,omitempty" typescript:"-"`
// FilterQuery supports a raw filter query string
FilterQuery string `json:"q,omitempty"`
}
Expand DownExpand Up@@ -290,7 +294,11 @@ func (f WorkspaceFilter) asRequestOption() RequestOption {

// Workspaces returns all workspaces the authenticated user has access to.
func (c *Client) Workspaces(ctx context.Context, filter WorkspaceFilter) ([]Workspace, error) {
res, err := c.Request(ctx, http.MethodGet, "/api/v2/workspaces", nil, filter.asRequestOption())
page := Pagination{
Offset: filter.Offset,
Limit: filter.Limit,
}
res, err := c.Request(ctx, http.MethodGet, "/api/v2/workspaces", nil, filter.asRequestOption(), page.asRequestOption())
if err != nil {
return nil, err
}
Expand Down

[8]ページ先頭

©2009-2025 Movatter.jp