Skip to content

Commit 0707a0f

Browse files
claudearclaude
andcommitted
Downgrade missing required CSV column from error to warning
When importing a CSV that doesn't contain all required columns defined in the database schema, the validation was throwing an exception that halted the entire import. This changes the behavior to emit a warning instead, allowing the import to proceed with null values for missing columns. The database layer will handle any actual constraint violations on a per-row basis. Fixes: CLOUD-3K9V Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent bbdd8ef commit 0707a0f

2 files changed

Lines changed: 51 additions & 11 deletions

File tree

src/Migration/Sources/CSV.php

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -469,36 +469,33 @@ private function validateCSVHeaders(array $headers, array $columnTypes, array $r
469469
$internals = ['$id', '$permissions', '$createdAt', '$updatedAt'];
470470
$allKnown = \array_keys($columnTypes);
471471

472-
// Only validate that required columns are present
472+
$messages = [];
473+
474+
// Check for missing required columns — warn but don't fail
473475
$missingRequired = [];
474476
foreach (\array_keys($requiredColumns) as $requiredColumn) {
475477
if (!\in_array($requiredColumn, $headers)) {
476478
$missingRequired[] = $requiredColumn;
477479
}
478480
}
479481

480-
$messages = [];
481-
482-
// If there are missing required columns, throw an exception
483482
if (!empty($missingRequired)) {
484483
$label = \count($missingRequired) === 1 ? 'Missing required column' : 'Missing required columns';
485-
$messages[] = "$label: '" . \implode("', '", $missingRequired) . "'";
486-
}
487-
if (!empty($missingRequired)) {
488-
throw new \Exception('CSV header validation failed: ' . \implode('. ', $messages), Exception::CODE_VALIDATION);
484+
$messages[] = "$label: '" . \implode("', '", $missingRequired) . "' (rows will have null values)";
489485
}
490486

491-
// If there are unknown columns but no missing required columns, just log a warning
487+
// Check for unknown columns
492488
$unknown = \array_diff($headers, $allKnown, $internals);
493489
if (!empty($unknown)) {
494490
$label = \count($unknown) === 1 ? 'Unknown column' : 'Unknown columns';
495491
$messages[] = "$label: '" . \implode("', '", $unknown) . "' (will be ignored)";
496492
}
497-
if (!empty($unknown)) {
493+
494+
if (!empty($messages)) {
498495
$this->addWarning(new Warning(
499496
UtopiaResource::TYPE_ROW,
500497
Transfer::GROUP_DATABASES,
501-
\implode(', ', $messages),
498+
\implode('. ', $messages),
502499
$this->resourceId
503500
));
504501
}

tests/Migration/Unit/General/CSVTest.php

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -416,6 +416,49 @@ public function testCSVExportImportCompatibility()
416416
}
417417
}
418418

419+
/**
420+
* @throws \ReflectionException
421+
*/
422+
private function callValidateCSVHeaders(array $headers, array $columnTypes, array $requiredColumns): void
423+
{
424+
$reflection = new \ReflectionClass(CSV::class);
425+
$instance = $reflection->newInstanceWithoutConstructor();
426+
427+
$refProp = $reflection->getProperty('resourceId');
428+
$refProp->setAccessible(true);
429+
$refProp->setValue($instance, 'test_db:test_table');
430+
431+
$refMethod = $reflection->getMethod('validateCSVHeaders');
432+
$refMethod->setAccessible(true);
433+
434+
$refMethod->invoke($instance, $headers, $columnTypes, $requiredColumns);
435+
}
436+
437+
public function testValidateCSVHeadersMissingRequiredColumnDoesNotThrow(): void
438+
{
439+
// A CSV with only 'name' column, but 'texte' is required in the schema
440+
$headers = ['name'];
441+
$columnTypes = ['name' => 'string', 'texte' => 'string'];
442+
$requiredColumns = ['texte' => true];
443+
444+
// This should NOT throw — missing required columns should be a warning, not an error
445+
$this->callValidateCSVHeaders($headers, $columnTypes, $requiredColumns);
446+
447+
// If we reach here, the test passes (no exception thrown)
448+
$this->assertTrue(true);
449+
}
450+
451+
public function testValidateCSVHeadersAllRequiredPresent(): void
452+
{
453+
$headers = ['name', 'texte'];
454+
$columnTypes = ['name' => 'string', 'texte' => 'string'];
455+
$requiredColumns = ['texte' => true];
456+
457+
// Should not throw when all required columns are present
458+
$this->callValidateCSVHeaders($headers, $columnTypes, $requiredColumns);
459+
$this->assertTrue(true);
460+
}
461+
419462
private function recursiveDelete(string $dir): void
420463
{
421464
if (is_dir($dir)) {

0 commit comments

Comments
 (0)