Skip to content

Commit 1db5012

Browse files
refactor(scf): remove custom pagination logic
1 parent 787aaf8 commit 1db5012

2 files changed

Lines changed: 51 additions & 92 deletions

File tree

‎internal/cmd/beta/scf/organization/list/list.go‎

Lines changed: 18 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@ import (
2323
const (
2424
limitFlag = "limit"
2525

26-
// maxPageSize is the maximum number of items the API returns per page.
27-
maxPageSize = 100
26+
// maxPageSize is the maximum number of items the API returns per page. 5000 is the maximum the API supports.
27+
maxPageSize = 5000
2828
)
2929

3030
type inputModel struct {
@@ -36,7 +36,7 @@ func NewCmd(params *types.CmdParams) *cobra.Command {
3636
cmd := &cobra.Command{
3737
Use: "list",
3838
Short: "Lists all STACKIT Cloud Foundry organizations",
39-
Long: "Lists all STACKIT Cloud Foundry organizations.",
39+
Long: fmt.Sprintf("Lists all STACKIT Cloud Foundry organizations. The number of fetched organizations is limited to %d.", maxPageSize),
4040
Args: args.NoArgs,
4141
Example: examples.Build(
4242
examples.NewExample(
@@ -61,7 +61,7 @@ func NewCmd(params *types.CmdParams) *cobra.Command {
6161
}
6262

6363
// Call API
64-
resp, err := fetchOrganizations(ctx, model, apiClient)
64+
resp, err := fetchOrganizations(ctx, model, maxPageSize, apiClient.DefaultAPI)
6565
if err != nil {
6666
return fmt.Errorf("fetch STACKIT Cloud Foundry organizations: %w", err)
6767
}
@@ -113,38 +113,23 @@ func buildRequest(ctx context.Context, model *inputModel, apiClient scf.DefaultA
113113
return apiClient.ListOrganizations(ctx, model.ProjectId, model.Region).WithDetails(true).Page(page).PerPage(perPage)
114114
}
115115

116-
// fetchOrganizations pages through the organizations. The API pages by offset (page * perPage),
117-
// so perPage must stay the same for every request, otherwise pages overlap or skip items.
118-
// This is why we cannot use the pagination utils of the SDK here.
119-
func fetchOrganizations(ctx context.Context, model *inputModel, apiClient *scf.APIClient) ([]scf.OrganizationsListItem, error) {
120-
perPage := int64(maxPageSize)
116+
// fetchOrganizations fetches the organizations without using the non AIP-compliant pagination.
117+
func fetchOrganizations(ctx context.Context, model *inputModel, pageSize int, apiClient scf.DefaultAPI) ([]scf.OrganizationsListItem, error) {
118+
if pageSize < 1 {
119+
return nil, fmt.Errorf("list STACKIT Cloud Foundry organizations: maxPageSize must not be smaller than 1")
120+
}
121+
organizations := []scf.OrganizationsListItem{}
122+
perPage := int64(pageSize)
121123
if model.Limit != nil && *model.Limit < perPage {
122124
perPage = *model.Limit
123125
}
124-
125-
organizations := []scf.OrganizationsListItem{}
126-
for page := int64(1); ; page++ {
127-
resp, err := buildRequest(ctx, model, apiClient.DefaultAPI, page, perPage).Execute()
128-
if err != nil {
129-
return nil, fmt.Errorf("list STACKIT Cloud Foundry organizations: %w", err)
130-
}
131-
if resp == nil || len(resp.Resources) == 0 {
132-
break
133-
}
134-
organizations = append(organizations, resp.Resources...)
135-
136-
// Stop and truncate if limit is reached
137-
if model.Limit != nil && int64(len(organizations)) >= *model.Limit {
138-
organizations = organizations[:*model.Limit]
139-
break
140-
}
141-
// Stop if no more pages
142-
if int64(len(resp.Resources)) < perPage {
143-
break
144-
}
145-
if totalPages, ok := resp.Pagination.GetTotalPagesOk(); ok && page >= *totalPages {
146-
break
147-
}
126+
resp, err := buildRequest(ctx, model, apiClient, 1, perPage).Execute()
127+
if err != nil {
128+
return nil, fmt.Errorf("list STACKIT Cloud Foundry organizations: %w", err)
129+
}
130+
organizations = resp.Resources
131+
if model.Limit != nil && int64(len(resp.Resources)) >= *model.Limit {
132+
organizations = resp.Resources[:*model.Limit]
148133
}
149134
return organizations, nil
150135
}

‎internal/cmd/beta/scf/organization/list/list_test.go‎

Lines changed: 33 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ package list
33
import (
44
"context"
55
"encoding/json"
6-
"fmt"
76
"net/http"
87
"net/http/httptest"
98
"strconv"
@@ -153,78 +152,56 @@ func TestBuildRequest(t *testing.T) {
153152
}
154153
}
155154

156-
func fixturePage(count int, totalPages *int64) scf.OrganizationsList {
157-
return scf.OrganizationsList{
158-
Resources: make([]scf.OrganizationsListItem, count),
159-
Pagination: scf.Pagination{TotalPages: totalPages},
160-
}
155+
func fixtureResponse(count int) []scf.OrganizationsListItem {
156+
return make([]scf.OrganizationsListItem, count)
161157
}
162158

163159
func TestFetchOrganizations(t *testing.T) {
164160
tests := []struct {
165-
description string
166-
limit *int64
167-
responses []scf.OrganizationsList
168-
fails bool
169-
expectedCount int
170-
expectedRequests []string
161+
description string
162+
limit *int64
163+
maxPageSize int
164+
response []scf.OrganizationsListItem
165+
wantErr bool
166+
expectedCount int
171167
}{
172168
{
173-
description: "no organizations",
174-
responses: []scf.OrganizationsList{fixturePage(0, nil)},
175-
expectedCount: 0,
176-
expectedRequests: []string{"page=1 perPage=100"},
177-
},
178-
{
179-
description: "multiple pages, stops on short page",
180-
responses: []scf.OrganizationsList{
181-
fixturePage(100, nil),
182-
fixturePage(100, nil),
183-
fixturePage(50, nil),
184-
},
185-
expectedCount: 250,
186-
expectedRequests: []string{"page=1 perPage=100", "page=2 perPage=100", "page=3 perPage=100"},
187-
},
188-
{
189-
description: "stops at totalPages",
190-
responses: []scf.OrganizationsList{fixturePage(100, new(int64(1)))},
191-
expectedCount: 100,
192-
expectedRequests: []string{"page=1 perPage=100"},
169+
description: "no organizations",
170+
maxPageSize: 10,
171+
response: fixtureResponse(0),
172+
expectedCount: 0,
193173
},
194174
{
195-
description: "limit below page size",
196-
limit: new(int64(10)),
197-
responses: []scf.OrganizationsList{fixturePage(10, nil)},
198-
expectedCount: 10,
199-
expectedRequests: []string{"page=1 perPage=10"},
175+
description: "Limit is smaller than default page size",
176+
limit: new(int64(2)),
177+
maxPageSize: 10,
178+
expectedCount: 2,
179+
response: fixtureResponse(3),
200180
},
201181
{
202-
// perPage must not shrink on the last page, the API pages by offset
203-
description: "limit across pages",
204-
limit: new(int64(150)),
205-
responses: []scf.OrganizationsList{
206-
fixturePage(100, nil),
207-
fixturePage(100, nil),
208-
},
209-
expectedCount: 150,
210-
expectedRequests: []string{"page=1 perPage=100", "page=2 perPage=100"},
182+
description: "Limit is unspecified",
183+
maxPageSize: 2,
184+
expectedCount: 2,
185+
response: fixtureResponse(3),
211186
},
212187
{
213-
description: "API error",
214-
fails: true,
188+
description: "page size smaller than 1 should error",
189+
maxPageSize: 0,
190+
wantErr: true,
215191
},
216192
}
217193
for _, tt := range tests {
218194
t.Run(tt.description, func(t *testing.T) {
219-
var requests []string
220195
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
221-
requests = append(requests, fmt.Sprintf("page=%s perPage=%s", r.URL.Query().Get("page"), r.URL.Query().Get("perPage")))
222-
if tt.fails {
223-
w.WriteHeader(http.StatusInternalServerError)
224-
return
196+
resources := tt.response
197+
if tt.maxPageSize < len(tt.response) {
198+
resources = resources[:tt.maxPageSize]
199+
}
200+
serverResponse := scf.OrganizationsList{
201+
Resources: resources,
225202
}
226203
w.Header().Set("Content-Type", "application/json")
227-
if err := json.NewEncoder(w).Encode(tt.responses[len(requests)-1]); err != nil {
204+
if err := json.NewEncoder(w).Encode(serverResponse); err != nil {
228205
t.Fatalf("write: %v", err)
229206
}
230207
})
@@ -241,8 +218,8 @@ func TestFetchOrganizations(t *testing.T) {
241218
model := fixtureInputModel(func(model *inputModel) {
242219
model.Limit = tt.limit
243220
})
244-
got, err := fetchOrganizations(testCtx, model, client)
245-
if tt.fails {
221+
got, err := fetchOrganizations(testCtx, model, tt.maxPageSize, client.DefaultAPI)
222+
if tt.wantErr {
246223
if err == nil {
247224
t.Fatalf("fetchOrganizations() expected error, got none")
248225
}
@@ -257,9 +234,6 @@ func TestFetchOrganizations(t *testing.T) {
257234
if len(got) != tt.expectedCount {
258235
t.Errorf("fetchOrganizations() expected %d organizations, got %d", tt.expectedCount, len(got))
259236
}
260-
if diff := cmp.Diff(tt.expectedRequests, requests); diff != "" {
261-
t.Errorf("fetchOrganizations() requests mismatch (-want +got):\n%s", diff)
262-
}
263237
})
264238
}
265239
}

0 commit comments

Comments
 (0)