Moved import test files; Added timezone argument to importers

This commit is contained in:
Constantin Graf
2024-05-29 15:40:38 +02:00
committed by Constantin Graf
parent 7af77c9357
commit b5a240cb2f
25 changed files with 62 additions and 38 deletions

View File

@@ -11,6 +11,7 @@ use App\Service\Import\Importers\ImporterProvider;
use App\Service\Import\Importers\ImportException; use App\Service\Import\Importers\ImportException;
use App\Service\Import\Importers\ReportDto; use App\Service\Import\Importers\ReportDto;
use App\Service\Import\ImportService; use App\Service\Import\ImportService;
use App\Service\TimezoneService;
use Brick\Money\ISOCurrencyProvider; use Brick\Money\ISOCurrencyProvider;
use Filament\Forms; use Filament\Forms;
use Filament\Forms\Components\Select; use Filament\Forms\Components\Select;
@@ -112,11 +113,14 @@ class OrganizationResource extends Resource
if ($file === null) { if ($file === null) {
throw new \Exception('File not found'); throw new \Exception('File not found');
} }
/** @var string $timezone */
$timezone = $data['timezone'];
/** @var ReportDto $report */ /** @var ReportDto $report */
$report = app(ImportService::class)->import( $report = app(ImportService::class)->import(
$record, $record,
$data['type'], $data['type'],
$file $file,
$timezone
); );
Notification::make() Notification::make()
->title('Import successful') ->title('Import successful')
@@ -156,6 +160,11 @@ class OrganizationResource extends Resource
return $select; return $select;
}), }),
Forms\Components\Select::make('timezone')
->label('Timezone')
->options(fn (): array => app(TimezoneService::class)->getSelectOptions())
->searchable()
->required(),
]), ]),
]) ])
->bulkActions([ ->bulkActions([

View File

@@ -67,10 +67,12 @@ class ImportController extends Controller
], 400); ], 400);
} }
$timezone = $this->user()->timezone;
$report = $importService->import( $report = $importService->import(
$organization, $organization,
$request->input('type'), $request->input('type'),
$importData $importData,
$timezone
); );
return new JsonResponse([ return new JsonResponse([

View File

@@ -19,7 +19,7 @@ class ImportService
/** /**
* @throws ImportException * @throws ImportException
*/ */
public function import(Organization $organization, string $importerType, string $data): ReportDto public function import(Organization $organization, string $importerType, string $data, string $timezone): ReportDto
{ {
/** @var ImporterContract $importer */ /** @var ImporterContract $importer */
$importer = app(ImporterProvider::class)->getImporter($importerType); $importer = app(ImporterProvider::class)->getImporter($importerType);
@@ -27,8 +27,8 @@ class ImportService
Storage::disk(config('filesystems.default')) Storage::disk(config('filesystems.default'))
->put('import/'.Carbon::now()->toDateString().'-'.$organization->getKey().'-'.Str::uuid(), $data); ->put('import/'.Carbon::now()->toDateString().'-'.$organization->getKey().'-'.Str::uuid(), $data);
DB::transaction(function () use (&$importer, &$data) { DB::transaction(function () use (&$importer, &$data, &$timezone) {
$importer->importData($data); $importer->importData($data, $timezone);
}); });
return $importer->getReport(); return $importer->getReport();

View File

@@ -15,7 +15,7 @@ class ClockifyProjectsImporter extends DefaultImporter
* @throws ImportException * @throws ImportException
*/ */
#[\Override] #[\Override]
public function importData(string $data): void public function importData(string $data, string $timezone): void
{ {
try { try {
$reader = Reader::createFromString($data); $reader = Reader::createFromString($data);

View File

@@ -40,7 +40,7 @@ class ClockifyTimeEntriesImporter extends DefaultImporter
* @throws ImportException * @throws ImportException
*/ */
#[\Override] #[\Override]
public function importData(string $data): void public function importData(string $data, string $timezone): void
{ {
try { try {
$reader = Reader::createFromString($data); $reader = Reader::createFromString($data);
@@ -106,9 +106,9 @@ class ClockifyTimeEntriesImporter extends DefaultImporter
// Start // Start
if (preg_match('/^[0-9]{1,2}:[0-9]{1,2} (AM|PM)$/', $record['Start Time']) === 1) { if (preg_match('/^[0-9]{1,2}:[0-9]{1,2} (AM|PM)$/', $record['Start Time']) === 1) {
$start = Carbon::createFromFormat('m/d/Y h:i A', $record['Start Date'].' '.$record['Start Time'], 'UTC'); $start = Carbon::createFromFormat('m/d/Y h:i A', $record['Start Date'].' '.$record['Start Time'], $timezone);
} else { } else {
$start = Carbon::createFromFormat('m/d/Y H:i:s A', $record['Start Date'].' '.$record['Start Time'], 'UTC'); $start = Carbon::createFromFormat('m/d/Y H:i:s A', $record['Start Date'].' '.$record['Start Time'], $timezone);
} }
if ($start === null) { if ($start === null) {
throw new ImportException('Start date ("'.$record['Start Date'].'") or time ("'.$record['Start Time'].'") are invalid'); throw new ImportException('Start date ("'.$record['Start Date'].'") or time ("'.$record['Start Time'].'") are invalid');
@@ -117,9 +117,9 @@ class ClockifyTimeEntriesImporter extends DefaultImporter
// End // End
if (preg_match('/^[0-9]{1,2}:[0-9]{1,2} (AM|PM)$/', $record['End Time']) === 1) { if (preg_match('/^[0-9]{1,2}:[0-9]{1,2} (AM|PM)$/', $record['End Time']) === 1) {
$end = Carbon::createFromFormat('m/d/Y h:i A', $record['End Date'].' '.$record['End Time'], 'UTC'); $end = Carbon::createFromFormat('m/d/Y h:i A', $record['End Date'].' '.$record['End Time'], $timezone);
} else { } else {
$end = Carbon::createFromFormat('m/d/Y H:i:s A', $record['End Date'].' '.$record['End Time'], 'UTC'); $end = Carbon::createFromFormat('m/d/Y H:i:s A', $record['End Date'].' '.$record['End Time'], $timezone);
} }
if ($end === null) { if ($end === null) {
throw new ImportException('End date ("'.$record['End Date'].'") or time ("'.$record['End Time'].'") are invalid'); throw new ImportException('End date ("'.$record['End Date'].'") or time ("'.$record['End Time'].'") are invalid');

View File

@@ -10,7 +10,7 @@ interface ImporterContract
{ {
public function init(Organization $organization): void; public function init(Organization $organization): void;
public function importData(string $data): void; public function importData(string $data, string $timezone): void;
public function getReport(): ReportDto; public function getReport(): ReportDto;

View File

@@ -16,7 +16,7 @@ class TogglDataImporter extends DefaultImporter
* @throws ImportException * @throws ImportException
*/ */
#[\Override] #[\Override]
public function importData(string $data): void public function importData(string $data, string $timezone): void
{ {
try { try {
$zip = new ZipArchive(); $zip = new ZipArchive();

View File

@@ -40,7 +40,7 @@ class TogglTimeEntriesImporter extends DefaultImporter
* @throws ImportException * @throws ImportException
*/ */
#[\Override] #[\Override]
public function importData(string $data): void public function importData(string $data, string $timezone): void
{ {
try { try {
$reader = Reader::createFromString($data); $reader = Reader::createFromString($data);
@@ -100,12 +100,12 @@ class TogglTimeEntriesImporter extends DefaultImporter
} }
$timeEntry->billable = $record['Billable'] === 'Yes'; $timeEntry->billable = $record['Billable'] === 'Yes';
$timeEntry->tags = $this->getTags($record['Tags']); $timeEntry->tags = $this->getTags($record['Tags']);
$start = Carbon::createFromFormat('Y-m-d H:i:s', $record['Start date'].' '.$record['Start time'], 'UTC'); $start = Carbon::createFromFormat('Y-m-d H:i:s', $record['Start date'].' '.$record['Start time'], $timezone);
if ($start === null) { if ($start === null) {
throw new ImportException('Start date ("'.$record['Start date'].'") or time ("'.$record['Start time'].'") are invalid'); throw new ImportException('Start date ("'.$record['Start date'].'") or time ("'.$record['Start time'].'") are invalid');
} }
$timeEntry->start = $start; $timeEntry->start = $start;
$end = Carbon::createFromFormat('Y-m-d H:i:s', $record['End date'].' '.$record['End time'], 'UTC'); $end = Carbon::createFromFormat('Y-m-d H:i:s', $record['End date'].' '.$record['End time'], $timezone);
if ($end === null) { if ($end === null) {
throw new ImportException('End date ("'.$record['End date'].'") or time ("'.$record['End time'].'") are invalid'); throw new ImportException('End date ("'.$record['End date'].'") or time ("'.$record['End time'].'") are invalid');
} }

View File

@@ -60,7 +60,7 @@ return [
'testfiles' => [ 'testfiles' => [
'driver' => 'local', 'driver' => 'local',
'root' => storage_path('tests'), 'root' => resource_path('testfiles'),
'throw' => true, 'throw' => true,
], ],

View File

@@ -19,11 +19,12 @@ class ImportServiceTest extends TestCase
// Arrange // Arrange
Storage::fake(config('filesystems.default')); Storage::fake(config('filesystems.default'));
$organization = Organization::factory()->create(); $organization = Organization::factory()->create();
$data = file_get_contents(storage_path('tests/toggl_time_entries_import_test_1.csv')); $timezone = 'Europe/Vienna';
$data = Storage::disk('testfiles')->get('toggl_time_entries_import_test_1.csv');
// Act // Act
$importService = app(ImportService::class); $importService = app(ImportService::class);
$report = $importService->import($organization, 'toggl_time_entries', $data); $report = $importService->import($organization, 'toggl_time_entries', $data, $timezone);
// Assert // Assert
$this->assertSame(2, $report->timeEntriesCreated); $this->assertSame(2, $report->timeEntriesCreated);

View File

@@ -6,6 +6,7 @@ namespace Tests\Unit\Service\Import\Importer;
use App\Models\Organization; use App\Models\Organization;
use App\Service\Import\Importers\ClockifyProjectsImporter; use App\Service\Import\Importers\ClockifyProjectsImporter;
use Illuminate\Support\Facades\Storage;
class ClockifyProjectsImporterTest extends ImporterTestAbstract class ClockifyProjectsImporterTest extends ImporterTestAbstract
{ {
@@ -13,12 +14,13 @@ class ClockifyProjectsImporterTest extends ImporterTestAbstract
{ {
// Arrange // Arrange
$organization = Organization::factory()->create(); $organization = Organization::factory()->create();
$timezone = 'Europe/Vienna';
$importer = new ClockifyProjectsImporter(); $importer = new ClockifyProjectsImporter();
$importer->init($organization); $importer->init($organization);
$data = file_get_contents(storage_path('tests/clockify_projects_import_test_1.csv')); $data = Storage::disk('testfiles')->get('clockify_projects_import_test_1.csv');
// Act // Act
$importer->importData($data); $importer->importData($data, $timezone);
// Assert // Assert
$this->checkTestScenarioProjectsOnlyAfterImport(); $this->checkTestScenarioProjectsOnlyAfterImport();
@@ -28,15 +30,16 @@ class ClockifyProjectsImporterTest extends ImporterTestAbstract
{ {
// Arrange // Arrange
$organization = Organization::factory()->create(); $organization = Organization::factory()->create();
$timezone = 'Europe/Vienna';
$importer = new ClockifyProjectsImporter(); $importer = new ClockifyProjectsImporter();
$importer->init($organization); $importer->init($organization);
$data = file_get_contents(storage_path('tests/clockify_projects_import_test_1.csv')); $data = Storage::disk('testfiles')->get('clockify_projects_import_test_1.csv');
$importer->importData($data); $importer->importData($data, $timezone);
$importer = new ClockifyProjectsImporter(); $importer = new ClockifyProjectsImporter();
$importer->init($organization); $importer->init($organization);
// Act // Act
$importer->importData($data); $importer->importData($data, $timezone);
// Assert // Assert
$this->checkTestScenarioProjectsOnlyAfterImport(); $this->checkTestScenarioProjectsOnlyAfterImport();

View File

@@ -7,6 +7,7 @@ namespace Tests\Unit\Service\Import\Importer;
use App\Models\Organization; use App\Models\Organization;
use App\Models\TimeEntry; use App\Models\TimeEntry;
use App\Service\Import\Importers\ClockifyTimeEntriesImporter; use App\Service\Import\Importers\ClockifyTimeEntriesImporter;
use Illuminate\Support\Facades\Storage;
class ClockifyTimeEntriesImporterTest extends ImporterTestAbstract class ClockifyTimeEntriesImporterTest extends ImporterTestAbstract
{ {
@@ -14,12 +15,13 @@ class ClockifyTimeEntriesImporterTest extends ImporterTestAbstract
{ {
// Arrange // Arrange
$organization = Organization::factory()->create(); $organization = Organization::factory()->create();
$timezone = 'Europe/Vienna';
$importer = new ClockifyTimeEntriesImporter(); $importer = new ClockifyTimeEntriesImporter();
$importer->init($organization); $importer->init($organization);
$data = file_get_contents(storage_path('tests/clockify_time_entries_import_test_1.csv')); $data = Storage::disk('testfiles')->get('clockify_time_entries_import_test_1.csv');
// Act // Act
$importer->importData($data); $importer->importData($data, $timezone);
// Assert // Assert
$testScenario = $this->checkTestScenarioAfterImportExcludingTimeEntries(); $testScenario = $this->checkTestScenarioAfterImportExcludingTimeEntries();
@@ -45,15 +47,16 @@ class ClockifyTimeEntriesImporterTest extends ImporterTestAbstract
{ {
// Arrange // Arrange
$organization = Organization::factory()->create(); $organization = Organization::factory()->create();
$timezone = 'Europe/Vienna';
$importer = new ClockifyTimeEntriesImporter(); $importer = new ClockifyTimeEntriesImporter();
$importer->init($organization); $importer->init($organization);
$data = file_get_contents(storage_path('tests/clockify_time_entries_import_test_1.csv')); $data = Storage::disk('testfiles')->get('clockify_time_entries_import_test_1.csv');
$importer->importData($data); $importer->importData($data, $timezone);
$importer = new ClockifyTimeEntriesImporter(); $importer = new ClockifyTimeEntriesImporter();
$importer->init($organization); $importer->init($organization);
// Act // Act
$importer->importData($data); $importer->importData($data, $timezone);
// Assert // Assert
$testScenario = $this->checkTestScenarioAfterImportExcludingTimeEntries(); $testScenario = $this->checkTestScenarioAfterImportExcludingTimeEntries();

View File

@@ -33,12 +33,13 @@ class TogglDataImporterTest extends ImporterTestAbstract
{ {
// Arrange // Arrange
$organization = Organization::factory()->create(); $organization = Organization::factory()->create();
$timezone = 'Europe/Vienna';
$importer = new TogglDataImporter(); $importer = new TogglDataImporter();
$importer->init($organization); $importer->init($organization);
// Act // Act
try { try {
$importer->importData('not a zip'); $importer->importData('not a zip', $timezone);
} catch (Exception $e) { } catch (Exception $e) {
$this->assertInstanceOf(ImportException::class, $e); $this->assertInstanceOf(ImportException::class, $e);
$this->assertSame('Invalid ZIP, error code: 19', $e->getMessage()); $this->assertSame('Invalid ZIP, error code: 19', $e->getMessage());
@@ -52,13 +53,14 @@ class TogglDataImporterTest extends ImporterTestAbstract
{ {
// Arrange // Arrange
$zipPath = $this->createTestZip('toggl_data_import_test_1'); $zipPath = $this->createTestZip('toggl_data_import_test_1');
$timezone = 'Europe/Vienna';
$organization = Organization::factory()->create(); $organization = Organization::factory()->create();
$importer = new TogglDataImporter(); $importer = new TogglDataImporter();
$importer->init($organization); $importer->init($organization);
$data = file_get_contents($zipPath); $data = file_get_contents($zipPath);
// Act // Act
$importer->importData($data); $importer->importData($data, $timezone);
$report = $importer->getReport(); $report = $importer->getReport();
// Assert // Assert
@@ -75,16 +77,17 @@ class TogglDataImporterTest extends ImporterTestAbstract
{ {
// Arrange // Arrange
$zipPath = $this->createTestZip('toggl_data_import_test_1'); $zipPath = $this->createTestZip('toggl_data_import_test_1');
$timezone = 'Europe/Vienna';
$organization = Organization::factory()->create(); $organization = Organization::factory()->create();
$importer = new TogglDataImporter(); $importer = new TogglDataImporter();
$importer->init($organization); $importer->init($organization);
$data = file_get_contents($zipPath); $data = file_get_contents($zipPath);
$importer->importData($data); $importer->importData($data, $timezone);
$importer = new TogglDataImporter(); $importer = new TogglDataImporter();
$importer->init($organization); $importer->init($organization);
// Act // Act
$importer->importData($data); $importer->importData($data, $timezone);
$report = $importer->getReport(); $report = $importer->getReport();
// Assert // Assert

View File

@@ -7,6 +7,7 @@ namespace Tests\Unit\Service\Import\Importer;
use App\Models\Organization; use App\Models\Organization;
use App\Models\TimeEntry; use App\Models\TimeEntry;
use App\Service\Import\Importers\TogglTimeEntriesImporter; use App\Service\Import\Importers\TogglTimeEntriesImporter;
use Illuminate\Support\Facades\Storage;
class TogglTimeEntriesImporterTest extends ImporterTestAbstract class TogglTimeEntriesImporterTest extends ImporterTestAbstract
{ {
@@ -14,12 +15,13 @@ class TogglTimeEntriesImporterTest extends ImporterTestAbstract
{ {
// Arrange // Arrange
$organization = Organization::factory()->create(); $organization = Organization::factory()->create();
$timezone = 'Europe/Vienna';
$importer = new TogglTimeEntriesImporter(); $importer = new TogglTimeEntriesImporter();
$importer->init($organization); $importer->init($organization);
$data = file_get_contents(storage_path('tests/toggl_time_entries_import_test_1.csv')); $data = Storage::disk('testfiles')->get('toggl_time_entries_import_test_1.csv');
// Act // Act
$importer->importData($data); $importer->importData($data, $timezone);
$report = $importer->getReport(); $report = $importer->getReport();
// Assert // Assert
@@ -52,15 +54,16 @@ class TogglTimeEntriesImporterTest extends ImporterTestAbstract
{ {
// Arrange // Arrange
$organization = Organization::factory()->create(); $organization = Organization::factory()->create();
$timezone = 'Europe/Vienna';
$importer = new TogglTimeEntriesImporter(); $importer = new TogglTimeEntriesImporter();
$importer->init($organization); $importer->init($organization);
$data = file_get_contents(storage_path('tests/toggl_time_entries_import_test_1.csv')); $data = Storage::disk('testfiles')->get('toggl_time_entries_import_test_1.csv');
$importer->importData($data, []); $importer->importData($data, $timezone);
$importer = new TogglTimeEntriesImporter(); $importer = new TogglTimeEntriesImporter();
$importer->init($organization); $importer->init($organization);
// Act // Act
$importer->importData($data); $importer->importData($data, $timezone);
$report = $importer->getReport(); $report = $importer->getReport();
// Assert // Assert