Files
git-contribution-graph/src/Service/ProbeTrait.php
T
haylanandClaude-Bot 71bfb38028 feat(provider): add health-probe contract and supporting value objects
Add getName(), ping(), and probe() to ProviderInterface. Introduce
ProbeTrait to classify HTTP/transport exceptions into typed error codes,
ProviderStatus as the result value object, and ProviderStatusType /
ProviderErrorCode as backing enums.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-30 14:13:07 +02:00

64 lines
2.3 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Service;
use Symfony\Contracts\HttpClient\Exception\HttpExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
trait ProbeTrait
{
public function probe(): ProviderStatus
{
if (!$this->isConfigured()) {
return new ProviderStatus($this->getName(), ProviderStatusType::NotConfigured);
}
try {
$this->ping();
return new ProviderStatus($this->getName(), ProviderStatusType::Ok);
} catch (\Throwable $e) {
return $this->statusFromException($e);
}
}
private function statusFromException(\Throwable $e): ProviderStatus
{
[$error, $message] = $this->classifyException($e);
return new ProviderStatus($this->getName(), ProviderStatusType::Error, $error, $message);
}
/** @return array{ProviderErrorCode, string} */
private function classifyException(\Throwable $e): array
{
if ($e instanceof TransportExceptionInterface) {
return [ProviderErrorCode::UrlUnreachable, 'Could not reach the server: ' . $e->getMessage()];
}
if ($e instanceof HttpExceptionInterface) {
$code = $e->getResponse()->getStatusCode();
return match (true) {
$code === 401 => [ProviderErrorCode::AuthFailed, 'Invalid or expired token — verify your credentials'],
$code === 403 => [ProviderErrorCode::AuthFailed, 'Access denied — token lacks the required scopes'],
$code === 404 => [ProviderErrorCode::UrlUnreachable, 'Endpoint not found — check the configured URL'],
default => [ProviderErrorCode::Unknown, "HTTP {$code}: " . $e->getMessage()],
};
}
$msg = $e->getMessage();
$lower = strtolower($msg);
$error = match (true) {
str_contains($msg, 'not found') || str_contains($msg, 'Could not resolve') => ProviderErrorCode::UserNotFound,
str_contains($msg, 'GraphQL error')
&& (str_contains($lower, 'unauthorized') || str_contains($lower, 'bad credentials')) => ProviderErrorCode::AuthFailed,
default => ProviderErrorCode::Unknown,
};
return [$error, $msg];
}
}