Skip to content

Commit 6dbcfb8

Browse files
committed
refactor pagination, add pagination to build list
1 parent 886182d commit 6dbcfb8

File tree

13 files changed

+943
-193
lines changed

13 files changed

+943
-193
lines changed

packages/api/internal/api/api.gen.go

Lines changed: 21 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/api/internal/api/spec.gen.go

Lines changed: 109 additions & 109 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/api/internal/api/types.gen.go

Lines changed: 17 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/api/internal/handlers/sandboxes_list.go

Lines changed: 25 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,11 @@ import (
2727
)
2828

2929
const (
30-
maxSandboxListLimit int32 = 100
31-
defaultSandboxListLimit int32 = 100
30+
sandboxesDefaultLimit = int32(100)
31+
sandboxesMaxLimit = int32(100)
3232
)
3333

34-
func (a *APIStore) getPausedSandboxes(ctx context.Context, teamID uuid.UUID, runningSandboxesIDs []string, metadataFilter *map[string]string, limit int32, cursorTime time.Time, cursorID string) ([]utils.PaginatedSandbox, error) {
35-
// Apply limit + 1 to check if there are more results
36-
queryLimit := limit + 1
34+
func (a *APIStore) getPausedSandboxes(ctx context.Context, teamID uuid.UUID, runningSandboxesIDs []string, metadataFilter *map[string]string, queryLimit int32, cursorTime time.Time, cursorID string) ([]utils.PaginatedSandbox, error) {
3735
queryMetadata := dbtypes.JSONBStringMap{}
3836
if metadataFilter != nil {
3937
queryMetadata = *metadataFilter
@@ -115,14 +113,23 @@ func (a *APIStore) GetV2Sandboxes(c *gin.Context, params api.GetV2SandboxesParam
115113
states = append(states, *params.State...)
116114
}
117115

118-
limit := defaultSandboxListLimit
119-
if params.Limit != nil {
120-
limit = *params.Limit
121-
}
116+
// Initialize pagination
117+
pagination, err := utils.NewPagination[utils.PaginatedSandbox](
118+
utils.PaginationParams{
119+
Limit: params.Limit,
120+
NextToken: params.NextToken,
121+
},
122+
utils.PaginationConfig{
123+
DefaultLimit: sandboxesDefaultLimit,
124+
MaxLimit: sandboxesMaxLimit,
125+
DefaultID: utils.MaxSandboxID,
126+
},
127+
)
128+
if err != nil {
129+
telemetry.ReportError(ctx, "error parsing pagination cursor", err)
130+
a.sendAPIStoreError(c, http.StatusBadRequest, "Invalid next token")
122131

123-
// Clip limit to max
124-
if limit > maxSandboxListLimit {
125-
limit = maxSandboxListLimit
132+
return
126133
}
127134

128135
metadataFilter, err := utils.ParseMetadata(params.Metadata)
@@ -136,15 +143,6 @@ func (a *APIStore) GetV2Sandboxes(c *gin.Context, params api.GetV2SandboxesParam
136143
// Get sandboxes with pagination
137144
sandboxes := make([]utils.PaginatedSandbox, 0)
138145

139-
// Parse the next token to offset sandboxes for pagination
140-
cursorTime, cursorID, err := utils.ParseNextToken(params.NextToken)
141-
if err != nil {
142-
zap.L().Error("Error parsing cursor", zap.Error(err))
143-
a.sendAPIStoreError(c, http.StatusBadRequest, "Invalid next token")
144-
145-
return
146-
}
147-
148146
allSandboxes := a.orchestrator.GetSandboxes(ctx, team.ID, []sandbox.State{sandbox.StateRunning, sandbox.StatePausing})
149147
runningSandboxes := sharedUtils.Filter(allSandboxes, func(sbx sandbox.Sandbox) bool {
150148
return sbx.State == sandbox.StateRunning
@@ -163,7 +161,7 @@ func (a *APIStore) GetV2Sandboxes(c *gin.Context, params api.GetV2SandboxesParam
163161
c.Header("X-Total-Running", strconv.Itoa(len(runningSandboxList)))
164162

165163
// Filter based on cursor
166-
runningSandboxList = utils.FilterBasedOnCursor(runningSandboxList, cursorTime, cursorID)
164+
runningSandboxList = utils.FilterBasedOnCursor(runningSandboxList, pagination.CursorTime(), pagination.CursorID())
167165

168166
sandboxes = append(sandboxes, runningSandboxList...)
169167
}
@@ -178,7 +176,7 @@ func (a *APIStore) GetV2Sandboxes(c *gin.Context, params api.GetV2SandboxesParam
178176
runningSandboxesIDs = append(runningSandboxesIDs, utils.ShortID(info.SandboxID))
179177
}
180178

181-
pausedSandboxList, err := a.getPausedSandboxes(ctx, team.ID, runningSandboxesIDs, metadataFilter, limit, cursorTime, cursorID)
179+
pausedSandboxList, err := a.getPausedSandboxes(ctx, team.ID, runningSandboxesIDs, metadataFilter, pagination.QueryLimit(), pagination.CursorTime(), pagination.CursorID())
182180
if err != nil {
183181
zap.L().Error("Error getting paused sandboxes", zap.Error(err))
184182
a.sendAPIStoreError(c, http.StatusInternalServerError, "Error getting paused sandboxes")
@@ -188,7 +186,7 @@ func (a *APIStore) GetV2Sandboxes(c *gin.Context, params api.GetV2SandboxesParam
188186

189187
pausingSandboxList := instanceInfoToPaginatedSandboxes(pausingSandboxes)
190188
pausingSandboxList = utils.FilterSandboxesOnMetadata(pausingSandboxList, metadataFilter)
191-
pausingSandboxList = utils.FilterBasedOnCursor(pausingSandboxList, cursorTime, cursorID)
189+
pausingSandboxList = utils.FilterBasedOnCursor(pausingSandboxList, pagination.CursorTime(), pagination.CursorID())
192190

193191
sandboxes = append(sandboxes, pausedSandboxList...)
194192
sandboxes = append(sandboxes, pausingSandboxList...)
@@ -197,21 +195,9 @@ func (a *APIStore) GetV2Sandboxes(c *gin.Context, params api.GetV2SandboxesParam
197195
// We need to sort again after merging running and paused sandboxes
198196
utils.SortPaginatedSandboxesDesc(sandboxes)
199197

200-
var nextToken *string
201-
if len(sandboxes) > int(limit) {
202-
// We have more results than the limit, so we need to set the nextToken
203-
lastSandbox := sandboxes[limit-1]
204-
cursor := lastSandbox.GenerateCursor()
205-
nextToken = &cursor
206-
207-
// Trim to the requested limit
208-
sandboxes = sandboxes[:limit]
209-
}
210-
211-
// Add pagination info to headers
212-
if nextToken != nil {
213-
c.Header("X-Next-Token", *nextToken)
214-
}
198+
sandboxes = pagination.ProcessResultsWithHeader(c, sandboxes, func(s utils.PaginatedSandbox) (time.Time, string) {
199+
return s.PaginationTimestamp, s.SandboxID
200+
})
215201

216202
c.JSON(http.StatusOK, sandboxes)
217203
}

packages/api/internal/handlers/template_get.go

Lines changed: 43 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,27 @@ package handlers
33
import (
44
"fmt"
55
"net/http"
6+
"time"
67

78
"github.com/gin-gonic/gin"
9+
"github.com/google/uuid"
810

911
"github.com/e2b-dev/infra/packages/api/internal/api"
12+
"github.com/e2b-dev/infra/packages/api/internal/utils"
1013
"github.com/e2b-dev/infra/packages/db/dberrors"
14+
"github.com/e2b-dev/infra/packages/db/queries"
1115
"github.com/e2b-dev/infra/packages/shared/pkg/telemetry"
12-
"github.com/e2b-dev/infra/packages/shared/pkg/utils"
16+
sharedUtils "github.com/e2b-dev/infra/packages/shared/pkg/utils"
1317
)
1418

15-
func (a *APIStore) GetTemplatesTemplateID(c *gin.Context, templateID api.TemplateID) {
19+
const (
20+
templateBuildsDefaultLimit = int32(100)
21+
templateBuildsMaxLimit = int32(100)
22+
)
23+
24+
var maxBuildID = uuid.Max.String()
25+
26+
func (a *APIStore) GetTemplatesTemplateID(c *gin.Context, templateID api.TemplateID, params api.GetTemplatesTemplateIDParams) {
1627
ctx := c.Request.Context()
1728

1829
team, apiErr := a.GetTeam(ctx, c, nil)
@@ -46,14 +57,42 @@ func (a *APIStore) GetTemplatesTemplateID(c *gin.Context, templateID api.Templat
4657
return
4758
}
4859

49-
builds, err := a.sqlcDB.GetTemplateBuilds(ctx, templateID)
60+
// Initialize pagination
61+
pagination, err := utils.NewPagination[queries.EnvBuild](
62+
utils.PaginationParams{
63+
Limit: params.Limit,
64+
NextToken: params.NextToken,
65+
},
66+
utils.PaginationConfig{
67+
DefaultLimit: templateBuildsDefaultLimit,
68+
MaxLimit: templateBuildsMaxLimit,
69+
DefaultID: maxBuildID,
70+
},
71+
)
72+
if err != nil {
73+
telemetry.ReportError(ctx, "error parsing pagination cursor", err)
74+
a.sendAPIStoreError(c, http.StatusBadRequest, "Invalid next token")
75+
76+
return
77+
}
78+
79+
builds, err := a.sqlcDB.GetTemplateBuilds(ctx, queries.GetTemplateBuildsParams{
80+
TemplateID: templateID,
81+
CursorTime: pagination.CursorTime(),
82+
CursorID: pagination.CursorID(),
83+
BuildLimit: pagination.QueryLimit(),
84+
})
5085
if err != nil {
5186
a.sendAPIStoreError(c, http.StatusInternalServerError, "Error when getting builds")
5287
telemetry.ReportCriticalError(ctx, "error when getting builds", err)
5388

5489
return
5590
}
5691

92+
builds = pagination.ProcessResultsWithHeader(c, builds, func(b queries.EnvBuild) (time.Time, string) {
93+
return b.CreatedAt, b.ID.String()
94+
})
95+
5796
res := api.TemplateWithBuilds{
5897
TemplateID: template.ID,
5998
Public: template.Public,
@@ -74,7 +113,7 @@ func (a *APIStore) GetTemplatesTemplateID(c *gin.Context, templateID api.Templat
74113
FinishedAt: item.FinishedAt,
75114
CpuCount: api.CPUCount(item.Vcpu),
76115
MemoryMB: api.MemoryMB(item.RamMb),
77-
DiskSizeMB: utils.CastPtr(item.TotalDiskSizeMb, func(v int64) api.DiskSizeMB { return api.DiskSizeMB(v) }),
116+
DiskSizeMB: sharedUtils.CastPtr(item.TotalDiskSizeMb, func(v int64) api.DiskSizeMB { return api.DiskSizeMB(v) }),
78117
EnvdVersion: item.EnvdVersion,
79118
})
80119
}

0 commit comments

Comments
 (0)