Skip to content

Commit 455e24f

Browse files
fix(api): wait for knowledge dispatch and encode filenames
1 parent 766526b commit 455e24f

4 files changed

Lines changed: 43 additions & 9 deletions

File tree

apps/sim/app/api/v2/files/[fileId]/route.test.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,25 @@ describe('v2 single-file routes', () => {
141141
})
142142
})
143143

144+
it('encodes special characters in the extended download filename', async () => {
145+
mocks.download.mockResolvedValueOnce({
146+
file: fileRecord({ name: "it's (final)* café.pdf" }),
147+
stream: new Blob(['pdf']).stream(),
148+
contentType: 'application/pdf',
149+
contentLength: 3,
150+
})
151+
152+
const response = await GET(
153+
new NextRequest(`http://localhost:3000/api/v2/files/${FILE_ID}?workspaceId=${WORKSPACE_ID}`),
154+
context
155+
)
156+
157+
expect(response.status).toBe(200)
158+
expect(response.headers.get('Content-Disposition')).toBe(
159+
`attachment; filename="it's (final)* caf_.pdf"; filename*=UTF-8''it%27s%20%28final%29%2A%20caf%C3%A9.pdf`
160+
)
161+
})
162+
144163
it('conceals cross-workspace download authorization', async () => {
145164
mocks.download.mockRejectedValue(new NoWorkspaceAccessError())
146165

apps/sim/app/api/v2/files/[fileId]/route.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import { deleteWorkspaceFileOperation } from '@/lib/workspace-files/application/
1414
import { downloadWorkspaceFileStream } from '@/lib/workspace-files/application/download-workspace-file'
1515
import { fileOperations } from '@/lib/workspace-files/application/operations'
1616
import { renameWorkspaceFile } from '@/lib/workspace-files/application/rename-workspace-file'
17+
import { encodeFilenameForHeader } from '@/app/api/files/utils'
1718
import { toV2File } from '@/app/api/v2/files/utils'
1819

1920
export const dynamic = 'force-dynamic'
@@ -42,7 +43,7 @@ export const GET = defineV2BinaryRoute({
4243
present: ({ file, stream, contentType, contentLength }) => ({
4344
body: stream,
4445
contentType,
45-
contentDisposition: `attachment; filename="${file.name.replace(/[^\w.-]/g, '_')}"; filename*=UTF-8''${encodeURIComponent(file.name)}`,
46+
contentDisposition: `attachment; ${encodeFilenameForHeader(file.name)}`,
4647
contentLength,
4748
}),
4849
})

apps/sim/lib/knowledge/application/documents.test.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -449,6 +449,26 @@ describe('knowledge document application use cases', () => {
449449
expect(mocks.recordAudit).not.toHaveBeenCalled()
450450
})
451451

452+
it('propagates processing dispatch failures without reporting upload success', async () => {
453+
const failure = new Error('queue unavailable')
454+
mocks.processQueue.mockRejectedValueOnce(failure)
455+
456+
await expect(
457+
uploadKnowledgeDocument.execute({
458+
principal: { kind: 'session', userId: 'user-1', sessionId: 'session-1' },
459+
input: {
460+
knowledgeBaseId: 'knowledge-1',
461+
assertedWorkspaceId: 'workspace-1',
462+
document,
463+
},
464+
})
465+
).rejects.toBe(failure)
466+
467+
expect(mocks.createDocument).toHaveBeenCalledOnce()
468+
expect(mocks.processQueue).toHaveBeenCalledOnce()
469+
expect(mocks.recordAudit).not.toHaveBeenCalled()
470+
})
471+
452472
it('bounds bulk document creation before billing or orchestration', async () => {
453473
await expect(
454474
createKnowledgeDocuments.execute({

apps/sim/lib/knowledge/application/documents.ts

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -287,19 +287,13 @@ export const uploadKnowledgeDocument = defineAuthorizedKnowledgeUseCase({
287287
fileSize: document.fileSize,
288288
mimeType: document.mimeType,
289289
}
290-
processDocumentsWithQueue(
290+
await processDocumentsWithQueue(
291291
[processingDocument],
292292
context.knowledgeBaseId,
293293
input.processingOptions ?? {},
294294
requestId,
295295
billingAttribution
296-
).catch((error: unknown) => {
297-
logger.error('Knowledge document processing pipeline failed', {
298-
knowledgeBaseId: context.knowledgeBaseId,
299-
documentId: document.id,
300-
error,
301-
})
302-
})
296+
)
303297
}
304298
return { document, created: true as const }
305299
},

0 commit comments

Comments
 (0)