Skip to content

feat(migrate): import users from a CSV export - #214

Merged
Bccorb merged 3 commits into
mainfrom
feat/migrate-csv
Oct 5, 2026
Merged

Bccorb merged 3 commits into
mainfrom
feat/migrate-csv

Conversation

@Bccorb

@Bccorb Bccorb commented Oct 5, 2026 •

Copy link
Copy Markdown
Contributor

Part of #208 and fells-code/seamless-auth-api#334. Entra ID follows in #213.

Adds @seamless-auth/types ^0.24.0 as a dependency. Merge after fells-code/seamless-auth-api#340 is released: on an auth server without POST /admin/users/import, the command says the instance does not support user import.

What it adds

seamless migrate csv users.csv                 # dry run, writes users.migrate-plan.{csv,json}
seamless migrate csv users.csv --apply         # import, writes users.migrate-result.{csv,json}
seamless migrate csv export.csv --map hr.json --source town-hr --apply
  • Columns found by header (email, externalId/id/employee id, phone/mobile, roles, organizations/department), or named in a --map file kept beside the export along with default roles, organization roles and the separator
  • Local validation of every row with the shared UserImportRowSchema, so a bad email is reported as invalid with its spreadsheet row and never costs the rest of a batch
  • Batches of 200 (USER_IMPORT_MAX_ROWS), results mapped back to CSV rows. If a later batch fails, the outcomes of the batches that finished are still written to the report
  • Report as CSV and JSON, one line per row: created, updated, unchanged, rejected (with the server's reason) or invalid. CSV cells that a spreadsheet would run as a formula are neutralized, since the values come from whatever system exported them
  • Exit 1 when any row is rejected or invalid, for scripting
  • RFC 4180 CSV parser with no new dependency (quoted commas, doubled quotes, embedded newlines, CRLF, byte order mark)
  • README section and seamless help migrate

Starts on cli#144: this is the first command to use @seamless-auth/types instead of Record<string, unknown>.

Verification

  • tsc --noEmit, npm run build, and the suite: 1054 passed, 4 skipped after merging current main (25 new tests). Coverage 99.2% lines
  • End to end against feat(admin): bulk user import for migrations seamless-auth-api#340 on a scratch Postgres, with an isolated CLI config, using a five-row HR export (valid with an org and role, valid, an admin role, a bad email, an unknown department):
    • dry run: 2 would be created, 2 rejected, 1 invalid, nothing written
    • --apply: the same counts, both users unverified with their roles, external ids and memberships, plus the per-user and per-batch audit events
    • re-run: all unchanged
    • an imported user then registered with their email and landed on the imported account (same id, imported role), now verified

Bccorb added 3 commits October 5, 2026 13:16
Adds seamless migrate csv, which maps CSV columns onto the user import
contract, validates rows with @seamless-auth/types, sends them in batches
of 200 and writes a report of every row's outcome. Dry run by default.
@Bccorb
Bccorb marked this pull request as ready for review October 5, 2026 23:59
@Bccorb
Bccorb merged commit 0622194 into main Oct 5, 2026
3 checks passed
@Bccorb
Bccorb deleted the feat/migrate-csv branch October 6, 2026 00:00
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