diff --git a/src/Audit/Adapter.php b/src/Audit/Adapter.php index 33f0d8b..131e2ba 100644 --- a/src/Audit/Adapter.php +++ b/src/Audit/Adapter.php @@ -239,4 +239,13 @@ abstract public function find(array $queries = []): array; * @throws \Exception */ abstract public function count(array $queries = [], ?int $max = null): int; + + /** + * Ping the adapter to check connectivity. + * + * Returns false on any connectivity failure rather than throwing. + * + * @return bool True when the backing store is reachable, false otherwise. + */ + abstract public function ping(): bool; } diff --git a/src/Audit/Adapter/ClickHouse.php b/src/Audit/Adapter/ClickHouse.php index d96327b..c0ee9b7 100644 --- a/src/Audit/Adapter/ClickHouse.php +++ b/src/Audit/Adapter/ClickHouse.php @@ -113,6 +113,29 @@ public function getName(): string return 'ClickHouse'; } + /** + * Ping ClickHouse to check connectivity. + * + * Uses ClickHouse's dedicated /ping endpoint, which bypasses the query + * pipeline, requires no database context, and is not recorded in query + * logs. Returns false on any connectivity failure rather than throwing. + * + * @return bool True when ClickHouse is reachable, false otherwise. + */ + public function ping(): bool + { + $scheme = $this->secure ? 'https' : 'http'; + $url = "{$scheme}://{$this->host}:{$this->port}/ping"; + + try { + $response = $this->client->fetch(url: $url, method: Client::METHOD_GET); + } catch (\Throwable) { + return false; + } + + return $response->getStatusCode() === 200; + } + /** * Validate host parameter. * diff --git a/src/Audit/Adapter/Database.php b/src/Audit/Adapter/Database.php index cda3383..2c0f780 100644 --- a/src/Audit/Adapter/Database.php +++ b/src/Audit/Adapter/Database.php @@ -34,6 +34,22 @@ public function getName(): string return 'Database'; } + /** + * Ping the database to check connectivity. + * + * Returns false on any connectivity failure rather than throwing. + * + * @return bool True when the database is reachable, false otherwise. + */ + public function ping(): bool + { + try { + return $this->db->ping(); + } catch (\Throwable) { + return false; + } + } + /** * Setup database structure. * diff --git a/src/Audit/Audit.php b/src/Audit/Audit.php index bd51560..0cd52c6 100644 --- a/src/Audit/Audit.php +++ b/src/Audit/Audit.php @@ -287,4 +287,16 @@ public function count(array $queries = [], ?int $max = null): int { return $this->adapter->count($queries, $max); } + + /** + * Ping the adapter to check connectivity. + * + * Returns false on any connectivity failure rather than throwing. + * + * @return bool True when the backing store is reachable, false otherwise. + */ + public function ping(): bool + { + return $this->adapter->ping(); + } } diff --git a/tests/Audit/AuditBase.php b/tests/Audit/AuditBase.php index 30531b1..4ba7c74 100644 --- a/tests/Audit/AuditBase.php +++ b/tests/Audit/AuditBase.php @@ -62,6 +62,11 @@ public function createLogs(): void $this->assertInstanceOf('Utopia\\Audit\\Log', $this->audit->log(null, 'insert', 'user/null', $userAgent, $ip, $dataWithAttributes)); } + public function testPing(): void + { + $this->assertTrue($this->audit->ping()); + } + public function testGetLogsByUser(): void { $logs = $this->audit->getLogsByUser('userId');