From 5666098ae692a7cddbe9366c35ff1a751e29cfb6 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Mon, 8 Dec 2025 23:47:30 +1300 Subject: [PATCH] Update validator --- .../Utopia/Database/Validator/Operation.php | 141 +++++++----------- 1 file changed, 58 insertions(+), 83 deletions(-) diff --git a/src/Appwrite/Utopia/Database/Validator/Operation.php b/src/Appwrite/Utopia/Database/Validator/Operation.php index e6884ac677..5650fe658c 100644 --- a/src/Appwrite/Utopia/Database/Validator/Operation.php +++ b/src/Appwrite/Utopia/Database/Validator/Operation.php @@ -2,6 +2,8 @@ namespace Appwrite\Utopia\Database\Validator; +use Appwrite\Utopia\Request\Model\Database\Legacy\TransactionOperation as TransactionOperationLegacy; +use Appwrite\Utopia\Request\Model\Database\TablesDB\TransactionOperation as TransactionOperationTablesDB; use Utopia\Validator; class Operation extends Validator @@ -9,10 +11,7 @@ class Operation extends Validator private string $description = ''; /** @var array */ - private array $required = [ - 'databaseId', - 'action', - ]; + private array $required = []; /** @var array */ private array $requiresDocumentId = [ @@ -39,38 +38,33 @@ class Operation extends Validator ]; /** @var array */ - private array $actions = [ - 'create' => true, - 'update' => true, - 'upsert' => true, - 'delete' => true, - 'increment' => true, - 'decrement' => true, - 'bulkCreate' => true, - 'bulkUpdate' => true, - 'bulkUpsert' => true, - 'bulkDelete' => true, - ]; + private array $actions = []; private string $collectionIdName = ''; private string $documentIdName = ''; public function __construct(private readonly string $type) { - switch ($this->type) { - case 'legacy': - $this->collectionIdName = 'collectionId'; - $this->documentIdName = 'documentId'; - break; - case 'tablesdb': - $this->collectionIdName = 'tableId'; - $this->documentIdName = 'rowId'; - break; - default: - throw new \InvalidArgumentException('Invalid type provided.'); + $model = match ($this->type) { + 'legacy' => new TransactionOperationLegacy(), + 'tablesdb' => new TransactionOperationTablesDB(), + default => throw new \InvalidArgumentException('Invalid type provided.'), + }; + + // Get required fields from the model + $this->required = $model->getRequired(); + + // Get valid actions from the model's action rule enum + $rules = $model->getRules(); + if (isset($rules['action']['enum'])) { + foreach ($rules['action']['enum'] as $action) { + $this->actions[$action] = true; + } } - $this->required[] = $this->collectionIdName; + // Set collection/document ID names based on model rules + $this->collectionIdName = isset($rules['tableId']) ? 'tableId' : 'collectionId'; + $this->documentIdName = isset($rules['rowId']) ? 'rowId' : 'documentId'; } public function getDescription(): string @@ -94,85 +88,70 @@ class Operation extends Validator } /** - * @param mixed $value + * @param TransactionOperationLegacy|TransactionOperationTablesDB $value */ public function isValid($value): bool { - // Must be array‑like - if (!\is_array($value)) { - $this->description = 'Value must be an array'; + $databaseId = $value->getDatabaseId(); + $collectionId = $value->getCollectionId(); + $documentId = $value->getDocumentId(); + $action = $value->getAction(); + $data = $value->getData(); + + // Validate databaseId is non-empty + if (!\is_string($databaseId) || \trim($databaseId) === '') { + $this->description = "Key 'databaseId' must be a non-empty string"; return false; } - // Mandatory keys - foreach ($this->required as $key) { - if (!\array_key_exists($key, $value)) { - $this->description = "Missing required key: {$key}"; - return false; - } + // Validate collectionId is non-empty + if (!\is_string($collectionId) || \trim($collectionId) === '') { + $this->description = "Key '{$this->collectionIdName}' must be a non-empty string"; + return false; } - // Required keys must be non‑empty - foreach ($this->required as $key) { - if (!\is_string($value[$key]) || \trim($value[$key]) === '') { - $this->description = "Key '{$key}' must be a non‑empty string"; - return false; - } + // Validate action is non-empty + if (!\is_string($action) || \trim($action) === '') { + $this->description = "Key 'action' must be a non-empty string"; + return false; } - // Validate action - if (!isset($this->actions[$value['action']])) { + // Validate action is valid + if (!isset($this->actions[$action])) { $this->description = "Key 'action' must be one of: " . \implode(', ', array_keys($this->actions)); return false; } - // If action requires documentId, it must be present - $actionRequiresDocumentId = ($this->requiresDocumentId[$value['action']] ?? false) === true; - if ($actionRequiresDocumentId && !\array_key_exists($this->documentIdName, $value)) { - $this->description = "Key '$this->documentIdName' is required for action '{$value['action']}'"; + // If action requires documentId, it must be present and non-empty + $actionRequiresDocumentId = ($this->requiresDocumentId[$action] ?? false) === true; + if ($actionRequiresDocumentId && ($documentId === null || \trim($documentId) === '')) { + $this->description = "Key '{$this->documentIdName}' is required for action '{$action}'"; return false; } - if (\array_key_exists($this->documentIdName, $value)) { - if (!\is_string($value[$this->documentIdName]) || \trim($value[$this->documentIdName]) === '') { - $this->description = "Key '$this->documentIdName' must be a non-empty string"; - return false; - } + // If documentId is provided, it must be non-empty + if ($documentId !== null && \trim($documentId) === '') { + $this->description = "Key '{$this->documentIdName}' must be a non-empty string"; + return false; } // Data validation - only required for certain actions - if (isset($this->requiresData[$value['action']]) && $this->requiresData[$value['action']]) { + if (isset($this->requiresData[$action]) && $this->requiresData[$action]) { // Data is required for this action - if (!\array_key_exists('data', $value)) { + if (empty($data)) { $this->description = "Missing required key: data"; return false; } - if (!\is_array($value['data'])) { - $this->description = "Key 'data' must be an array"; - return false; - } - } elseif (\array_key_exists('data', $value)) { - // Data is optional but if provided, must be an array - if (!\is_array($value['data'])) { - $this->description = "Key 'data' must be an array"; - return false; - } } // Bulk operation specific validations - $action = $value['action']; - // BulkUpdate and BulkDelete require queries if (\in_array($action, ['bulkUpdate', 'bulkDelete'])) { - if (!\array_key_exists('data', $value) || !\is_array($value['data'])) { - $this->description = "Key 'data' must be an array for {$action}"; - return false; - } - if (!\array_key_exists('queries', $value['data'])) { + if (!\array_key_exists('queries', $data)) { $this->description = "Key 'queries' is required in data for {$action}"; return false; } - if (!\is_array($value['data']['queries'])) { + if (!\is_array($data['queries'])) { $this->description = "Key 'queries' must be an array for {$action}"; return false; } @@ -180,11 +159,11 @@ class Operation extends Validator // BulkUpdate requires both queries and data if ($action === 'bulkUpdate') { - if (!\array_key_exists('data', $value['data'])) { + if (!\array_key_exists('data', $data)) { $this->description = "Key 'data' is required in data for {$action}"; return false; } - if (!\is_array($value['data']['data'])) { + if (!\is_array($data['data'])) { $this->description = "Key 'data.data' must be an array for {$action}"; return false; } @@ -192,18 +171,14 @@ class Operation extends Validator // Increment and Decrement require specific keys if (\in_array($action, ['increment', 'decrement'])) { - if (!\array_key_exists('data', $value) || !\is_array($value['data'])) { - $this->description = "Key 'data' must be an array for {$action}"; - return false; - } // Get the attribute key name based on type $attributeKey = $this->type === 'tablesdb' ? 'column' : 'attribute'; - if (!\array_key_exists($attributeKey, $value['data'])) { + if (!\array_key_exists($attributeKey, $data)) { $this->description = "Key '{$attributeKey}' is required in data for {$action}"; return false; } // Validate 'value' is numeric if provided (defaults to 1 if omitted) - if (\array_key_exists('value', $value['data']) && !\is_numeric($value['data']['value'])) { + if (\array_key_exists('value', $data) && !\is_numeric($data['value'])) { $this->description = "Key 'value' must be a numeric value for {$action}"; return false; }