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

Commit459ee4e

Browse files
authored
feat: add pagination to getWorkspaces (#4521)
1 parent574e5d3 commit459ee4e

File tree

8 files changed

+114
-13
lines changed

8 files changed

+114
-13
lines changed

‎coderd/database/databasefake/databasefake.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -695,6 +695,19 @@ func (q *fakeQuerier) GetAuthorizedWorkspaces(ctx context.Context, arg database.
695695
workspaces=append(workspaces,workspace)
696696
}
697697

698+
ifarg.Offset>0 {
699+
ifint(arg.Offset)>len(workspaces) {
700+
return []database.Workspace{},nil
701+
}
702+
workspaces=workspaces[arg.Offset:]
703+
}
704+
ifarg.Limit>0 {
705+
ifint(arg.Limit)>len(workspaces) {
706+
returnworkspaces,nil
707+
}
708+
workspaces=workspaces[:arg.Limit]
709+
}
710+
698711
returnworkspaces,nil
699712
}
700713

‎coderd/database/modelqueries.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"context"
55
"encoding/json"
66
"fmt"
7+
"strings"
78

89
"github.com/lib/pq"
910

@@ -164,8 +165,11 @@ type workspaceQuerier interface {
164165
// This code is copied from `GetWorkspaces` and adds the authorized filter WHERE
165166
// clause.
166167
func (q*sqlQuerier)GetAuthorizedWorkspaces(ctx context.Context,argGetWorkspacesParams,authorizedFilter rbac.AuthorizeFilter) ([]Workspace,error) {
168+
// In order to properly use ORDER BY, OFFSET, and LIMIT, we need to inject the
169+
// authorizedFilter between the end of the where clause and those statements.
170+
filter:=strings.Replace(getWorkspaces,"-- @authorize_filter",fmt.Sprintf(" AND %s",authorizedFilter.SQLString(rbac.NoACLConfig())),1)
167171
// The name comment is for metric tracking
168-
query:=fmt.Sprintf("-- name: GetAuthorizedWorkspaces :many\n%s AND %s",getWorkspaces,authorizedFilter.SQLString(rbac.NoACLConfig()))
172+
query:=fmt.Sprintf("-- name: GetAuthorizedWorkspaces :many\n%s",filter)
169173
rows,err:=q.db.QueryContext(ctx,query,
170174
arg.Deleted,
171175
arg.Status,
@@ -174,6 +178,8 @@ func (q *sqlQuerier) GetAuthorizedWorkspaces(ctx context.Context, arg GetWorkspa
174178
arg.TemplateName,
175179
pq.Array(arg.TemplateIds),
176180
arg.Name,
181+
arg.Offset,
182+
arg.Limit,
177183
)
178184
iferr!=nil {
179185
returnnil,xerrors.Errorf("get authorized workspaces: %w",err)

‎coderd/database/queries.sql.go

Lines changed: 15 additions & 0 deletions
Some generated files are not rendered by default. Learn more aboutcustomizing how changed files appear on GitHub.

‎coderd/database/queries/workspaces.sql

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,17 @@ WHERE
132132
name ILIKE'%'|| @name||'%'
133133
ELSE true
134134
END
135+
-- Authorize Filter clause will be injected below in GetAuthorizedWorkspaces
136+
-- @authorize_filter
137+
ORDER BY
138+
last_used_atDESC
139+
LIMIT
140+
CASE
141+
WHEN @limit_ ::integer>0 THEN
142+
@limit_
143+
END
144+
OFFSET
145+
@offset_
135146
;
136147

137148
-- name: GetWorkspaceByOwnerIDAndName :one

‎coderd/workspaces.go

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -98,8 +98,13 @@ func (api *API) workspaces(rw http.ResponseWriter, r *http.Request) {
9898
ctx:=r.Context()
9999
apiKey:=httpmw.APIKey(r)
100100

101+
page,ok:=parsePagination(rw,r)
102+
if!ok {
103+
return
104+
}
105+
101106
queryStr:=r.URL.Query().Get("q")
102-
filter,errs:=workspaceSearchQuery(queryStr)
107+
filter,errs:=workspaceSearchQuery(queryStr,page)
103108
iflen(errs)>0 {
104109
httpapi.Write(ctx,rw,http.StatusBadRequest, codersdk.Response{
105110
Message:"Invalid workspace search query.",
@@ -1072,11 +1077,15 @@ func validWorkspaceSchedule(s *string, min time.Duration) (sql.NullString, error
10721077

10731078
// workspaceSearchQuery takes a query string and returns the workspace filter.
10741079
// It also can return the list of validation errors to return to the api.
1075-
funcworkspaceSearchQuery(querystring) (database.GetWorkspacesParams, []codersdk.ValidationError) {
1080+
funcworkspaceSearchQuery(querystring,page codersdk.Pagination) (database.GetWorkspacesParams, []codersdk.ValidationError) {
1081+
filter:= database.GetWorkspacesParams{
1082+
Offset:int32(page.Offset),
1083+
Limit:int32(page.Limit),
1084+
}
10761085
searchParams:=make(url.Values)
10771086
ifquery=="" {
10781087
// No filter
1079-
returndatabase.GetWorkspacesParams{},nil
1088+
returnfilter,nil
10801089
}
10811090
query=strings.ToLower(query)
10821091
// Because we do this in 2 passes, we want to maintain quotes on the first
@@ -1112,13 +1121,10 @@ func workspaceSearchQuery(query string) (database.GetWorkspacesParams, []codersd
11121121
// Using the query param parser here just returns consistent errors with
11131122
// other parsing.
11141123
parser:=httpapi.NewQueryParamParser()
1115-
filter:= database.GetWorkspacesParams{
1116-
Deleted:false,
1117-
OwnerUsername:parser.String(searchParams,"","owner"),
1118-
TemplateName:parser.String(searchParams,"","template"),
1119-
Name:parser.String(searchParams,"","name"),
1120-
Status:parser.String(searchParams,"","status"),
1121-
}
1124+
filter.OwnerUsername=parser.String(searchParams,"","owner")
1125+
filter.TemplateName=parser.String(searchParams,"","template")
1126+
filter.Name=parser.String(searchParams,"","name")
1127+
filter.Status=parser.String(searchParams,"","status")
11221128

11231129
returnfilter,parser.Errors
11241130
}

‎coderd/workspaces_internal_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"testing"
77

88
"github.com/coder/coder/coderd/database"
9+
"github.com/coder/coder/codersdk"
910

1011
"github.com/stretchr/testify/require"
1112
)
@@ -135,7 +136,7 @@ func TestSearchWorkspace(t *testing.T) {
135136
c:=c
136137
t.Run(c.Name,func(t*testing.T) {
137138
t.Parallel()
138-
values,errs:=workspaceSearchQuery(c.Query)
139+
values,errs:=workspaceSearchQuery(c.Query, codersdk.Pagination{})
139140
ifc.ExpectedErrorContains!="" {
140141
require.True(t,len(errs)>0,"expect some errors")
141142
vars strings.Builder

‎coderd/workspaces_test.go

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -781,6 +781,47 @@ func TestWorkspaceFilterManual(t *testing.T) {
781781
})
782782
}
783783

784+
funcTestOffsetLimit(t*testing.T) {
785+
t.Parallel()
786+
ctx,cancel:=context.WithTimeout(context.Background(),testutil.WaitLong)
787+
defercancel()
788+
client:=coderdtest.New(t,&coderdtest.Options{IncludeProvisionerDaemon:true})
789+
user:=coderdtest.CreateFirstUser(t,client)
790+
version:=coderdtest.CreateTemplateVersion(t,client,user.OrganizationID,nil)
791+
coderdtest.AwaitTemplateVersionJob(t,client,version.ID)
792+
template:=coderdtest.CreateTemplate(t,client,user.OrganizationID,version.ID)
793+
_=coderdtest.CreateWorkspace(t,client,user.OrganizationID,template.ID)
794+
_=coderdtest.CreateWorkspace(t,client,user.OrganizationID,template.ID)
795+
_=coderdtest.CreateWorkspace(t,client,user.OrganizationID,template.ID)
796+
797+
// empty finds all workspaces
798+
ws,err:=client.Workspaces(ctx, codersdk.WorkspaceFilter{})
799+
require.NoError(t,err)
800+
require.Len(t,ws,3)
801+
802+
// offset 1 finds 2 workspaces
803+
ws,err=client.Workspaces(ctx, codersdk.WorkspaceFilter{
804+
Offset:1,
805+
})
806+
require.NoError(t,err)
807+
require.Len(t,ws,2)
808+
809+
// offset 1 limit 1 finds 1 workspace
810+
ws,err=client.Workspaces(ctx, codersdk.WorkspaceFilter{
811+
Offset:1,
812+
Limit:1,
813+
})
814+
require.NoError(t,err)
815+
require.Len(t,ws,1)
816+
817+
// offset 3 finds no workspaces
818+
ws,err=client.Workspaces(ctx, codersdk.WorkspaceFilter{
819+
Offset:3,
820+
})
821+
require.NoError(t,err)
822+
require.Len(t,ws,0)
823+
}
824+
784825
funcTestPostWorkspaceBuild(t*testing.T) {
785826
t.Parallel()
786827
t.Run("NoTemplateVersion",func(t*testing.T) {

‎codersdk/workspaces.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,10 @@ type WorkspaceFilter struct {
254254
Namestring`json:"name,omitempty" typescript:"-"`
255255
// Status is a workspace status, which is really the status of the latest build
256256
Statusstring`json:"status,omitempty" typescript:"-"`
257+
// Offset is the number of workspaces to skip before returning results.
258+
Offsetint`json:"offset,omitempty" typescript:"-"`
259+
// Limit is a limit on the number of workspaces returned.
260+
Limitint`json:"limit,omitempty" typescript:"-"`
257261
// FilterQuery supports a raw filter query string
258262
FilterQuerystring`json:"q,omitempty"`
259263
}
@@ -290,7 +294,11 @@ func (f WorkspaceFilter) asRequestOption() RequestOption {
290294

291295
// Workspaces returns all workspaces the authenticated user has access to.
292296
func (c*Client)Workspaces(ctx context.Context,filterWorkspaceFilter) ([]Workspace,error) {
293-
res,err:=c.Request(ctx,http.MethodGet,"/api/v2/workspaces",nil,filter.asRequestOption())
297+
page:=Pagination{
298+
Offset:filter.Offset,
299+
Limit:filter.Limit,
300+
}
301+
res,err:=c.Request(ctx,http.MethodGet,"/api/v2/workspaces",nil,filter.asRequestOption(),page.asRequestOption())
294302
iferr!=nil {
295303
returnnil,err
296304
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp