diff --git a/app/Service/Import/ImportService.php b/app/Service/Import/ImportService.php index c8acf866..c13f3cd0 100644 --- a/app/Service/Import/ImportService.php +++ b/app/Service/Import/ImportService.php @@ -10,6 +10,7 @@ use App\Service\Import\Importers\ImporterProvider; use App\Service\Import\Importers\ImportException; use App\Service\Import\Importers\ReportDto; use Illuminate\Support\Carbon; +use Illuminate\Support\Facades\Cache; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Storage; use Illuminate\Support\Str; @@ -27,9 +28,16 @@ class ImportService Storage::disk(config('filesystems.default')) ->put('import/'.Carbon::now()->toDateString().'-'.$organization->getKey().'-'.Str::uuid(), $data); - DB::transaction(function () use (&$importer, &$data, &$timezone) { - $importer->importData($data, $timezone); - }); + $lock = Cache::lock('import:'.$organization->getKey(), config('octane.max_execution_time', 60) + 1); + + if ($lock->get()) { + DB::transaction(function () use (&$importer, &$data, &$timezone) { + $importer->importData($data, $timezone); + }); + $lock->release(); + } else { + throw new ImportException('Import is already in progress'); + } return $importer->getReport(); } diff --git a/config/octane.php b/config/octane.php index 06cf8206..f3e7e3f4 100644 --- a/config/octane.php +++ b/config/octane.php @@ -221,7 +221,7 @@ return [ | */ - 'max_execution_time' => 45, + 'max_execution_time' => 120, /** * Custom swoole config diff --git a/docker-compose.yml b/docker-compose.yml index ffb3efd3..cd0a4276 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -29,6 +29,7 @@ services: - "host.docker.internal:host-gateway" - "storage.${NGINX_HOST_NAME}:${REVERSE_PROXY_IP:-10.100.100.10}" environment: + SUPERVISOR_PHP_COMMAND: "/usr/bin/php -d variables_order=EGPCS /var/www/html/artisan octane:start --server=frankenphp --host=0.0.0.0 --admin-port=2019 --port=80" XDG_CONFIG_HOME: /var/www/html/config XDG_DATA_HOME: /var/www/html/data WWWUSER: '${WWWUSER}' diff --git a/tests/Unit/Service/Import/ImportServiceTest.php b/tests/Unit/Service/Import/ImportServiceTest.php index 233d31a6..c5631392 100644 --- a/tests/Unit/Service/Import/ImportServiceTest.php +++ b/tests/Unit/Service/Import/ImportServiceTest.php @@ -6,8 +6,10 @@ namespace Tests\Unit\Service\Import; use App\Models\Organization; use App\Service\Import\Importers\ImporterProvider; +use App\Service\Import\Importers\ImportException; use App\Service\Import\ImportService; use Illuminate\Foundation\Testing\RefreshDatabase; +use Illuminate\Support\Facades\Cache; use Illuminate\Support\Facades\Storage; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\UsesClass; @@ -40,4 +42,23 @@ class ImportServiceTest extends TestCase $this->assertSame(2, $report->projectsCreated); $this->assertSame(1, $report->clientsCreated); } + + public function test_import_throws_exception_if_import_is_already_in_progress(): void + { + // Arrange + Storage::fake(config('filesystems.default')); + $organization = Organization::factory()->create(); + $timezone = 'Europe/Vienna'; + $data = Storage::disk('testfiles')->get('toggl_time_entries_import_test_1.csv'); + Cache::lock('import:'.$organization->getKey(), 10)->get(); + + // Act + $importService = app(ImportService::class); + try { + $importService->import($organization, 'toggl_time_entries', $data, $timezone); + } catch (ImportException $e) { + // Assert + $this->assertSame('Import is already in progress', $e->getMessage()); + } + } }