Skip to content

feat(tables): import csv into existing tables#4199

Merged
icecrasher321 merged 4 commits intostagingfrom
feat/tables-import-csv
Apr 16, 2026
Merged

feat(tables): import csv into existing tables#4199
icecrasher321 merged 4 commits intostagingfrom
feat/tables-import-csv

Conversation

@icecrasher321
Copy link
Copy Markdown
Collaborator

Summary

Import csv into existing tables with configurable mapping of columns. Can append to table [checking uniqueness constraints] or replace rows.

Also added capability to mothership.

Type of Change

  • New feature

Testing

Tested manually.

Checklist

  • Code follows project style guidelines
  • Self-reviewed my changes
  • Tests added/updated and passing
  • No new warnings introduced
  • I confirm that I have read and agree to the terms outlined in the Contributor License Agreement (CLA)

@vercel
Copy link
Copy Markdown

vercel bot commented Apr 16, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
docs Skipped Skipped Apr 16, 2026 8:28pm

Request Review

@cursor
Copy link
Copy Markdown

cursor bot commented Apr 16, 2026

PR Summary

Medium Risk
Touches table mutation paths by adding transactional row replacement and a new import endpoint, so bugs could lead to data loss or constraint/limit regressions despite added validation and tests.

Overview
Adds a new API endpoint POST /api/table/[tableId]/import-csv to import CSV/TSV data into an existing table with append or transactional replace modes, optional explicit header→column mapping, row-limit checks, archived/workspace validation, and client-friendly 400 errors (including partial insert counts on failed appends).

Introduces shared csv-import utilities (parsing, schema inference, name sanitization, mapping validation, coercion, size/batch constants) and refactors the “create table from CSV” route plus the Copilot user_table tool to reuse them; also adds replaceTableRows in table service with schema/size/unique checks and a locked transaction.

Wires a new ImportCsvDialog into the table header and tables list context menu, adds a useImportCsvIntoTable hook, and updates Copilot tool schemas/catalog to accept mode + mapping for table file imports; includes new unit tests covering route behavior, csv-import helpers, Copilot import, and file-output unwrapping.

Reviewed by Cursor Bugbot for commit 1a45b8e. Configure here.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Apr 16, 2026

Greptile Summary

This PR adds CSV import into existing tables — with append/replace modes and configurable column mapping — through a new /api/table/[tableId]/import-csv route, an ImportCsvDialog UI component, and Mothership (copilot) support. It also centralises all CSV parsing/coercion logic that was previously duplicated across the create-from-CSV route and the copilot tool into a shared lib/table/csv-import.ts module.

All remaining findings are P2: two instances of subpath imports instead of the @/lib/table barrel, a duplicated type definition in the query hooks, and a harmless double-unwrap in the copilot file serialiser.

Confidence Score: 5/5

Safe to merge — no blocking issues; only minor style/duplication P2 findings.

All P2. The core implementation is correct: the replace path uses a transactional FOR UPDATE lock, query invalidation covers rows + count + lists, validation catches required columns, unique constraints, and capacity before writing, and test coverage is thorough across both the route and the shared library.

import-csv-dialog.tsx (barrel import style) and hooks/queries/tables.ts (duplicate type declarations)

Important Files Changed

Filename Overview
apps/sim/app/api/table/[tableId]/import-csv/route.ts New POST route for importing CSV into an existing table; correctly validates auth, workspace ownership, archived state, mapping, and capacity limits before delegating to shared service helpers.
apps/sim/lib/table/csv-import.ts New shared CSV parsing, schema inference, validation, and coercion library; well-documented with clear exports and comprehensive test coverage.
apps/sim/lib/table/service.ts Adds replaceTableRows using a transaction with a FOR UPDATE lock to avoid empty-table intermediate states; validates schema, row size, uniqueness within the batch, and capacity before executing.
apps/sim/app/workspace/[workspaceId]/tables/components/import-csv-dialog/import-csv-dialog.tsx New dialog for mapping and importing a CSV into an existing table; minor P2 issue — imports come from subpath @/lib/table/csv-import and @/lib/table/types instead of the barrel @/lib/table.
apps/sim/hooks/queries/tables.ts Adds useImportCsvIntoTable mutation; correctly calls invalidateRowCount (which invalidates rows + detail + lists). Minor issue: re-declares CsvHeaderMapping and CsvImportMode types that already exist in @/lib/table.
apps/sim/lib/copilot/tools/server/table/user-table.ts Upgrades import_file operation to use shared CSV helpers; adds mode (append/replace) and mapping parameters, workspace ownership check, and archived table guard.
apps/sim/lib/copilot/request/tools/files.ts Extracts unwrapFunctionExecuteOutput helper; serializeOutputForFile now passes an already-unwrapped value to extractTabularData which calls unwrapFunctionExecuteOutput a second time (harmless no-op but redundant).
apps/sim/app/api/table/[tableId]/import-csv/route.test.ts Comprehensive test suite for the new import route; covers auth, access control, append, replace, mapping, capacity limits, unique violations, TSV, and invalid file extensions.
apps/sim/lib/table/csv-import.test.ts Unit tests for all CSV import helpers; good coverage of parseCsvBuffer (including BOM stripping and Uint8Array input), type inference, sanitization, auto-mapping, and coercion.

Sequence Diagram

sequenceDiagram
    actor User
    participant Dialog as ImportCsvDialog
    participant Hook as useImportCsvIntoTable
    participant API as POST /api/table/[tableId]/import-csv
    participant CsvLib as lib/table/csv-import
    participant Service as lib/table/service

    User->>Dialog: Select CSV file
    Dialog->>CsvLib: parseCsvBuffer(Uint8Array)
    CsvLib-->>Dialog: { headers, rows }
    Dialog->>CsvLib: buildAutoMapping(headers, schema)
    CsvLib-->>Dialog: mapping
    Dialog-->>User: Show column mapping UI

    User->>Dialog: Confirm import (append | replace)
    Dialog->>Hook: mutateAsync({ tableId, file, mode, mapping })
    Hook->>API: POST FormData
    API->>API: checkSessionOrInternalAuth
    API->>API: checkAccess(tableId, userId, write)
    API->>CsvLib: parseCsvBuffer(Buffer)
    CsvLib-->>API: { headers, rows }
    API->>CsvLib: buildAutoMapping / validateMapping
    CsvLib-->>API: validation result
    API->>CsvLib: coerceRowsForTable(rows, schema, effectiveMap)
    CsvLib-->>API: coerced RowData[]

    alt mode = append
        API->>Service: batchInsertRows (in batches of 1000)
        Service-->>API: inserted rows
    else mode = replace
        API->>Service: replaceTableRows (transactional DELETE + INSERT)
        Service-->>API: { deletedCount, insertedCount }
    end

    API-->>Hook: { success, data }
    Hook->>Hook: invalidateRowCount (rows + detail + lists)
    Hook-->>Dialog: result
    Dialog-->>User: toast + close
Loading

Comments Outside Diff (3)

  1. apps/sim/app/workspace/[workspaceId]/tables/components/import-csv-dialog/import-csv-dialog.tsx, line 893-895 (link)

    P2 Subpath imports bypass the barrel

    @/lib/table/csv-import and @/lib/table/types are re-exported via the @/lib/table barrel (index.ts). Per project conventions, consumers should import from the barrel, not from subpaths directly.

    And remove the import type { TableDefinition } line below (line 894).

    Context Used: Import patterns for the Sim application (source)

    Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

  2. apps/sim/hooks/queries/tables.ts, line 1504-1505 (link)

    P2 Duplicate type definition

    CsvHeaderMapping and CsvImportMode are already exported from @/lib/table (via csv-import.ts). Redefining them here creates two identical but independent types — a future change to one won't propagate to the other.

    Or just import them where needed from @/lib/table and remove these local declarations.

    Context Used: Import patterns for the Sim application (source)

  3. apps/sim/lib/copilot/request/tools/files.ts, line 136-142 (link)

    P2 Double unwrap in serializeOutputForFile

    serializeOutputForFile already calls unwrapFunctionExecuteOutput at the top and passes unwrapped to extractTabularData. But extractTabularData calls unwrapFunctionExecuteOutput again internally. The second unwrap is a no-op on an already-unwrapped value, but it's redundant and slightly confusing. Consider either having extractTabularData accept an already-unwrapped value (skip internal unwrapping when called from here) or passing output directly and letting extractTabularData own the unwrapping.

Reviews (1): Last reviewed commit: "feat(tables): import csv into existing t..." | Re-trigger Greptile

Comment thread apps/sim/lib/copilot/tools/server/table/user-table.ts
@icecrasher321
Copy link
Copy Markdown
Collaborator Author

bugbot run

Comment thread apps/sim/lib/copilot/request/tools/files.ts
@icecrasher321
Copy link
Copy Markdown
Collaborator Author

bugbot run

Copy link
Copy Markdown

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 1a45b8e. Configure here.

Comment thread apps/sim/app/api/table/import-csv/route.ts
@icecrasher321 icecrasher321 merged commit 1708bbe into staging Apr 16, 2026
14 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant