diff --git a/app/Http/Controllers/Woo/IntegrationController.php b/app/Http/Controllers/Woo/IntegrationController.php index efe741a..6ab1213 100644 --- a/app/Http/Controllers/Woo/IntegrationController.php +++ b/app/Http/Controllers/Woo/IntegrationController.php @@ -36,6 +36,8 @@ class IntegrationController extends Controller return redirect() ->route('woo.integrations.index') - ->with('status', 'Integration status refreshed.'); + ->with('success', $store + ? 'Integration status refreshed.' + : 'Connect a store to refresh integration status.'); } } diff --git a/app/Services/Woo/PluginClient.php b/app/Services/Woo/PluginClient.php index 76057d4..397e6b9 100644 --- a/app/Services/Woo/PluginClient.php +++ b/app/Services/Woo/PluginClient.php @@ -3,6 +3,7 @@ namespace App\Services\Woo; use App\Models\WooStore; +use Illuminate\Http\Client\ConnectionException; use Illuminate\Http\Client\Response; use Illuminate\Http\UploadedFile; use Illuminate\Support\Facades\Http; @@ -30,7 +31,13 @@ class PluginClient public function delete(WooStore $store, string $path): bool { - return $this->raw($store, 'DELETE', $path)->successful(); + try { + return $this->raw($store, 'DELETE', $path)->successful(); + } catch (ConnectionException $e) { + $this->logConnectionFailure($store, 'DELETE', $path, $e); + + return false; + } } /** @return array|null */ @@ -39,15 +46,21 @@ class PluginClient $site = rtrim((string) $store->site_url, '/'); $url = $site.'/wp-json/ladill-woo/v1/media'; - $response = Http::acceptJson() - ->timeout(60) - ->withHeaders(['X-Ladill-Store' => $store->public_id]) - ->attach( - 'file', - file_get_contents($file->getRealPath()) ?: '', - $file->getClientOriginalName() ?: 'upload.jpg', - ) - ->post($url, array_filter(['alt' => $alt !== '' ? $alt : null])); + try { + $response = Http::acceptJson() + ->timeout(60) + ->withHeaders(['X-Ladill-Store' => $store->public_id]) + ->attach( + 'file', + file_get_contents($file->getRealPath()) ?: '', + $file->getClientOriginalName() ?: 'upload.jpg', + ) + ->post($url, array_filter(['alt' => $alt !== '' ? $alt : null])); + } catch (ConnectionException $e) { + $this->logConnectionFailure($store, 'POST', 'media', $e); + + return null; + } if (! $response->successful()) { Log::warning('Woo plugin media upload failed', [ @@ -67,7 +80,13 @@ class PluginClient /** @return array|null */ private function request(WooStore $store, string $method, string $path, array $data = []): ?array { - $response = $this->raw($store, $method, $path, $data); + try { + $response = $this->raw($store, $method, $path, $data); + } catch (ConnectionException $e) { + $this->logConnectionFailure($store, $method, $path, $e); + + return null; + } if (! $response->successful()) { Log::warning('Woo plugin API request failed', [ @@ -103,4 +122,14 @@ class PluginClient default => throw new \InvalidArgumentException("Unsupported method: {$method}"), }; } + + private function logConnectionFailure(WooStore $store, string $method, string $path, ConnectionException $e): void + { + Log::warning('Woo plugin API connection failed', [ + 'store_id' => $store->id, + 'method' => $method, + 'path' => $path, + 'message' => $e->getMessage(), + ]); + } } diff --git a/tests/Feature/WooIntegrationsTest.php b/tests/Feature/WooIntegrationsTest.php index 7af74db..d36163f 100644 --- a/tests/Feature/WooIntegrationsTest.php +++ b/tests/Feature/WooIntegrationsTest.php @@ -143,4 +143,31 @@ class WooIntegrationsTest extends TestCase Http::assertSentCount(2); $this->assertTrue(Cache::has('woo.integrations.'.$store->id)); } + + public function test_integrations_refresh_survives_store_connection_timeout(): void + { + $user = User::factory()->create(); + WooStore::create([ + 'user_id' => $user->id, + 'site_url' => 'https://shop.example.com', + 'site_name' => 'Example Shop', + 'status' => WooStore::STATUS_ACTIVE, + 'connected_at' => now(), + 'public_id' => 'store-public-id', + ]); + + Http::fake([ + 'https://shop.example.com/wp-json/ladill-woo/v1/integrations/status' => function () { + throw new \Illuminate\Http\Client\ConnectionException( + new \Exception('cURL error 28: Operation timed out') + ); + }, + ]); + + $this->actingAs($user) + ->from(route('woo.integrations.index')) + ->post(route('woo.integrations.refresh')) + ->assertRedirect(route('woo.integrations.index')) + ->assertSessionHas('success'); + } }