Skip to content

Commit ddf7030

Browse files
pnvasanthclaude
andcommitted
feat: propagate staleCursor to GraphQL PageInfo and add tests
- Update connectionFromNodes to extract staleCursor from FeedResponse and include it in PageInfo - Add tests for FeedClient parsing staleCursor from feed service response - Add tests for connectionFromNodes propagating staleCursor to PageInfo Co-Authored-By: Claude Opus 4.5 <[email protected]>
1 parent fb955ee commit ddf7030

File tree

2 files changed

+120
-0
lines changed

2 files changed

+120
-0
lines changed

__tests__/integrations/feed.ts

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ import {
66
FeedPreferencesConfigGenerator,
77
FeedResponse,
88
} from '../../src/integrations/feed';
9+
import {
10+
connectionFromNodes,
11+
feedCursorPageGenerator,
12+
} from '../../src/schema/common';
913
import { MockContext, saveFixtures } from '../helpers';
1014
import { deleteKeysByPattern } from '../../src/redis';
1115
import createOrGetConnection from '../../src/db';
@@ -128,6 +132,112 @@ describe('FeedClient', () => {
128132
],
129133
});
130134
});
135+
136+
it('should parse staleCursor from feed service response', async () => {
137+
const responseWithStaleCursor = {
138+
...rawFeedResponse,
139+
cursor: 'abc123',
140+
stale_cursor: true,
141+
};
142+
143+
nock(url)
144+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
145+
.post('', config as any)
146+
.reply(200, responseWithStaleCursor);
147+
148+
const feedClient = new FeedClient(url);
149+
const feed = await feedClient.fetchFeed(ctx, 'id', config);
150+
expect(feed).toMatchObject({
151+
cursor: 'abc123',
152+
staleCursor: true,
153+
});
154+
});
155+
156+
it('should not include staleCursor when not present in response', async () => {
157+
nock(url)
158+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
159+
.post('', config as any)
160+
.reply(200, rawFeedResponse);
161+
162+
const feedClient = new FeedClient(url);
163+
const feed = await feedClient.fetchFeed(ctx, 'id', config);
164+
expect(feed.staleCursor).toBeUndefined();
165+
});
166+
});
167+
168+
describe('connectionFromNodes with staleCursor', () => {
169+
const pageGenerator = feedCursorPageGenerator<{ id: string }>(30, 50);
170+
171+
it('should include staleCursor in pageInfo when present in queryParams', () => {
172+
const nodes = [{ id: '1' }, { id: '2' }];
173+
const page = { limit: 30 };
174+
const queryParams: FeedResponse = {
175+
data: [
176+
['1', null],
177+
['2', null],
178+
],
179+
cursor: 'next-cursor',
180+
staleCursor: true,
181+
};
182+
183+
const result = connectionFromNodes(
184+
{},
185+
nodes,
186+
undefined,
187+
page,
188+
pageGenerator,
189+
undefined,
190+
queryParams,
191+
);
192+
193+
expect(result.pageInfo.staleCursor).toBe(true);
194+
});
195+
196+
it('should have undefined staleCursor in pageInfo when not present in queryParams', () => {
197+
const nodes = [{ id: '1' }, { id: '2' }];
198+
const page = { limit: 30 };
199+
const queryParams: FeedResponse = {
200+
data: [
201+
['1', null],
202+
['2', null],
203+
],
204+
cursor: 'next-cursor',
205+
};
206+
207+
const result = connectionFromNodes(
208+
{},
209+
nodes,
210+
undefined,
211+
page,
212+
pageGenerator,
213+
undefined,
214+
queryParams,
215+
);
216+
217+
expect(result.pageInfo.staleCursor).toBeUndefined();
218+
});
219+
220+
it('should include staleCursor in pageInfo even when nodes are empty', () => {
221+
const nodes: { id: string }[] = [];
222+
const page = { limit: 30 };
223+
const queryParams: FeedResponse = {
224+
data: [],
225+
staleCursor: true,
226+
};
227+
228+
const result = connectionFromNodes(
229+
{},
230+
nodes,
231+
undefined,
232+
page,
233+
pageGenerator,
234+
undefined,
235+
queryParams,
236+
);
237+
238+
expect(result.pageInfo.staleCursor).toBe(true);
239+
expect(result.edges).toHaveLength(0);
240+
});
131241
});
132242

133243
describe('FeedPreferencesConfigGenerator', () => {

src/schema/common.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -345,13 +345,22 @@ export function connectionFromNodes<
345345
queryParams?: TParams,
346346
): Connection<TReturn> & TExtra {
347347
const transformedNodes = pageGenerator.transformNodes?.(page, nodes) ?? nodes;
348+
// Extract staleCursor from queryParams if it's a FeedResponse
349+
const staleCursor =
350+
queryParams &&
351+
typeof queryParams === 'object' &&
352+
'staleCursor' in queryParams
353+
? (queryParams as FeedResponse).staleCursor
354+
: undefined;
355+
348356
if (!transformedNodes.length) {
349357
return {
350358
pageInfo: {
351359
startCursor: null,
352360
endCursor: null,
353361
hasNextPage: false,
354362
hasPreviousPage: false,
363+
staleCursor,
355364
},
356365
edges: [],
357366
...extra,
@@ -370,6 +379,7 @@ export function connectionFromNodes<
370379
endCursor: edges[edges.length - 1].cursor,
371380
hasNextPage: pageGenerator.hasNextPage(page, nodes.length, total),
372381
hasPreviousPage: pageGenerator.hasPreviousPage(page, nodes.length, total),
382+
staleCursor,
373383
},
374384
edges,
375385
...extra,

0 commit comments

Comments
 (0)