Skip to content

Commit 790ef5a

Browse files
committed
fix(textract): preserve first-page metadata across async pagination
pollTextractJob's merge callbacks spread only the latest page, dropping any field (DocumentMetadata, model version) the first page had but a follow-up NextToken page omits. Merge accumulated first so later pages only override fields they actually return.
1 parent dfe14bf commit 790ef5a

3 files changed

Lines changed: 35 additions & 0 deletions

File tree

apps/sim/app/api/tools/textract/analyze-expense/route.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => {
165165
(nextToken) =>
166166
client.send(new GetExpenseAnalysisCommand({ JobId: jobId, NextToken: nextToken })),
167167
(accumulated, page) => ({
168+
...accumulated,
168169
...page,
169170
ExpenseDocuments: [
170171
...(accumulated.ExpenseDocuments ?? []),

apps/sim/app/api/tools/textract/parse/route.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => {
156156
new GetDocumentTextDetectionCommand({ JobId: jobId, NextToken: nextToken })
157157
),
158158
(accumulated, page) => ({
159+
...accumulated,
159160
...page,
160161
Blocks: [...(accumulated.Blocks ?? []), ...(page.Blocks ?? [])],
161162
})

apps/sim/app/api/tools/textract/shared.test.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ describe('pollTextractJob', () => {
111111
return { JobStatus: 'SUCCEEDED', Blocks: [{ Id: '2' }] }
112112
},
113113
(accumulated, page) => ({
114+
...accumulated,
114115
...page,
115116
Blocks: [...(accumulated.Blocks ?? []), ...(page.Blocks ?? [])],
116117
})
@@ -120,6 +121,38 @@ describe('pollTextractJob', () => {
120121
expect(result.Blocks).toHaveLength(2)
121122
})
122123

124+
it('preserves fields the first page has but a later page omits (e.g. DocumentMetadata)', async () => {
125+
const result = await pollTextractJob<{
126+
JobStatus?: string
127+
NextToken?: string
128+
Blocks?: unknown[]
129+
DocumentMetadata?: { Pages?: number }
130+
}>(
131+
'req-4',
132+
logger,
133+
async (nextToken) => {
134+
if (!nextToken) {
135+
return {
136+
JobStatus: 'SUCCEEDED',
137+
Blocks: [{ Id: '1' }],
138+
DocumentMetadata: { Pages: 3 },
139+
NextToken: 'next',
140+
}
141+
}
142+
// Follow-up pages from AWS often omit DocumentMetadata/model-version fields.
143+
return { JobStatus: 'SUCCEEDED', Blocks: [{ Id: '2' }] }
144+
},
145+
(accumulated, page) => ({
146+
...accumulated,
147+
...page,
148+
Blocks: [...(accumulated.Blocks ?? []), ...(page.Blocks ?? [])],
149+
})
150+
)
151+
152+
expect(result.Blocks).toHaveLength(2)
153+
expect(result.DocumentMetadata).toEqual({ Pages: 3 })
154+
})
155+
123156
it('throws a TextractRouteError when the job fails', async () => {
124157
await expect(
125158
pollTextractJob(

0 commit comments

Comments
 (0)