Skip to content

Commit 13b0b5a

Browse files
authored
Support pagination for missing one implementation feature IDs query (#1269)
* Implement pagination token and page size * Address nits * litn
1 parent a158acc commit 13b0b5a

File tree

2 files changed

+146
-1
lines changed

2 files changed

+146
-1
lines changed

lib/gcpspanner/missing_one_implementation_feature_list.go

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,24 @@ type MissingOneImplFeatureListPage struct {
4040
FeatureList []MissingOneImplFeature
4141
}
4242

43+
// missingOneImplFeatureListCursor: Represents a point for resuming queries based on the
44+
// numerical offset from the start of the result set. Useful for pagination.
45+
type missingOneImplFeatureListCursor struct {
46+
Offset int `json:"offset"`
47+
}
48+
49+
// decodeMissingOneImplFeatureListCursor provides a wrapper around the generic decodeCursor.
50+
func decodeMissingOneImplFeatureListCursor(cursor string) (*missingOneImplFeatureListCursor, error) {
51+
return decodeCursor[missingOneImplFeatureListCursor](cursor)
52+
}
53+
54+
// encodeMissingOneImplFeatureListCursor provides a wrapper around the generic encodeCursor.
55+
func encodeMissingOneImplFeatureListCursor(offset int) string {
56+
return encodeCursor(missingOneImplFeatureListCursor{
57+
Offset: offset,
58+
})
59+
}
60+
4361
// MissingOneImplFeature contains information regarding the list of features implemented in all other browsers but not
4462
// in the target browser.
4563
type MissingOneImplFeature struct {
@@ -69,16 +87,23 @@ AND
6987
{{ end }}
7088
1=1
7189
ORDER BY KEY ASC
90+
LIMIT @limit
91+
{{ if .Offset }}
92+
OFFSET {{ .Offset }}
93+
{{ end }}
7294
`
7395

7496
type missingOneImplFeatureListTemplateData struct {
7597
OtherBrowsersParamNames []string
98+
Offset int
7699
}
77100

78101
func buildMissingOneImplFeatureListTemplate(
79102
targetBrowser string,
80103
otherBrowsers []string,
81104
targetDate time.Time,
105+
cursor *missingOneImplFeatureListCursor,
106+
pageSize int,
82107
) spanner.Statement {
83108
params := map[string]interface{}{}
84109
allBrowsers := make([]string, len(otherBrowsers)+1)
@@ -93,9 +118,11 @@ func buildMissingOneImplFeatureListTemplate(
93118
}
94119

95120
params["targetDate"] = targetDate
121+
params["limit"] = pageSize
96122

97123
tmplData := missingOneImplFeatureListTemplateData{
98124
OtherBrowsersParamNames: otherBrowsersParamNames,
125+
Offset: cursor.Offset,
99126
}
100127

101128
sql := missingOneImplFeatureListTemplate.Execute(tmplData)
@@ -110,14 +137,27 @@ func (c *Client) MissingOneImplFeatureList(
110137
targetBrowser string,
111138
otherBrowsers []string,
112139
targetDate time.Time,
140+
pageSize int,
141+
pageToken *string,
113142
) (*MissingOneImplFeatureListPage, error) {
143+
var cursor *missingOneImplFeatureListCursor
144+
var err error
145+
if pageToken != nil {
146+
cursor, err = decodeMissingOneImplFeatureListCursor(*pageToken)
147+
if err != nil {
148+
return nil, errors.Join(ErrInternalQueryFailure, err)
149+
}
150+
}
151+
114152
txn := c.ReadOnlyTransaction()
115153
defer txn.Close()
116154

117155
stmt := buildMissingOneImplFeatureListTemplate(
118156
targetBrowser,
119157
otherBrowsers,
120158
targetDate,
159+
cursor,
160+
pageSize,
121161
)
122162

123163
it := txn.Query(ctx, stmt)
@@ -144,5 +184,14 @@ func (c *Client) MissingOneImplFeatureList(
144184
NextPageToken: nil,
145185
}
146186

187+
if len(results) == pageSize {
188+
previousOffset := 0
189+
if cursor != nil {
190+
previousOffset = cursor.Offset
191+
}
192+
token := encodeMissingOneImplFeatureListCursor(previousOffset + pageSize)
193+
page.NextPageToken = &token
194+
}
195+
147196
return &page, nil
148197
}

lib/gcpspanner/missing_one_implementation_feature_list_test.go

Lines changed: 97 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,13 +127,17 @@ func loadDataForListMissingOneImplFeatureList(ctx context.Context, t *testing.T,
127127
}
128128
}
129129

130+
// nolint:unparam // WONTFIX
130131
func assertMissingOneImplFeatureList(ctx context.Context, t *testing.T, targetDate time.Time,
131-
targetBrowser string, otherBrowsers []string, expectedPage *MissingOneImplFeatureListPage) {
132+
targetBrowser string, otherBrowsers []string, expectedPage *MissingOneImplFeatureListPage, token *string,
133+
pageSize int) {
132134
result, err := spannerClient.MissingOneImplFeatureList(
133135
ctx,
134136
targetBrowser,
135137
otherBrowsers,
136138
targetDate,
139+
pageSize,
140+
token,
137141
)
138142
if err != nil {
139143
t.Errorf("Unexpected error: %v", err)
@@ -157,6 +161,8 @@ func testMissingOneImplFeatureListSuite(
157161
"barBrowser",
158162
}
159163
targetDate := time.Date(2024, 4, 15, 0, 0, 0, 0, time.UTC)
164+
pageSize := 25
165+
token := encodeMissingOneImplFeatureListCursor(0)
160166

161167
t.Run("simple successful query", func(t *testing.T) {
162168
expectedResult := &MissingOneImplFeatureListPage{
@@ -183,6 +189,8 @@ func testMissingOneImplFeatureListSuite(
183189
targetBrowser,
184190
otherBrowsers,
185191
expectedResult,
192+
&token,
193+
pageSize,
186194
)
187195
})
188196

@@ -199,6 +207,8 @@ func testMissingOneImplFeatureListSuite(
199207
targetBrowser,
200208
otherBrowsers,
201209
expectedResult,
210+
&token,
211+
pageSize,
202212
)
203213
})
204214

@@ -231,8 +241,94 @@ func testMissingOneImplFeatureListSuite(
231241
targetBrowser,
232242
subsetBrowsers,
233243
expectedResult,
244+
&token,
245+
pageSize,
234246
)
235247
})
248+
249+
t.Run("simple successful query with pagination", func(t *testing.T) {
250+
pageToken := encodeMissingOneImplFeatureListCursor(1)
251+
expectedResult := &MissingOneImplFeatureListPage{
252+
NextPageToken: nil,
253+
FeatureList: []MissingOneImplFeature{
254+
// fooBrowser 113 release
255+
// Currently supported features:
256+
// fooBrowser: FeatureX, FeatureZ, FeatureY, FeatureW
257+
// barBrowser: FeatureX, FeatureZ, FeatureY, FeatureW
258+
// bazBrowser: FeatureX, FeatureY
259+
// Missing in on for bazBrowser: FeatureW, FeatureZ
260+
{
261+
WebFeatureID: "FeatureZ",
262+
},
263+
},
264+
}
265+
assertMissingOneImplFeatureList(
266+
ctx,
267+
t,
268+
targetDate,
269+
targetBrowser,
270+
otherBrowsers,
271+
expectedResult,
272+
&pageToken,
273+
pageSize,
274+
)
275+
})
276+
277+
t.Run("Return a page token with page size 1", func(t *testing.T) {
278+
onePerPage := 1
279+
returnToken := encodeMissingOneImplFeatureListCursor(1)
280+
expectedResult := &MissingOneImplFeatureListPage{
281+
NextPageToken: &returnToken,
282+
FeatureList: []MissingOneImplFeature{
283+
// fooBrowser 113 release
284+
// Currently supported features:
285+
// fooBrowser: FeatureX, FeatureZ, FeatureY, FeatureW
286+
// barBrowser: FeatureX, FeatureZ, FeatureY, FeatureW
287+
// bazBrowser: FeatureX, FeatureY
288+
// Missing in on for bazBrowser: FeatureW, FeatureZ
289+
{
290+
WebFeatureID: "FeatureW",
291+
},
292+
},
293+
}
294+
assertMissingOneImplFeatureList(
295+
ctx,
296+
t,
297+
targetDate,
298+
targetBrowser,
299+
otherBrowsers,
300+
expectedResult,
301+
&token,
302+
onePerPage,
303+
)
304+
305+
pageTwoToken := encodeMissingOneImplFeatureListCursor(2)
306+
expectedResultPageTwo := &MissingOneImplFeatureListPage{
307+
NextPageToken: &pageTwoToken,
308+
FeatureList: []MissingOneImplFeature{
309+
// fooBrowser 113 release
310+
// Currently supported features:
311+
// fooBrowser: FeatureX, FeatureZ, FeatureY, FeatureW
312+
// barBrowser: FeatureX, FeatureZ, FeatureY, FeatureW
313+
// bazBrowser: FeatureX, FeatureY
314+
// Missing in on for bazBrowser: FeatureW, FeatureZ
315+
{
316+
WebFeatureID: "FeatureZ",
317+
},
318+
},
319+
}
320+
assertMissingOneImplFeatureList(
321+
ctx,
322+
t,
323+
targetDate,
324+
targetBrowser,
325+
otherBrowsers,
326+
expectedResultPageTwo,
327+
&returnToken,
328+
onePerPage,
329+
)
330+
})
331+
236332
})
237333
}
238334

0 commit comments

Comments
 (0)