Skip to content

Commit 9410ba5

Browse files
Merge pull request #69 from stepzen-dev/add_offset_test
chore: update OFFSET pagination snippet
2 parents 15b26fb + f99b876 commit 9410ba5

File tree

4 files changed

+171
-1
lines changed

4 files changed

+171
-1
lines changed

rest/pagination/README.md

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,27 @@ The field `Query.customersPageNumber` is an example of `PAGE_NUMBER` pagination.
6060

6161
## OFFSET pagination
6262

63-
TODO
63+
A REST API using the OFFSET style pagination typically takes two arguments, limit and offset,
64+
and returns a list of values and metadata indicating the total number of records. The response layout
65+
varies, but is similar to:
66+
67+
```json
68+
{
69+
"meta": {
70+
"records": 8
71+
},
72+
"values": [
73+
{
74+
"name": "Bob"
75+
},
76+
{
77+
"name": "Charlie"
78+
}
79+
]
80+
}
81+
```
82+
83+
The field `Query.customersOffset` is an example of `OFFSET` pagination.
6484

6585
## NEXT_CURSOR pagination
6686

rest/pagination/api.graphql

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,4 +117,82 @@ type Query {
117117
}
118118
"""
119119
)
120+
121+
"""
122+
`customersOffset` pages through twenty three generated `Customer` objects.
123+
The data is supplied by a simulated REST API using offset pagination
124+
but is exposed using standard GraphQL pagination.
125+
126+
Typically the `endpoint` argument of `@rest` would have query
127+
parameters that set the pagination arguments expected by the
128+
REST API from the field arguments `first` and `after` (offset defaults to 0 if empty),
129+
for example: `?limt=$first&offset=$after`.
130+
"""
131+
customersOffset(first: Int!, after: String = ""): CustomerConnection
132+
@rest(
133+
# pagination sets the type of pagination the REST API uses
134+
# and for OFFSET requires a setter that declares
135+
# where the total number of records are in the JSON response.
136+
pagination: {
137+
type: OFFSET
138+
setters: [{ field: "total", path: "meta.records" }]
139+
}
140+
141+
# resultroot indicates where root in the JSON response
142+
# for the values that will populate the nodes.
143+
# Note this does not affect pagination setters, they
144+
# are always relative to the root of the response.
145+
resultroot: "values[]"
146+
147+
# Ecmascript (with empty endpoint) is used to mimic the response from a REST api.
148+
# Note ECMAScript is only used to generate a mock response with customer objects and page number metadata,
149+
# using @rest against a real endpoint would not typically require any ECMAScript.
150+
endpoint: "stepzen:empty"
151+
ecmascript: """
152+
function transformREST() {
153+
// A total of 23 items will be returned
154+
const totalItems = 23;
155+
156+
// Pagination field arguments
157+
// Since this is OFFSET pagination
158+
// "after" is decoded by StepZen from the opaque string value
159+
// and passed into @rest as a integer offset value,
160+
// with the offset of the first record being zero.
161+
const limit = get('first');
162+
const offset = get('after');
163+
164+
// metadata - total number of records
165+
const records = Math.ceil(totalItems / limit)
166+
167+
// generate customers for nodes based on the limit and offset values
168+
const startIndex = offset+1 || 1;
169+
const endIndex = Math.min(startIndex + limit, totalItems+1);
170+
var customers = []
171+
for (let i = startIndex; i < endIndex; i++) {
172+
customers.push({
173+
id: i,
174+
name: 'name-' + i,
175+
email: 'user-' + i + '@example.com'
176+
});
177+
}
178+
179+
// This returns a typical layout of a REST response
180+
// when pagination is through an offset.
181+
// @rest must be configured to match the REST response layout.
182+
//
183+
// pagination setters defines that the page count
184+
// is taken from meta.records
185+
//
186+
// resultroot corresponds to the location that contains the
187+
// data values. Note the REST API returns the customer objects,
188+
// StepZen automatically creates the connection/edges structure
189+
// for the values.
190+
return ({
191+
meta: { 'records': records },
192+
values: customers
193+
}
194+
);
195+
}
196+
"""
197+
)
120198
}

rest/pagination/requests.graphql

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,23 @@ query CustomersPageNumber($first: Int!, $after: String) {
1515
}
1616
}
1717

18+
# Page through customers from a REST API using page numbers
19+
query CustomersOffset($first: Int!, $after: String) {
20+
customersOffset(first: $first, after: $after) {
21+
edges {
22+
node {
23+
id
24+
name
25+
email
26+
}
27+
}
28+
pageInfo {
29+
endCursor
30+
hasNextPage
31+
}
32+
}
33+
}
34+
1835
# Return the first customer (using a reshape version of a paginated field)
1936
query FirstCustomer {
2037
firstCustomer {

rest/pagination/tests/Test.js

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,61 @@ describe(testDescription, function () {
100100
nCustomers: generateNodes(1, 5).map(edge => edge.node),
101101
},
102102
},
103+
{
104+
label: "customersOffset-1-10",
105+
query: requests,
106+
operationName: 'CustomersOffset',
107+
variables: { first: 10 },
108+
expected: {
109+
customersOffset: {
110+
edges: generateNodes(1, 10),
111+
pageInfo: {
112+
endCursor:
113+
"eyJjIjoiTzpRdWVyeTpjdXN0b21lcnNPZmZzZXQiLCJvIjo5fQ==",
114+
hasNextPage: false,
115+
},
116+
},
117+
},
118+
},
119+
{
120+
label: "customersOffset-10-20",
121+
query: requests,
122+
operationName: 'CustomersOffset',
123+
variables: {
124+
first: 10,
125+
after: "eyJjIjoiTzpRdWVyeTpjdXN0b21lcnNPZmZzZXQiLCJvIjo5fQ=="
126+
},
127+
expected: {
128+
customersOffset: {
129+
edges: generateNodes(11, 20),
130+
pageInfo: {
131+
endCursor:
132+
"eyJjIjoiTzpRdWVyeTpjdXN0b21lcnNPZmZzZXQiLCJvIjoxOX0=",
133+
hasNextPage: false,
134+
},
135+
},
136+
},
137+
},
138+
{
139+
label: "customersOffset-21-23",
140+
query: requests,
141+
operationName: 'CustomersOffset',
142+
variables: {
143+
first: 10,
144+
after:
145+
"eyJjIjoiTzpRdWVyeTpjdXN0b21lcnNPZmZzZXQiLCJvIjoxOX0=",
146+
},
147+
expected: {
148+
customersOffset: {
149+
edges: generateNodes(21, 23),
150+
pageInfo: {
151+
endCursor:
152+
"eyJjIjoiTzpRdWVyeTpjdXN0b21lcnNPZmZzZXQiLCJvIjoyMn0=",
153+
hasNextPage: false,
154+
},
155+
},
156+
},
157+
},
103158
];
104159
return deployAndRun(__dirname, tests, stepzen.admin);
105160
});

0 commit comments

Comments
 (0)