Skip to content

Commit dd0cc0d

Browse files
committed
Add more validation for clockify importer
1 parent 3a482c1 commit dd0cc0d

File tree

3 files changed

+60
-12
lines changed

3 files changed

+60
-12
lines changed

app/Service/Import/Importers/ClockifyTimeEntriesImporter.php

Lines changed: 37 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -124,34 +124,59 @@ public function importData(string $data, string $timezone): void
124124
$timeEntry->is_imported = true;
125125

126126
// Start
127+
$start = null;
127128
try {
128-
if (preg_match('/^[0-9]{1,2}:[0-9]{1,2} (AM|PM)$/', $record['Start Time']) === 1) {
129-
$start = Carbon::createFromFormat('m/d/Y h:i A', $record['Start Date'].' '.$record['Start Time'], $timezone);
130-
} else {
131-
$start = Carbon::createFromFormat('m/d/Y H:i:s A', $record['Start Date'].' '.$record['Start Time'], $timezone);
129+
$startDateStr = $record['Start Date'];
130+
$startTimeStr = $record['Start Time'];
131+
$startStr = $startDateStr.' '.$startTimeStr;
132+
$matches = [];
133+
$checkResult = preg_match('/^([0-9]{1,2})\/([0-9]{1,2})\/([0-9]{4}) ([0-9]{1,2}):([0-9]{1,2})(:[0-9]{1,2})? (AM|PM)$/', $startStr, $matches);
134+
135+
if ($checkResult === 1) {
136+
if ((int) $matches[1] > 12) {
137+
throw new ImportException('Start date ("'.$startDateStr.'") is invalid, please select the correct date format before exporting from Clockify');
138+
}
139+
if ($matches[6] === '') {
140+
$start = Carbon::createFromFormat('m/d/Y h:i A', $startStr, $timezone);
141+
} else {
142+
$start = Carbon::createFromFormat('m/d/Y H:i:s A', $startStr, $timezone);
143+
}
132144
}
133145
} catch (InvalidFormatException) {
134-
throw new ImportException('Start date ("'.$record['Start Date'].'") or time ("'.$record['Start Time'].'") are invalid');
146+
throw new ImportException('Start date ("'.$startDateStr.'") or time ("'.$startTimeStr.'") are invalid');
135147
}
136148
if ($start === null) {
137-
throw new ImportException('Start date ("'.$record['Start Date'].'") or time ("'.$record['Start Time'].'") are invalid');
149+
throw new ImportException('Start date ("'.$startDateStr.'") or time ("'.$startTimeStr.'") are invalid');
138150
}
139151
$timeEntry->start = $start->utc();
140152

141153
// End
154+
$end = null;
142155
try {
143-
if (preg_match('/^[0-9]{1,2}:[0-9]{1,2} (AM|PM)$/', $record['End Time']) === 1) {
144-
$end = Carbon::createFromFormat('m/d/Y h:i A', $record['End Date'].' '.$record['End Time'], $timezone);
145-
} else {
146-
$end = Carbon::createFromFormat('m/d/Y H:i:s A', $record['End Date'].' '.$record['End Time'], $timezone);
156+
$endDateStr = $record['End Date'];
157+
$endTimeStr = $record['End Time'];
158+
$endStr = $endDateStr.' '.$endTimeStr;
159+
$matches = [];
160+
$checkResult = preg_match('/^([0-9]{1,2})\/([0-9]{1,2})\/([0-9]{4}) ([0-9]{1,2}):([0-9]{1,2})(:[0-9]{1,2})? (AM|PM)$/', $endStr, $matches);
161+
162+
if ($checkResult === 1) {
163+
if ((int) $matches[1] > 12) {
164+
throw new ImportException('Start date ("'.$endDateStr.'") is invalid, please select the correct date format before exporting from Clockify');
165+
}
166+
if ($matches[6] === '') {
167+
$end = Carbon::createFromFormat('m/d/Y h:i A', $endStr, $timezone);
168+
} else {
169+
$end = Carbon::createFromFormat('m/d/Y H:i:s A', $endStr, $timezone);
170+
}
147171
}
148172
} catch (InvalidFormatException) {
149-
throw new ImportException('End date ("'.$record['End Date'].'") or time ("'.$record['End Time'].'") are invalid');
173+
throw new ImportException('End date ("'.$endDateStr.'") or time ("'.$endTimeStr.'") are invalid');
150174
}
151175
if ($end === null) {
152-
throw new ImportException('End date ("'.$record['End Date'].'") or time ("'.$record['End Time'].'") are invalid');
176+
throw new ImportException('End date ("'.$endDateStr.'") or time ("'.$endTimeStr.'") are invalid');
153177
}
154178
$timeEntry->end = $end->utc();
179+
155180
$timeEntry->billable_rate = $this->billableRateService->getBillableRateForTimeEntryWithGivenRelations(
156181
$timeEntry,
157182
$projectMember,
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
"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"
2+
"Real World Project","Real World Client","\\ 🔥 Special characters ''''''`!@#$%^&*()_+\-=\[\]{};':''\\|,.''<>\/?~ \\\","A giant task","Peter Tester","Group1, Group2","[email protected]","","Regular","Yes","Yes","Invoice100","13/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"

tests/Unit/Service/Import/Importers/ClockifyTimeEntriesImporterTest.php

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,4 +94,25 @@ public function test_import_of_test_file_twice_succeeds(): void
9494
$this->assertSame(0, $report->projectsCreated);
9595
$this->assertSame(0, $report->clientsCreated);
9696
}
97+
98+
public function test_import_fails_if_month_in_date_is_bigger_than_12(): void
99+
{
100+
// Arrange
101+
$organization = Organization::factory()->create();
102+
$timezone = 'Europe/Vienna';
103+
$importer = new ClockifyTimeEntriesImporter;
104+
$importer->init($organization);
105+
$data = Storage::disk('testfiles')->get('clockify_time_entries_import_test_3.csv');
106+
107+
// Act
108+
try {
109+
$importer->importData($data, $timezone);
110+
} catch (ImportException $e) {
111+
// Assert
112+
$this->assertSame('Start date ("13/15/2024") is invalid, please select the correct date format before exporting from Clockify', $e->getMessage());
113+
114+
return;
115+
}
116+
$this->fail();
117+
}
97118
}

0 commit comments

Comments
 (0)