Skip to content

Commit 01a52d1

Browse files
committed
Use codersdk.Pagination in users endpoint
1 parent 475dcf9 commit 01a52d1

File tree

4 files changed

+39
-75
lines changed

4 files changed

+39
-75
lines changed

coderd/database/databasefake/databasefake.go

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -633,24 +633,27 @@ func (q *fakeQuerier) GetTemplateVersionsByTemplateID(_ context.Context, arg dat
633633
}
634634

635635
// Database orders by created_at
636-
sort.Slice(version, func(i, j int) bool {
637-
if version[i].CreatedAt.Equal(version[j].CreatedAt) {
636+
slices.SortFunc(version, func(a, b database.TemplateVersion) bool {
637+
if a.CreatedAt.Equal(b.CreatedAt) {
638638
// Technically the postgres database also orders by uuid. So match
639639
// that behavior
640-
return version[i].ID.String() < version[j].ID.String()
640+
return a.ID.String() < b.ID.String()
641641
}
642-
return version[i].CreatedAt.Before(version[j].CreatedAt)
642+
return a.CreatedAt.Before(b.CreatedAt)
643643
})
644644

645645
if arg.AfterID != uuid.Nil {
646646
found := false
647647
for i, v := range version {
648648
if v.ID == arg.AfterID {
649+
// We want to return all users after index i.
649650
version = version[i+1:]
650651
found = true
651652
break
652653
}
653654
}
655+
656+
// If no users after the time, then we return an empty list.
654657
if !found {
655658
return nil, sql.ErrNoRows
656659
}

coderd/users.go

Lines changed: 6 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import (
77
"errors"
88
"fmt"
99
"net/http"
10-
"strconv"
1110
"time"
1211

1312
"github.com/go-chi/chi/v5"
@@ -106,52 +105,20 @@ func (api *api) postFirstUser(rw http.ResponseWriter, r *http.Request) {
106105

107106
func (api *api) users(rw http.ResponseWriter, r *http.Request) {
108107
var (
109-
afterArg = r.URL.Query().Get("after_user")
110-
limitArg = r.URL.Query().Get("limit")
111-
offsetArg = r.URL.Query().Get("offset")
112108
searchName = r.URL.Query().Get("search")
113109
statusFilter = r.URL.Query().Get("status")
114110
)
115111

116-
// createdAfter is a user uuid.
117-
createdAfter := uuid.Nil
118-
if afterArg != "" {
119-
after, err := uuid.Parse(afterArg)
120-
if err != nil {
121-
httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{
122-
Message: fmt.Sprintf("after_user must be a valid uuid: %s", err.Error()),
123-
})
124-
return
125-
}
126-
createdAfter = after
127-
}
128-
129-
// Default to no limit and return all users.
130-
pageLimit := -1
131-
if limitArg != "" {
132-
limit, err := strconv.Atoi(limitArg)
133-
if err != nil {
134-
httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{
135-
Message: fmt.Sprintf("limit must be an integer: %s", err.Error()),
136-
})
137-
return
138-
}
139-
pageLimit = limit
140-
}
141-
142-
// The default for empty string is 0.
143-
offset, err := strconv.ParseInt(offsetArg, 10, 64)
144-
if offsetArg != "" && err != nil {
145-
httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{
146-
Message: fmt.Sprintf("offset must be an integer: %s", err.Error()),
147-
})
112+
paginationParams, err := httpapi.ParsePagination(r)
113+
if err != nil {
114+
httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{Message: fmt.Sprintf("parse pagination request: %s", err.Error())})
148115
return
149116
}
150117

151118
users, err := api.Database.GetUsers(r.Context(), database.GetUsersParams{
152-
AfterUser: createdAfter,
153-
OffsetOpt: int32(offset),
154-
LimitOpt: int32(pageLimit),
119+
AfterUser: paginationParams.AfterID,
120+
OffsetOpt: int32(paginationParams.Offset),
121+
LimitOpt: int32(paginationParams.Limit),
155122
Search: searchName,
156123
Status: statusFilter,
157124
})

coderd/users_test.go

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -795,7 +795,9 @@ func assertPagination(ctx context.Context, t *testing.T, client *codersdk.Client
795795

796796
// Check the first page
797797
page, err := client.Users(ctx, opt(codersdk.UsersRequest{
798-
Limit: limit,
798+
Pagination: codersdk.Pagination{
799+
Limit: limit,
800+
},
799801
}))
800802
require.NoError(t, err, "first page")
801803
require.Equalf(t, page, allUsers[:limit], "first page, limit=%d", limit)
@@ -811,15 +813,19 @@ func assertPagination(ctx context.Context, t *testing.T, client *codersdk.Client
811813
// This is using a cursor, and only works if all users created_at
812814
// is unique.
813815
page, err = client.Users(ctx, opt(codersdk.UsersRequest{
814-
Limit: limit,
815-
AfterUser: afterCursor,
816+
Pagination: codersdk.Pagination{
817+
Limit: limit,
818+
AfterID: afterCursor,
819+
},
816820
}))
817821
require.NoError(t, err, "next cursor page")
818822

819823
// Also check page by offset
820824
offsetPage, err := client.Users(ctx, opt(codersdk.UsersRequest{
821-
Limit: limit,
822-
Offset: count,
825+
Pagination: codersdk.Pagination{
826+
Limit: limit,
827+
Offset: count,
828+
},
823829
}))
824830
require.NoError(t, err, "next offset page")
825831

@@ -834,8 +840,10 @@ func assertPagination(ctx context.Context, t *testing.T, client *codersdk.Client
834840

835841
// Also check the before
836842
prevPage, err := client.Users(ctx, opt(codersdk.UsersRequest{
837-
Offset: count - limit,
838-
Limit: limit,
843+
Pagination: codersdk.Pagination{
844+
Offset: count - limit,
845+
Limit: limit,
846+
},
839847
}))
840848
require.NoError(t, err, "prev page")
841849
require.Equal(t, allUsers[count-limit:count], prevPage, "prev users")

codersdk/users.go

Lines changed: 11 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import (
55
"encoding/json"
66
"fmt"
77
"net/http"
8-
"strconv"
98
"time"
109

1110
"github.com/google/uuid"
@@ -22,19 +21,10 @@ const (
2221
)
2322

2423
type UsersRequest struct {
25-
AfterUser uuid.UUID `json:"after_user"`
26-
Search string `json:"search"`
27-
// Limit sets the maximum number of users to be returned
28-
// in a single page. If the limit is <= 0, there is no limit
29-
// and all users are returned.
30-
Limit int `json:"limit"`
31-
// Offset is used to indicate which page to return. An offset of 0
32-
// returns the first 'limit' number of users.
33-
// To get the next page, use offset=<limit>*<page_number>.
34-
// Offset is 0 indexed, so the first record sits at offset 0.
35-
Offset int `json:"offset"`
24+
Search string `json:"search"`
3625
// Filter users by status
3726
Status string `json:"status"`
27+
Pagination
3828
}
3929

4030
// User represents a user in Coder.
@@ -317,19 +307,15 @@ func (c *Client) userByIdentifier(ctx context.Context, ident string) (User, erro
317307
// Users returns all users according to the request parameters. If no parameters are set,
318308
// the default behavior is to return all users in a single page.
319309
func (c *Client) Users(ctx context.Context, req UsersRequest) ([]User, error) {
320-
res, err := c.request(ctx, http.MethodGet, fmt.Sprintf("/api/v2/users"), nil, func(r *http.Request) {
321-
q := r.URL.Query()
322-
if req.AfterUser != uuid.Nil {
323-
q.Set("after_user", req.AfterUser.String())
324-
}
325-
if req.Limit > 0 {
326-
q.Set("limit", strconv.Itoa(req.Limit))
327-
}
328-
q.Set("offset", strconv.Itoa(req.Offset))
329-
q.Set("search", req.Search)
330-
q.Set("status", req.Status)
331-
r.URL.RawQuery = q.Encode()
332-
})
310+
res, err := c.request(ctx, http.MethodGet, fmt.Sprintf("/api/v2/users"), nil,
311+
req.Pagination.asRequestOption(),
312+
func(r *http.Request) {
313+
q := r.URL.Query()
314+
q.Set("search", req.Search)
315+
q.Set("status", req.Status)
316+
r.URL.RawQuery = q.Encode()
317+
},
318+
)
333319
if err != nil {
334320
return []User{}, err
335321
}

0 commit comments

Comments
 (0)