From 874ee698496b25b99e87bda9bba3653910cdfc6a Mon Sep 17 00:00:00 2001 From: Constantin Graf Date: Tue, 15 Oct 2024 12:34:55 +0200 Subject: [PATCH] Fixed escaping issues in importer --- .../Importers/ClockifyTimeEntriesImporter.php | 2 ++ .../Importers/TogglTimeEntriesImporter.php | 2 ++ .../clockify_time_entries_import_test_2.csv | 2 ++ .../toggl_time_entries_import_test_2.csv | 2 ++ .../ClockifyTimeEntriesImporterTest.php | 26 +++++++++++++++++++ .../TogglTimeEntriesImporterTest.php | 26 +++++++++++++++++++ 6 files changed, 60 insertions(+) create mode 100644 resources/testfiles/clockify_time_entries_import_test_2.csv create mode 100644 resources/testfiles/toggl_time_entries_import_test_2.csv diff --git a/app/Service/Import/Importers/ClockifyTimeEntriesImporter.php b/app/Service/Import/Importers/ClockifyTimeEntriesImporter.php index a35bb7a3..6d040d7a 100644 --- a/app/Service/Import/Importers/ClockifyTimeEntriesImporter.php +++ b/app/Service/Import/Importers/ClockifyTimeEntriesImporter.php @@ -47,6 +47,8 @@ public function importData(string $data, string $timezone): void $reader = Reader::createFromString($data); $reader->setHeaderOffset(0); $reader->setDelimiter(','); + $reader->setEnclosure('"'); + $reader->setEscape(''); $header = $reader->getHeader(); $this->validateHeader($header); $records = $reader->getRecords(); diff --git a/app/Service/Import/Importers/TogglTimeEntriesImporter.php b/app/Service/Import/Importers/TogglTimeEntriesImporter.php index a6efa6e4..e6ec1685 100644 --- a/app/Service/Import/Importers/TogglTimeEntriesImporter.php +++ b/app/Service/Import/Importers/TogglTimeEntriesImporter.php @@ -47,6 +47,8 @@ public function importData(string $data, string $timezone): void $reader = Reader::createFromString($data); $reader->setHeaderOffset(0); $reader->setDelimiter(','); + $reader->setEnclosure('"'); + $reader->setEscape(''); $header = $reader->getHeader(); $this->validateHeader($header); $records = $reader->getRecords(); diff --git a/resources/testfiles/clockify_time_entries_import_test_2.csv b/resources/testfiles/clockify_time_entries_import_test_2.csv new file mode 100644 index 00000000..4245299e --- /dev/null +++ b/resources/testfiles/clockify_time_entries_import_test_2.csv @@ -0,0 +1,2 @@ +"Project","Client","Description","Task","User","Group","Email","Tags","Type","Billable","Invoiced","Invoice ID","Start Date","Start Time","End Date","End Time","Duration (h)","Duration (decimal)","Billable Rate (EUR)","Billable Amount (EUR)","Date of creation" +"Real World Project","Real World Client","\\ 🔥 Special characters ''''''`!@#$%^&*()_+\-=\[\]{};':''\\|,.''<>\/?~ \\\","A giant task","Peter Tester","Group1, Group2","peter.test@email.test","","Regular","Yes","Yes","Invoice100","10/15/2024","11:00:00 AM","10/15/2024","11:30:00 AM","00:30:00","0.50","1000.00","500.00","10/15/2024" diff --git a/resources/testfiles/toggl_time_entries_import_test_2.csv b/resources/testfiles/toggl_time_entries_import_test_2.csv new file mode 100644 index 00000000..3ef6587b --- /dev/null +++ b/resources/testfiles/toggl_time_entries_import_test_2.csv @@ -0,0 +1,2 @@ +"User","Email","Client","Project","Task","Description","Billable","Start date","Start time","End date","End time","Duration","Tags" +"Peter Tester","peter.test@email.test","Real World Client","Real World Project","A giant task","\\ 🔥 Special characters """"""`!@#$%^&*()_+\-=\[\]{};':""\\|,.''<>\/?~ \\\","No","2024-10-15","12:02:17","2024-10-15","12:02:19","00:00:02","" diff --git a/tests/Unit/Service/Import/Importers/ClockifyTimeEntriesImporterTest.php b/tests/Unit/Service/Import/Importers/ClockifyTimeEntriesImporterTest.php index a9d523dd..91adadd3 100644 --- a/tests/Unit/Service/Import/Importers/ClockifyTimeEntriesImporterTest.php +++ b/tests/Unit/Service/Import/Importers/ClockifyTimeEntriesImporterTest.php @@ -5,6 +5,7 @@ namespace Tests\Unit\Service\Import\Importers; use App\Models\Organization; +use App\Models\TimeEntry; use App\Service\Import\Importers\ClockifyTimeEntriesImporter; use App\Service\Import\Importers\DefaultImporter; use App\Service\Import\Importers\ImportException; @@ -42,6 +43,31 @@ public function test_import_of_test_file_succeeds(): void $this->assertSame(1, $report->clientsCreated); } + public function test_import_of_test_with_special_characters_description_succeeds(): void + { + // Arrange + $organization = Organization::factory()->create(); + $timezone = 'Europe/Vienna'; + $importer = new ClockifyTimeEntriesImporter; + $importer->init($organization); + // Description: \\ 🔥 Special characters """`!@#$%^&*()_+\-=\[\]{};':"\\|,.''<>\/?~ \\\ + $data = Storage::disk('testfiles')->get('clockify_time_entries_import_test_2.csv'); + + // Act + $importer->importData($data, $timezone); + $report = $importer->getReport(); + + // Assert + $timeEntry = TimeEntry::first(); + $this->assertSame('\\\\ 🔥 Special characters \'\'\'\'\'\'`!@#$%^&*()_+\-=\[\]{};\':\'\'\\\\|,.\'\'<>\/?~ \\\\\\', $timeEntry->description); + $this->assertSame(1, $report->timeEntriesCreated); + $this->assertSame(0, $report->tagsCreated); + $this->assertSame(1, $report->tasksCreated); + $this->assertSame(1, $report->usersCreated); + $this->assertSame(1, $report->projectsCreated); + $this->assertSame(1, $report->clientsCreated); + } + public function test_import_of_test_file_twice_succeeds(): void { // Arrange diff --git a/tests/Unit/Service/Import/Importers/TogglTimeEntriesImporterTest.php b/tests/Unit/Service/Import/Importers/TogglTimeEntriesImporterTest.php index 65880133..c3a62be9 100644 --- a/tests/Unit/Service/Import/Importers/TogglTimeEntriesImporterTest.php +++ b/tests/Unit/Service/Import/Importers/TogglTimeEntriesImporterTest.php @@ -5,6 +5,7 @@ namespace Tests\Unit\Service\Import\Importers; use App\Models\Organization; +use App\Models\TimeEntry; use App\Service\Import\Importers\DefaultImporter; use App\Service\Import\Importers\ImportException; use App\Service\Import\Importers\TogglTimeEntriesImporter; @@ -47,6 +48,31 @@ public function test_import_of_test_file_succeeds(): void $this->assertSame(1, $report->clientsCreated); } + public function test_import_of_test_with_special_characters_description_succeeds(): void + { + // Arrange + $organization = Organization::factory()->create(); + $timezone = 'Europe/Vienna'; + $importer = new TogglTimeEntriesImporter; + $importer->init($organization); + // Description: \\ 🔥 Special characters """`!@#$%^&*()_+\-=\[\]{};':"\\|,.''<>\/?~ \\\ + $data = Storage::disk('testfiles')->get('toggl_time_entries_import_test_2.csv'); + + // Act + $importer->importData($data, $timezone); + $report = $importer->getReport(); + + // Assert + $timeEntry = TimeEntry::first(); + $this->assertSame('\\\\ 🔥 Special characters """`!@#$%^&*()_+\-=\[\]{};\':"\\\\|,.\'\'<>\/?~ \\\\\\', $timeEntry->description); + $this->assertSame(1, $report->timeEntriesCreated); + $this->assertSame(0, $report->tagsCreated); + $this->assertSame(1, $report->tasksCreated); + $this->assertSame(1, $report->usersCreated); + $this->assertSame(1, $report->projectsCreated); + $this->assertSame(1, $report->clientsCreated); + } + public function test_import_of_test_file_twice_succeeds(): void { // Arrange