From 8b724ed5c94447785d63dbd9f3467adaf079546b Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Mon, 2 Feb 2026 01:51:32 +0000 Subject: [PATCH 1/5] refactor: streamline log creation by utilizing createBatch and enhance ID handling --- src/Audit/Adapter/ClickHouse.php | 100 ++----------------------------- 1 file changed, 5 insertions(+), 95 deletions(-) diff --git a/src/Audit/Adapter/ClickHouse.php b/src/Audit/Adapter/ClickHouse.php index f77cf70..0ccd861 100644 --- a/src/Audit/Adapter/ClickHouse.php +++ b/src/Audit/Adapter/ClickHouse.php @@ -758,98 +758,8 @@ private function formatDateTime(\DateTime|string|null $dateTime): string */ public function create(array $log): Log { - $logId = uniqid('', true); - - // Format time - use provided time or current time - /** @var string|\DateTime|null $providedTime */ - $providedTime = $log['time'] ?? null; - $formattedTime = $this->formatDateTime($providedTime); - - $tableName = $this->getTableName(); - - // Extract additional attributes from the data array - /** @var array $logData */ - $logData = $log['data'] ?? []; - - // Build JSON row for JSONEachRow format - $row = [ - 'id' => $logId, - 'time' => $formattedTime, - ]; - - // Get all column names from attributes - $schemaColumns = $this->getColumnNames(); - - // Separate data for the data column (non-schema attributes) - $nonSchemaData = $logData; - - $resourceValue = $log['resource'] ?? null; - if (!\is_string($resourceValue)) { - $resourceValue = ''; - } - $resource = $this->parseResource($resourceValue); - - foreach ($schemaColumns as $columnName) { - if ($columnName === 'time') { - // Skip time - already handled above - continue; - } - - // Get attribute metadata to determine if required - $attributeMetadata = $this->getAttribute($columnName); - $isRequiredAttribute = $attributeMetadata !== null && isset($attributeMetadata['required']) && $attributeMetadata['required']; - - // For 'data' column, we'll handle it separately at the end - if ($columnName === 'data') { - continue; - } - - // Check if value exists in main log first, then in data array - $attributeValue = null; - $hasAttributeValue = false; - - if (isset($log[$columnName])) { - // Value is in main log (e.g., userId, event, resource, etc.) - $attributeValue = $log[$columnName]; - $hasAttributeValue = true; - } elseif (isset($logData[$columnName])) { - // Value is in data array (additional attributes) - $attributeValue = $logData[$columnName]; - $hasAttributeValue = true; - // Remove from non-schema data as it's now a dedicated column - unset($nonSchemaData[$columnName]); - } elseif (isset($resource[$columnName])) { - // Value is in parsed resource (e.g., resourceType, resourceId, resourceParent) - $attributeValue = $resource[$columnName]; - $hasAttributeValue = true; - } - - // Validate required attributes - if ($isRequiredAttribute && !$hasAttributeValue) { - throw new \InvalidArgumentException("Required attribute '{$columnName}' is missing in log entry"); - } - - if ($hasAttributeValue) { - $row[$columnName] = $attributeValue; - } - } - - // Add the data column with remaining non-schema attributes - try { - $encodedData = json_encode($nonSchemaData, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_THROW_ON_ERROR); - } catch (\JsonException $e) { - throw new Exception('Failed to encode data column to JSON: ' . $e->getMessage()); - } - $row['data'] = $encodedData; - - if ($this->sharedTables) { - $row['tenant'] = $this->tenant; - } - - $escapedDatabaseAndTable = $this->escapeIdentifier($this->database) . '.' . $this->escapeIdentifier($tableName); - $insertSql = "INSERT INTO {$escapedDatabaseAndTable} FORMAT JSONEachRow"; - - $this->query($insertSql, [], [$row]); + // Use createBatch for the actual insertion + $this->createBatch([$log]); // Retrieve the created log using getById to ensure consistency $createdLog = $this->getById($logId); @@ -1186,8 +1096,8 @@ public function createBatch(array $logs): bool } } - // Build JSON row - $logId = uniqid('', true); + // Build JSON row - use provided id or generate one + $logId = $log['id'] ?? uniqid('', true); /** @var string|\DateTime|null $providedTime */ $providedTime = $processedLog['time'] ?? null; @@ -1224,7 +1134,7 @@ public function createBatch(array $logs): bool } if ($this->sharedTables) { - $row['tenant'] = $this->tenant; + $row['tenant'] = $log['$tenant'] ?? $this->tenant; } $rows[] = $row; From e275b89ecfde1f56190b32cc22a1220790861d09 Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Mon, 2 Feb 2026 02:05:35 +0000 Subject: [PATCH 2/5] feat: generate unique ID for logs if not provided in create method --- src/Audit/Adapter/ClickHouse.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Audit/Adapter/ClickHouse.php b/src/Audit/Adapter/ClickHouse.php index 0ccd861..f6d4862 100644 --- a/src/Audit/Adapter/ClickHouse.php +++ b/src/Audit/Adapter/ClickHouse.php @@ -758,6 +758,10 @@ private function formatDateTime(\DateTime|string|null $dateTime): string */ public function create(array $log): Log { + // Generate ID if not provided + $logId = $log['id'] ?? uniqid('', true); + $log['id'] = $logId; + // Use createBatch for the actual insertion $this->createBatch([$log]); From 324e51e309d02b2b273dd95caba1095860b64a58 Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Mon, 2 Feb 2026 02:06:41 +0000 Subject: [PATCH 3/5] refactor: simplify log entry method descriptions for clarity --- src/Audit/Adapter/ClickHouse.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Audit/Adapter/ClickHouse.php b/src/Audit/Adapter/ClickHouse.php index f6d4862..8ecdce3 100644 --- a/src/Audit/Adapter/ClickHouse.php +++ b/src/Audit/Adapter/ClickHouse.php @@ -751,7 +751,7 @@ private function formatDateTime(\DateTime|string|null $dateTime): string } /** - * Create an audit log entry using JSONEachRow format for optimal performance. + * Create an audit log entry * * @param array $log The log data * @throws Exception @@ -803,7 +803,7 @@ public function getById(string $id): ?Log } /** - * Find logs using Query objects with JSON format for reliable parsing. + * Find logs using Query objects. * * @param array $queries * @return array @@ -1048,7 +1048,7 @@ private function parseQueries(array $queries): array } /** - * Create multiple audit log entries in batch using JSONEachRow format for optimal performance. + * Create multiple audit log entries in batch. * * @param array> $logs The logs to insert * @throws Exception From 8eebe46dc63310dd20acb89bcb9e8997f4292a3a Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Mon, 2 Feb 2026 02:08:12 +0000 Subject: [PATCH 4/5] feat: add assertion for log ID type in ClickHouse adapter --- src/Audit/Adapter/ClickHouse.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Audit/Adapter/ClickHouse.php b/src/Audit/Adapter/ClickHouse.php index 8ecdce3..1466e38 100644 --- a/src/Audit/Adapter/ClickHouse.php +++ b/src/Audit/Adapter/ClickHouse.php @@ -760,6 +760,7 @@ public function create(array $log): Log { // Generate ID if not provided $logId = $log['id'] ?? uniqid('', true); + assert(is_string($logId)); $log['id'] = $logId; // Use createBatch for the actual insertion From c3df1d05d15c63d7823c1c62ff7dd2f106bd27e4 Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Mon, 2 Feb 2026 09:48:20 +0000 Subject: [PATCH 5/5] feat: enforce string type for log ID in ClickHouse adapter --- src/Audit/Adapter/ClickHouse.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Audit/Adapter/ClickHouse.php b/src/Audit/Adapter/ClickHouse.php index 1466e38..0d598d5 100644 --- a/src/Audit/Adapter/ClickHouse.php +++ b/src/Audit/Adapter/ClickHouse.php @@ -760,7 +760,9 @@ public function create(array $log): Log { // Generate ID if not provided $logId = $log['id'] ?? uniqid('', true); - assert(is_string($logId)); + if (!is_string($logId)) { + throw new Exception('Log ID must be a string'); + } $log['id'] = $logId; // Use createBatch for the actual insertion