Skip to content

Commit a48e63e

Browse files
committed
fix(search): keep earlier Slack pages on any later-page failure and page past short pages
1 parent e8e4a82 commit a48e63e

2 files changed

Lines changed: 63 additions & 9 deletions

File tree

‎apps/sim/lib/sim-search/live/providers.test.ts‎

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,56 @@ describe('Slack RTS pages', () => {
321321
expect(result.message).toContain('nextCursor')
322322
})
323323

324+
it('keeps earlier pages when a later page fails outside the Slack API', async () => {
325+
const api = client()
326+
api.json
327+
.mockResolvedValueOnce(slackPage(1, 20, 'page-2'))
328+
.mockRejectedValueOnce(new Error('socket hang up'))
329+
const result = await searchSlack(api, {
330+
...input,
331+
limit: 50,
332+
scopes,
333+
native: { provider: 'slack', query: 'launch' },
334+
})
335+
expect(result.documents).toHaveLength(20)
336+
expect(result.nextCursor).toBe('page-2')
337+
expect(result.partial).toBe(true)
338+
})
339+
340+
it('stops paging once the caller cancels the search', async () => {
341+
const api = client()
342+
const cancelled = new AbortController()
343+
api.json.mockResolvedValueOnce(slackPage(1, 20, 'page-2')).mockImplementationOnce(async () => {
344+
cancelled.abort()
345+
throw new Error('aborted')
346+
})
347+
await expect(
348+
searchSlack(api, {
349+
...input,
350+
limit: 50,
351+
scopes,
352+
signal: cancelled.signal,
353+
native: { provider: 'slack', query: 'launch' },
354+
})
355+
).rejects.toThrow('aborted')
356+
})
357+
358+
it('keeps paging past short pages that still carry a cursor', async () => {
359+
const api = client()
360+
api.json
361+
.mockResolvedValueOnce(slackPage(1, 10, 'page-2'))
362+
.mockResolvedValueOnce(slackPage(11, 10, 'page-3'))
363+
.mockResolvedValueOnce(slackPage(21, 10))
364+
const result = await searchSlack(api, {
365+
...input,
366+
limit: 30,
367+
scopes,
368+
native: { provider: 'slack', query: 'launch' },
369+
})
370+
expect(api.json).toHaveBeenCalledTimes(3)
371+
expect(result.documents).toHaveLength(30)
372+
})
373+
324374
it('still fails when the first page fails', async () => {
325375
const api = client()
326376
api.json.mockResolvedValue({ ok: false, error: 'ratelimited' })

‎apps/sim/lib/sim-search/live/slack.ts‎

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -39,15 +39,16 @@ function slackResult(value: unknown) {
3939
/** Results one Real-time Search call returns at most. */
4040
const SLACK_PAGE_SIZE = 20
4141
/**
42-
* Real-time Search pages a search aimed at Slack reads to fill a limit above one page. Each page
43-
* counts against the member's RTS rate limit of about 10 calls a minute.
42+
* Real-time Search pages a search aimed at Slack reads to fill its limit. Each page counts
43+
* against the member's RTS rate limit of about 10 calls a minute.
4444
*/
4545
const MAX_SLACK_SEARCH_PAGES = 3
4646

4747
/**
4848
* A search across every source reads one page, so Slack shares the result list with the other
4949
* sources. A Slack-scoped or native Slack search follows the cursor until it fills its limit.
50-
* A later page that fails keeps the earlier pages and returns the cursor it could not read.
50+
* A later page that fails keeps the earlier pages and returns the cursor it could not read,
51+
* unless the caller cancelled the search.
5152
*/
5253
export async function searchSlack(
5354
client: NativeClient,
@@ -58,10 +59,7 @@ export async function searchSlack(
5859
'reconnect',
5960
'Reconnect your Slack account to grant permission to search.'
6061
)
61-
const pages =
62-
input.native || input.filters?.source === 'slack'
63-
? Math.min(Math.ceil(input.limit / SLACK_PAGE_SIZE), MAX_SLACK_SEARCH_PAGES)
64-
: 1
62+
const pages = input.native || input.filters?.source === 'slack' ? MAX_SLACK_SEARCH_PAGES : 1
6563
const first = await searchSlackPage(client, input, input.native?.cursor, input.limit)
6664
const documents = [...first.documents]
6765
let nextCursor = first.nextCursor
@@ -71,12 +69,18 @@ export async function searchSlack(
7169
documents.push(...more.documents)
7270
nextCursor = more.nextCursor
7371
} catch (error) {
74-
if (!(error instanceof NativeSearchError)) throw error
72+
if (input.signal?.aborted) throw error
7573
return {
7674
documents,
7775
nextCursor,
7876
partial: true,
79-
message: joinMessages([first.message, error.message, 'Continue with nextCursor for more.']),
77+
message: joinMessages([
78+
first.message,
79+
error instanceof NativeSearchError
80+
? error.message
81+
: 'Slack could not return more results.',
82+
'Continue with nextCursor for more.',
83+
]),
8084
}
8185
}
8286
}

0 commit comments

Comments
 (0)