Repository navigation
feat(import): import several files or a folder in one run - #3745
Conversation
J2TeamNNL
left a comment
There was a problem hiding this comment.
Automated code review: 10 findings.
| return nil | ||
| } | ||
|
|
||
| /// A folder holding a single file a row format reads opens the sheet for one file. Files no row format reads |
There was a problem hiding this comment.
presentRowImportBatch opens the batch sheet (or the one-file sheet for a single-file folder) without the eligibility check presentRowImport runs via rowImportRefusal. On a read-only connection, an unsupported connection type, or with a sheet already presented, the user configures the whole batch and is only refused at Import. Call rowImportRefusal for the resolved files' formats first.
| } | ||
|
|
||
| @MainActor | ||
| private func runFiles( |
There was a problem hiding this comment.
A failure from beginTransaction in runFiles, or from the final commit in endTransaction, is thrown without the failure history row or completion report that single-file importFile makes. If the last COMMIT fails after 20 files, the user gets a generic alert with no history entry or notification.
| } | ||
|
|
||
| /// A file's row is recorded only while its rows are still there, so a rolled-back run records the failure alone. | ||
| private func recordHistory(of report: RowImportBatchReport, in scope: DatabaseScope) async { |
There was a problem hiding this comment.
recordHistory skips .stopped outcomes. With wrapInTransaction off or Skip and Continue, pressing Stop in file 3 after 5,000 rows leaves those rows in the database (rowsKept is true) but case .imported, .stopped, .notRun: break records no history row for them.
| state.isImporting = false | ||
| currentProgress = nil | ||
| } | ||
| guard !isStopRequested else { |
There was a problem hiding this comment.
The early-return stopped report uses transaction .untracked, so the alert says earlier files were kept. RowImportTableMaker.prepare never checks the stop flag, so Stop during table creation creates every table, then reports 'Stopped at X. The files imported before it were kept.' although nothing was imported and empty tables remain.
| } | ||
|
|
||
| @MainActor | ||
| private func runFile( |
There was a problem hiding this comment.
Progress updates for the previous file can overwrite the next file's reset state. track() queues Task { @MainActor } from KVO callbacks; after file N finishes and observers are invalidated, queued Tasks can run after runFile(N+1) resets progress, writing file N's final counts over the reset.
| return nil | ||
| } | ||
|
|
||
| private func policyProblem(in included: [File]) -> String? { |
There was a problem hiding this comment.
A plugin whose rowImportConfiguration is nil (default for third-party plugins built before this requirement) makes policyProblem return '%@ files cannot be imported together with other files.' for any multi-file run, with no way to fix it in the sheet. A folder of those files cannot be imported at all although the panel's readsRows accepts them.
| Button("Cancel") { isPresented = false } | ||
| .keyboardShortcut(.cancelAction) | ||
| Button("Import") { performImport() } | ||
| .buttonStyle(.borderedProminent) |
There was a problem hiding this comment.
The Import button's disabled state reads importService?.state.isImporting, but importService is plain @State, so the view does not re-render when the service changes. While prepare creates tables isImporting is false, so a second click or Return starts a second performImport that replaces the service and races on the same tables. Add a re-entry guard.
| .frame(maxWidth: .infinity, alignment: .leading) | ||
| } | ||
|
|
||
| /// A dropped file or folder joins the run, read the same way as a folder chosen in the panel. |
There was a problem hiding this comment.
onDrop is not disabled during a run. A file dropped mid-import mutates batch.files and triggers reads and detectFields while the captured plan executes, and after a stopped or failed run the dropped file looks part of the run but is not in the report.
|
|
||
| var fieldDetectionSignature: String { settings.detectionSignature } | ||
|
|
||
| var rowImportConfiguration: RowImportRunner.Configuration? { runConfiguration } |
There was a problem hiding this comment.
rowImportConfiguration and runConfiguration are pasted identically into the CSV, JSON and XLSX plugins, where rowImportConfiguration just returns runConfiguration. A new setting forgotten in one copy silently desynchronises batch policy from a file's own import. Share one definition.
| @@ -0,0 +1,653 @@ | |||
| // | |||
There was a problem hiding this comment.
Added docblocks break the repo rule 'Code comments say why, never what' (AGENTS.md). Examples: /// What the sheet that edits one file handed back..., /// Files and folders dropped on the list join the run..., /// Tables alone, because they are the only objects.... Several narrate design history. Trim to why-only comments.
|
Fixes for the review findings are in J2TeamNNL@ed43d48. Fixed (10 of 10):
Skipped: none. Not verified: no local build, and swiftlint cannot load sourcekit here. The files parse with swiftc. The new alert string is not yet in the String Catalog. Cherry-pick: |
…-files # Conflicts: # apple/TablePro/Views/Main/MainContentView.swift
Summary
RowImportBatchSheetwith its modelRowImportBatch. Each file starts on the existing table its name matches, else a new table named as the one-file sheet names it, with no two files on the same new name.ImportFormatPlugin.rowImportConfiguration. Additive (defaults to nil); the ABI check passed and the kit version stays 40.ImportService.importFilesruns every file in one lease, one session scope and one transaction. Each file's writer joins it and sends no BEGIN, COMMIT or ROLLBACK of its own (ImportDataSinkAdapter(joinsTransaction:)).RowImportSheetintoRowImportTableMaker, which asks Safe Mode once for every CREATE TABLE and retry DELETE in the run. The row inserts reuse that grant.Not in this PR
Test plan
ImportServiceBatchTests: one transaction for every file, a later failure rolls back earlier files under Stop and Rollback, Stop and Commit, Skip and Continue, a run without a transaction, joining the user's transaction, Stop before the first file, history after a rollback.RowImportBatchTests: table matching and new-table names, twenty files into twenty new tables, duplicate names, mixed format options, Delete existing rows refusals, unreadable files, mapping by name, retries.ImportBatchFilesTests(folder contents and order) andRowImportConfigurationTests(CSV, JSON, XLSX report their options).ImportDataSinkAdapterRollbackTests: a joining sink sends no transaction statements.Fixes #2499