Skip to content

Commit 96b203a

Browse files
improvement(tables): truncate over-limit CSV imports to the plan cap instead of rejecting
1 parent 1abd7cd commit 96b203a

2 files changed

Lines changed: 19 additions & 12 deletions

File tree

apps/sim/lib/copilot/tools/server/table/user-table.test.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -268,17 +268,21 @@ describe('userTableServerTool.create_from_file', () => {
268268
expect(createArgs.maxTables).toBe(3)
269269
})
270270

271-
it('rejects a file exceeding the plan row limit without creating a table', async () => {
271+
it('truncates to the plan row limit and reports dropped rows', async () => {
272+
// File has 2 data rows (Alice, Bob); plan cap is 1.
272273
mockGetWorkspaceTableLimits.mockResolvedValueOnce({ maxRowsPerTable: 1, maxTables: 3 })
273274

274275
const result = await userTableServerTool.execute(
275276
{ operation: 'create_from_file', args: { fileId: 'file-1' } },
276277
{ userId: 'user-1', workspaceId: 'workspace-1' }
277278
)
278279

279-
expect(result.success).toBe(false)
280-
expect(result.message).toMatch(/exceeds this plan's limit/i)
281-
expect(mockCreateTable).not.toHaveBeenCalled()
280+
expect(result.success).toBe(true)
281+
expect(mockCreateTable).toHaveBeenCalledTimes(1)
282+
const insertCall = mockBatchInsertRows.mock.calls[0][0] as { rows: unknown[] }
283+
expect(insertCall.rows).toHaveLength(1)
284+
expect(result.data?.rowCount).toBe(1)
285+
expect(result.message).toMatch(/dropped 1 row/i)
282286
expect(mockDeleteTable).not.toHaveBeenCalled()
283287
})
284288

apps/sim/lib/copilot/tools/server/table/user-table.ts

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -767,12 +767,8 @@ export const userTableServerTool: BaseServerTool<UserTableArgs, UserTableResult>
767767
assertNotAborted()
768768
const planLimits = await getWorkspaceTableLimits(workspaceId)
769769

770-
if (rows.length > planLimits.maxRowsPerTable) {
771-
return {
772-
success: false,
773-
message: `"${file.name}" has ${rows.length.toLocaleString()} rows, which exceeds this plan's limit of ${planLimits.maxRowsPerTable.toLocaleString()} rows per table.`,
774-
}
775-
}
770+
const droppedRows = Math.max(0, rows.length - planLimits.maxRowsPerTable)
771+
const rowsToImport = droppedRows > 0 ? rows.slice(0, planLimits.maxRowsPerTable) : rows
776772

777773
const table = await createTable(
778774
{
@@ -787,7 +783,7 @@ export const userTableServerTool: BaseServerTool<UserTableArgs, UserTableResult>
787783
requestId
788784
)
789785

790-
const coerced = coerceRowsForTable(rows, { columns }, headerToColumn)
786+
const coerced = coerceRowsForTable(rowsToImport, { columns }, headerToColumn)
791787
let inserted: number
792788
try {
793789
inserted = await batchInsertAll(table.id, coerced, table, workspaceId, context)
@@ -801,12 +797,19 @@ export const userTableServerTool: BaseServerTool<UserTableArgs, UserTableResult>
801797
fileName: file.name,
802798
columns: columns.length,
803799
rows: inserted,
800+
droppedRows,
804801
userId: context.userId,
805802
})
806803

804+
const createdMessage = `Created table "${table.name}" with ${columns.length} columns and ${inserted.toLocaleString()} rows from "${file.name}"`
805+
const message =
806+
droppedRows > 0
807+
? `${createdMessage}. Dropped ${droppedRows.toLocaleString()} row(s) that exceed this plan's limit of ${planLimits.maxRowsPerTable.toLocaleString()} rows per table.`
808+
: createdMessage
809+
807810
return {
808811
success: true,
809-
message: `Created table "${table.name}" with ${columns.length} columns and ${inserted} rows from "${file.name}"`,
812+
message,
810813
data: {
811814
tableId: table.id,
812815
tableName: table.name,

0 commit comments

Comments
 (0)