diff --git a/composer.json b/composer.json index 4e80efba9..c28e846a0 100755 --- a/composer.json +++ b/composer.json @@ -37,8 +37,6 @@ "utopia-php/mongo": "0.2.*" }, "require-dev": { - "ext-redis": "*", - "ext-mongodb": "*", "fakerphp/faker": "^1.14", "phpunit/phpunit": "^9.4", "pcov/clobber": "^2.0", diff --git a/composer.lock b/composer.lock index 877e99f97..c833faf44 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "39b989623bbd7307d93b354478d0af2a", + "content-hash": "edeac562417fec69a2f2c46f44fc6ab4", "packages": [ { "name": "composer/package-versions-deprecated", @@ -906,16 +906,16 @@ }, { "name": "phpstan/phpstan", - "version": "1.10.11", + "version": "1.10.14", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "8aa62e6ea8b58ffb650e02940e55a788cbc3fe21" + "reference": "d232901b09e67538e5c86a724be841bea5768a7c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/8aa62e6ea8b58ffb650e02940e55a788cbc3fe21", - "reference": "8aa62e6ea8b58ffb650e02940e55a788cbc3fe21", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/d232901b09e67538e5c86a724be841bea5768a7c", + "reference": "d232901b09e67538e5c86a724be841bea5768a7c", "shasum": "" }, "require": { @@ -964,7 +964,7 @@ "type": "tidelift" } ], - "time": "2023-04-04T19:17:42+00:00" + "time": "2023-04-19T13:47:27+00:00" }, { "name": "phpunit/php-code-coverage", @@ -1286,16 +1286,16 @@ }, { "name": "phpunit/phpunit", - "version": "9.6.6", + "version": "9.6.7", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "b65d59a059d3004a040c16a82e07bbdf6cfdd115" + "reference": "c993f0d3b0489ffc42ee2fe0bd645af1538a63b2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/b65d59a059d3004a040c16a82e07bbdf6cfdd115", - "reference": "b65d59a059d3004a040c16a82e07bbdf6cfdd115", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/c993f0d3b0489ffc42ee2fe0bd645af1538a63b2", + "reference": "c993f0d3b0489ffc42ee2fe0bd645af1538a63b2", "shasum": "" }, "require": { @@ -1369,7 +1369,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.6" + "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.7" }, "funding": [ { @@ -1385,7 +1385,7 @@ "type": "tidelift" } ], - "time": "2023-03-27T11:43:46+00:00" + "time": "2023-04-14T08:58:40+00:00" }, { "name": "psr/container", @@ -2672,9 +2672,6 @@ "ext-pdo": "*", "php": ">=8.0" }, - "platform-dev": { - "ext-redis": "*", - "ext-mongodb": "*" - }, - "plugin-api-version": "2.2.0" + "platform-dev": [], + "plugin-api-version": "2.3.0" } diff --git a/docs/add-new-adapter.md b/docs/add-new-adapter.md index 27aa42e54..4205cd9b8 100644 --- a/docs/add-new-adapter.md +++ b/docs/add-new-adapter.md @@ -31,7 +31,7 @@ Create your `NewDB.php` file in `src/Database/Adapter/` and extend the parent cl namespace Utopia\Database\Adapter; -use Exception; +use Utopia\Database\Exception; use Utopia\Database\Adapter; use Utopia\Database\Database; use Utopia\Database\Document; diff --git a/src/Database/Adapter.php b/src/Database/Adapter.php index 47ecad14c..d9e574851 100644 --- a/src/Database/Adapter.php +++ b/src/Database/Adapter.php @@ -3,6 +3,7 @@ namespace Utopia\Database; use Exception; +use Utopia\Database\Exception as DatabaseException; abstract class Adapter { @@ -66,7 +67,7 @@ public function resetDebug(): self public function setNamespace(string $namespace): bool { if (empty($namespace)) { - throw new Exception('Missing namespace'); + throw new DatabaseException('Missing namespace'); } $this->namespace = $this->filter($namespace); @@ -80,13 +81,13 @@ public function setNamespace(string $namespace): bool * Get namespace of current set scope * * @return string - * @throws Exception + * @throws DatabaseException * */ public function getNamespace(): string { if (empty($this->namespace)) { - throw new Exception('Missing namespace'); + throw new DatabaseException('Missing namespace'); } return $this->namespace; @@ -106,7 +107,7 @@ public function getNamespace(): string public function setDefaultDatabase(string $name, bool $reset = false): bool { if (empty($name) && $reset === false) { - throw new Exception('Missing database'); + throw new DatabaseException('Missing database'); } $this->defaultDatabase = ($reset) ? '' : $this->filter($name); @@ -126,7 +127,7 @@ public function setDefaultDatabase(string $name, bool $reset = false): bool public function getDefaultDatabase(): string { if (empty($this->defaultDatabase)) { - throw new Exception('Missing default database'); + throw new DatabaseException('Missing default database'); } return $this->defaultDatabase; @@ -589,7 +590,7 @@ public function filter(string $value): string $value = preg_replace("/[^A-Za-z0-9\_\-]/", '', $value); if (\is_null($value)) { - throw new Exception('Failed to filter key'); + throw new DatabaseException('Failed to filter key'); } return $value; diff --git a/src/Database/Adapter/MariaDB.php b/src/Database/Adapter/MariaDB.php index 6ea81b1c3..a676f9acb 100644 --- a/src/Database/Adapter/MariaDB.php +++ b/src/Database/Adapter/MariaDB.php @@ -5,6 +5,7 @@ use Exception; use PDO; use PDOException; +use Utopia\Database\Exception as DatabaseException; use Utopia\Database\Database; use Utopia\Database\Document; use Utopia\Database\Exception\Duplicate; @@ -297,7 +298,7 @@ public function createRelationship( case Database::RELATION_MANY_TO_MANY: return true; default: - throw new Exception('Invalid relationship type.'); + throw new DatabaseException('Invalid relationship type'); } return $this->getPDO() @@ -376,7 +377,7 @@ public function updateRelationship( } break; default: - throw new Exception('Invalid relationship type.'); + throw new DatabaseException('Invalid relationship type'); } if (empty($sql)) { @@ -442,7 +443,7 @@ public function deleteRelationship( $sql = "DROP TABLE {$junction}; DROP TABLE {$perms}"; break; default: - throw new Exception('Invalid relationship type.'); + throw new DatabaseException('Invalid relationship type'); } if (empty($sql)) { @@ -624,7 +625,7 @@ public function createDocument(string $collection, Document $document): Document } if (!$this->getPDO()->commit()) { - throw new Exception('Failed to commit transaction'); + throw new DatabaseException('Failed to commit transaction'); } return $document; @@ -810,7 +811,7 @@ public function updateDocument(string $collection, Document $document): Document } if (!$this->getPDO()->commit()) { - throw new Exception('Failed to commit transaction'); + throw new DatabaseException('Failed to commit transaction'); } return $document; @@ -841,7 +842,7 @@ public function increaseDocumentAttribute(string $collection, string $id, string $stmt->bindValue(':_uid', $id); $stmt->bindValue(':val', $value); - $stmt->execute() || throw new Exception('Failed to update Attribute'); + $stmt->execute() || throw new DatabaseException('Failed to update attribute'); return true; } @@ -867,15 +868,15 @@ public function deleteDocument(string $collection, string $id): bool $stmtPermissions->bindValue(':_uid', $id); try { - $stmt->execute() || throw new Exception('Failed to delete document'); - $stmtPermissions->execute() || throw new Exception('Failed to clean permissions'); + $stmt->execute() || throw new DatabaseException('Failed to delete document'); + $stmtPermissions->execute() || throw new DatabaseException('Failed to clean permissions'); } catch (\Throwable $th) { $this->getPDO()->rollBack(); - throw new Exception($th->getMessage()); + throw new DatabaseException($th->getMessage()); } if (!$this->getPDO()->commit()) { - throw new Exception('Failed to commit transaction'); + throw new DatabaseException('Failed to commit transaction'); } return true; @@ -1024,7 +1025,7 @@ public function find(string $collection, array $queries = [], ?int $limit = 25, }; if (is_null($cursor[$attribute] ?? null)) { - throw new Exception("Order attribute '{$attribute}' is empty."); + throw new DatabaseException("Order attribute '{$attribute}' is empty"); } $stmt->bindValue(':cursor', $cursor[$attribute], $this->getPDOType($cursor[$attribute])); } @@ -1305,7 +1306,7 @@ protected function getSQLType(string $type, int $size, bool $signed = true): str return 'DATETIME(3)'; default: - throw new Exception('Unknown Type'); + throw new DatabaseException('Unknown type: ' . $type . '. Must be one of ' . Database::VAR_STRING . ', ' . Database::VAR_INTEGER . ', ' . Database::VAR_FLOAT . ', ' . Database::VAR_BOOLEAN . ', ' . Database::VAR_DATETIME . ', ' . Database::VAR_RELATIONSHIP); } } @@ -1322,10 +1323,11 @@ protected function getSQLType(string $type, int $size, bool $signed = true): str protected function getSQLIndex(string $collection, string $id, string $type, array $attributes): string { $type = match ($type) { - Database::INDEX_KEY, Database::INDEX_ARRAY => 'INDEX', + Database::INDEX_KEY, + Database::INDEX_ARRAY => 'INDEX', Database::INDEX_UNIQUE => 'UNIQUE INDEX', Database::INDEX_FULLTEXT => 'FULLTEXT INDEX', - default => throw new Exception('Unknown Index Type:' . $type), + default => throw new DatabaseException('Unknown index type: ' . $type . '. Must be one of ' . Database::INDEX_KEY . ', ' . Database::INDEX_UNIQUE . ', ' . Database::INDEX_ARRAY . ', ' . Database::INDEX_FULLTEXT), }; return "CREATE {$type} `{$id}` ON {$this->getSQLTable($collection)} ( " . implode(', ', $attributes) . " )"; @@ -1344,7 +1346,7 @@ protected function getPDOType(mixed $value): int 'double', 'string' => PDO::PARAM_STR, 'integer', 'boolean' => PDO::PARAM_INT, 'NULL' => PDO::PARAM_NULL, - default => throw new Exception('Unknown PDO Type for ' . gettype($value)), + default => throw new DatabaseException('Unknown PDO Type for ' . \gettype($value)), }; } diff --git a/src/Database/Adapter/Mongo.php b/src/Database/Adapter/Mongo.php index 9ec415cb0..d44102edf 100644 --- a/src/Database/Adapter/Mongo.php +++ b/src/Database/Adapter/Mongo.php @@ -3,6 +3,7 @@ namespace Utopia\Database\Adapter; use Exception; + use MongoDB\BSON\ObjectId; use MongoDB\BSON\Regex; use MongoDB\BSON\UTCDateTime; @@ -12,6 +13,7 @@ use Utopia\Database\Database; use Utopia\Database\Exception\Timeout; use Utopia\Database\Exception\Duplicate; +use Utopia\Database\Exception as DatabaseException; use Utopia\Database\Validator\Authorization; use Utopia\Database\Query; use Utopia\Mongo\Exception as MongoException; @@ -161,7 +163,7 @@ public function createCollection(string $name, array $attributes = [], array $in try { $this->getClient()->createCollection($id); } catch (MongoException $e) { - throw $e; + throw new DatabaseException($e->getMessage(), $e->getCode(), $e); } $indexesCreated = $this->client->createIndexes($id, [ @@ -411,7 +413,7 @@ public function updateRelationship( } break; default: - throw new Exception('Invalid relationship type.'); + throw new DatabaseException('Invalid relationship type'); } return true; @@ -467,7 +469,7 @@ public function deleteRelationship( $this->getClient()->dropCollection($junction); break; default: - throw new Exception('Invalid relationship type.'); + throw new DatabaseException('Invalid relationship type'); } return true; @@ -860,7 +862,7 @@ public function find(string $collection, array $queries = [], ?int $limit = 25, $attribute = $orderAttributes[0]; if (is_null($cursor[$attribute] ?? null)) { - throw new Exception("Order attribute '{$attribute}' is empty."); + throw new DatabaseException("Order attribute '{$attribute}' is empty"); } $orderOperatorInternalId = Query::TYPE_GREATER; @@ -1266,35 +1268,22 @@ protected function buildFilters(array $queries): array */ protected function getQueryOperator(string $operator): string { - switch ($operator) { - case Query::TYPE_EQUAL: - return '$eq'; - case Query::TYPE_NOTEQUAL: - return '$ne'; - case Query::TYPE_LESSER: - return '$lt'; - case Query::TYPE_LESSEREQUAL: - return '$lte'; - case Query::TYPE_GREATER: - return '$gt'; - case Query::TYPE_GREATEREQUAL: - return '$gte'; - case Query::TYPE_CONTAINS: - return '$in'; - case Query::TYPE_SEARCH: - return '$search'; - case Query::TYPE_BETWEEN: - return 'between'; // this is not an operator will be replaced with $gte/$lte - case Query::TYPE_IS_NULL: - return '$eq'; - case Query::TYPE_IS_NOT_NULL: - return '$ne'; - case Query::TYPE_STARTS_WITH: - case Query::TYPE_ENDS_WITH: - return '$regex'; - default: - throw new Exception('Unknown Operator:' . $operator); - } + return match ($operator) { + Query::TYPE_EQUAL, + Query::TYPE_IS_NULL => '$eq', + Query::TYPE_NOT_EQUAL, + Query::TYPE_IS_NOT_NULL => '$ne', + Query::TYPE_LESSER => '$lt', + Query::TYPE_LESSER_EQUAL => '$lte', + Query::TYPE_GREATER => '$gt', + Query::TYPE_GREATER_EQUAL => '$gte', + Query::TYPE_CONTAINS => '$in', + Query::TYPE_SEARCH => '$search', + Query::TYPE_BETWEEN => 'between', + Query::TYPE_STARTS_WITH, + Query::TYPE_ENDS_WITH => '$regex', + default => throw new DatabaseException('Unknown operator:' . $operator . '. Must be one of ' . Query::TYPE_EQUAL . ', ' . Query::TYPE_NOT_EQUAL . ', ' . Query::TYPE_LESSER . ', ' . Query::TYPE_LESSER_EQUAL . ', ' . Query::TYPE_GREATER . ', ' . Query::TYPE_GREATER_EQUAL . ', ' . Query::TYPE_IS_NULL . ', ' . Query::TYPE_IS_NOT_NULL . ', ' . Query::TYPE_BETWEEN . ', ' . Query::TYPE_CONTAINS . ', ' . Query::TYPE_SEARCH . ', ' . Query::TYPE_SELECT), + }; } protected function getQueryValue(string $method, mixed $value): mixed @@ -1324,7 +1313,7 @@ protected function getOrder(string $order): int return match ($order) { Database::ORDER_ASC => 1, Database::ORDER_DESC => -1, - default => throw new Exception('Unknown sort order:' . $order), + default => throw new DatabaseException('Unknown sort order:' . $order . '. Must be one of ' . Database::ORDER_ASC . ', ' . Database::ORDER_DESC), }; } @@ -1553,12 +1542,12 @@ public function getSupportForCasting(): bool * Return set namespace. * * @return string - * @throws \Exception + * @throws Exception */ public function getNamespace(): string { if (empty($this->namespace)) { - throw new Exception('Missing namespace'); + throw new DatabaseException('Missing namespace'); } return $this->namespace; @@ -1570,12 +1559,12 @@ public function getNamespace(): string * @param string $name * @param bool $reset * @return bool - * @throws \Exception + * @throws Exception */ public function setDefaultDatabase(string $name, bool $reset = false): bool { if (empty($name) && $reset === false) { - throw new Exception('Missing database'); + throw new DatabaseException('Missing database'); } $this->defaultDatabase = ($reset) ? '' : $this->filter($name); @@ -1588,12 +1577,12 @@ public function setDefaultDatabase(string $name, bool $reset = false): bool * * @param string $namespace * @return bool - * @throws \Exception + * @throws Exception */ public function setNamespace(string $namespace): bool { if (empty($namespace)) { - throw new Exception('Missing namespace'); + throw new DatabaseException('Missing namespace'); } $this->namespace = $this->filter($namespace); diff --git a/src/Database/Adapter/MySQL.php b/src/Database/Adapter/MySQL.php index b8686f7b1..9394556c6 100644 --- a/src/Database/Adapter/MySQL.php +++ b/src/Database/Adapter/MySQL.php @@ -4,8 +4,8 @@ use Exception; use PDOException; -use Swoole\Database\PDOProxy; use Utopia\Database\Database; +use Utopia\Database\Exception as DatabaseException; use Utopia\Database\Exception\Timeout; class MySQL extends MariaDB @@ -47,7 +47,7 @@ protected function getSQLIndex(string $collection, string $id, string $type, arr break; default: - throw new Exception('Unknown Index Type:' . $type); + throw new DatabaseException('Unknown index type: ' . $type . '. Must be one of ' . Database::INDEX_KEY . ', ' . Database::INDEX_UNIQUE . ', ' . Database::INDEX_ARRAY . ', ' . Database::INDEX_FULLTEXT); } return 'CREATE '.$type.' `'.$id.'` ON `'.$this->getDefaultDatabase().'`.`'.$this->getNamespace().'_'.$collection.'` ( '.implode(', ', $attributes).' );'; diff --git a/src/Database/Adapter/Postgres.php b/src/Database/Adapter/Postgres.php index 7a9a20ebd..fb569c85f 100644 --- a/src/Database/Adapter/Postgres.php +++ b/src/Database/Adapter/Postgres.php @@ -8,6 +8,7 @@ use Throwable; use Utopia\Database\Database; use Utopia\Database\Document; +use Utopia\Database\Exception as DatabaseException; use Utopia\Database\Exception\Duplicate; use Utopia\Database\Exception\Timeout; use Utopia\Database\Query; @@ -135,11 +136,11 @@ public function createCollection(string $name, array $attributes = [], array $in } } catch (Exception $e) { $this->getPDO()->rollBack(); - throw new Exception('Failed to create collection: ' . $e->getMessage()); + throw new DatabaseException('Failed to create collection: ' . $e->getMessage()); } if (!$this->getPDO()->commit()) { - throw new Exception('Failed to commit transaction'); + throw new DatabaseException('Failed to commit transaction'); } // Update $this->getIndexCount when adding another default index @@ -309,7 +310,7 @@ public function createRelationship( case Database::RELATION_MANY_TO_MANY: return true; default: - throw new Exception('Invalid relationship type.'); + throw new DatabaseException('Invalid relationship type'); } return $this->getPDO() @@ -388,7 +389,7 @@ public function updateRelationship( } break; default: - throw new Exception('Invalid relationship type.'); + throw new DatabaseException('Invalid relationship type'); } if (empty($sql)) { @@ -451,7 +452,7 @@ public function deleteRelationship( $sql = "DROP TABLE {$junction}; DROP TABLE {$perms}"; break; default: - throw new Exception('Invalid relationship type.'); + throw new DatabaseException('Invalid relationship type'); } return $this->getPDO() @@ -596,7 +597,7 @@ public function createDocument(string $collection, Document $document): Document $bindKey = 'key_' . $attributeIndex; $attribute = $this->filter($attribute); - $value = (is_bool($value)) ? ($value == true ? "true" : "false") : $value; + $value = (is_bool($value)) ? ($value ? "true" : "false") : $value; $stmt->bindValue(':' . $bindKey, $value, $this->getPDOType($value)); $attributeIndex++; } @@ -636,7 +637,7 @@ public function createDocument(string $collection, Document $document): Document } if (!$this->getPDO()->commit()) { - throw new Exception('Failed to commit transaction'); + throw new DatabaseException('Failed to commit transaction'); } return $document; @@ -819,7 +820,7 @@ public function updateDocument(string $collection, Document $document): Document } if (!$this->getPDO()->commit()) { - throw new Exception('Failed to commit transaction'); + throw new DatabaseException('Failed to commit transaction'); } return $document; @@ -850,7 +851,7 @@ public function increaseDocumentAttribute(string $collection, string $id, string $stmt->bindValue(':_uid', $id); $stmt->bindValue(':val', $value); - $stmt->execute() || throw new Exception('Failed to update Attribute'); + $stmt->execute() || throw new DatabaseException('Failed to update attribute'); return true; } @@ -877,15 +878,15 @@ public function deleteDocument(string $collection, string $id): bool $stmtPermissions->bindValue(':_uid', $id); try { - $stmt->execute() || throw new Exception('Failed to delete document'); - $stmtPermissions->execute() || throw new Exception('Failed to clean permissions'); + $stmt->execute() || throw new DatabaseException('Failed to delete document'); + $stmtPermissions->execute() || throw new DatabaseException('Failed to clean permissions'); } catch (\Throwable $th) { $this->getPDO()->rollBack(); - throw new Exception($th->getMessage()); + throw new DatabaseException($th->getMessage()); } if (!$this->getPDO()->commit()) { - throw new Exception('Failed to commit transaction'); + throw new DatabaseException('Failed to commit transaction'); } return true; @@ -1031,7 +1032,7 @@ public function find(string $collection, array $queries = [], ?int $limit = 25, }; if (is_null($cursor[$attribute] ?? null)) { - throw new Exception("Order attribute '{$attribute}' is empty."); + throw new DatabaseException("Order attribute '{$attribute}' is empty."); } $stmt->bindValue(':cursor', $cursor[$attribute], $this->getPDOType($cursor[$attribute])); } @@ -1296,7 +1297,7 @@ protected function getSQLType(string $type, int $size, bool $signed = true): str return 'TIMESTAMP(3)'; default: - throw new Exception('Unknown Type: ' . $type); + throw new DatabaseException('Unknown Type: ' . $type); } } @@ -1314,9 +1315,11 @@ protected function getSQLType(string $type, int $size, bool $signed = true): str protected function getSQLIndex(string $collection, string $id, string $type, array $attributes): string { $type = match ($type) { - Database::INDEX_KEY, Database::INDEX_ARRAY, Database::INDEX_FULLTEXT => 'INDEX', + Database::INDEX_KEY, + Database::INDEX_ARRAY, + Database::INDEX_FULLTEXT => 'INDEX', Database::INDEX_UNIQUE => 'UNIQUE INDEX', - default => throw new Exception('Unknown Index Type:' . $type), + default => throw new DatabaseException('Unknown index type: ' . $type . '. Must be one of ' . Database::INDEX_KEY . ', ' . Database::INDEX_UNIQUE . ', ' . Database::INDEX_ARRAY . ', ' . Database::INDEX_FULLTEXT), }; return 'CREATE ' . $type . ' "' . $this->getNamespace() . '_' . $collection . '_' . $id . '" ON ' . $this->getSQLTable($collection) . ' ( ' . implode(', ', $attributes) . ' );'; @@ -1353,16 +1356,16 @@ protected function getSQLTable(string $name): string * @param mixed $value * * @return int - * @throws Exception + * @throws DatabaseException */ - protected function getPDOType($value): int + protected function getPDOType(mixed $value): int { - return match (gettype($value)) { + return match (\gettype($value)) { 'string', 'double' => PDO::PARAM_STR, 'boolean' => PDO::PARAM_BOOL, 'integer' => PDO::PARAM_INT, 'NULL' => PDO::PARAM_NULL, - default => throw new Exception('Unknown PDO Type for ' . gettype($value)), + default => throw new DatabaseException('Unknown PDO Type for ' . \gettype($value)), }; } diff --git a/src/Database/Adapter/SQL.php b/src/Database/Adapter/SQL.php index 3ccb41c8d..5b585b82b 100644 --- a/src/Database/Adapter/SQL.php +++ b/src/Database/Adapter/SQL.php @@ -8,6 +8,7 @@ use Utopia\Database\Adapter; use Utopia\Database\Database; use Utopia\Database\Document; +use Utopia\Database\Exception as DatabaseException; use Utopia\Database\Query; abstract class SQL extends Adapter @@ -358,7 +359,7 @@ public function getAttributeWidth(Document $collection): int $total += 19; // 2022-06-26 14:46:24 break; default: - throw new Exception('Unknown Type'); + throw new DatabaseException('Unknown type: ' . $attribute['type']); } } @@ -706,15 +707,15 @@ protected function getSQLOperator(string $method): string switch ($method) { case Query::TYPE_EQUAL: return '='; - case Query::TYPE_NOTEQUAL: + case Query::TYPE_NOT_EQUAL: return '!='; case Query::TYPE_LESSER: return '<'; - case Query::TYPE_LESSEREQUAL: + case Query::TYPE_LESSER_EQUAL: return '<='; case Query::TYPE_GREATER: return '>'; - case Query::TYPE_GREATEREQUAL: + case Query::TYPE_GREATER_EQUAL: return '>='; case Query::TYPE_IS_NULL: return 'IS NULL'; @@ -724,7 +725,7 @@ protected function getSQLOperator(string $method): string case Query::TYPE_ENDS_WITH: return 'LIKE'; default: - throw new Exception('Unknown method:' . $method); + throw new DatabaseException('Unknown method: ' . $method); } } @@ -738,7 +739,7 @@ protected function getSQLPlaceholder(Query $query): string $json = \json_encode([$query->getAttribute(), $query->getMethod(), $query->getValues()]); if ($json === false) { - throw new Exception('Failed to encode query'); + throw new DatabaseException('Failed to encode query'); } return \md5($json); @@ -782,7 +783,7 @@ protected function getSQLIndexType(string $type): string return 'FULLTEXT INDEX'; default: - throw new Exception('Unknown Index Type:' . $type); + throw new DatabaseException('Unknown index type: ' . $type . '. Must be one of ' . Database::INDEX_KEY . ', ' . Database::INDEX_UNIQUE . ', ' . Database::INDEX_ARRAY . ', ' . Database::INDEX_FULLTEXT); } } diff --git a/src/Database/Adapter/SQLite.php b/src/Database/Adapter/SQLite.php index 5aa7d04ee..397d4001f 100644 --- a/src/Database/Adapter/SQLite.php +++ b/src/Database/Adapter/SQLite.php @@ -3,11 +3,12 @@ namespace Utopia\Database\Adapter; use PDO; -use Exception; use PDOException; +use Exception; use Utopia\Database\Database; use Utopia\Database\Document; use Utopia\Database\Helpers\ID; +use Utopia\Database\Exception as DatabaseException; use Utopia\Database\Exception\Duplicate; /** @@ -219,7 +220,7 @@ public function deleteAttribute(string $collection, string $id, bool $array = fa $collection = $this->getDocument(Database::METADATA, $name); if ($collection->isEmpty()) { - throw new Exception('Collection not found'); + throw new DatabaseException('Collection not found'); } $indexes = \json_decode($collection->getAttribute('indexes', []), true); @@ -410,7 +411,7 @@ public function createDocument(string $collection, Document $document): Document } if (!$this->getPDO()->commit()) { - throw new Exception('Failed to commit transaction'); + throw new DatabaseException('Failed to commit transaction'); } return $document; @@ -593,7 +594,7 @@ public function updateDocument(string $collection, Document $document): Document } if (!$this->getPDO()->commit()) { - throw new Exception('Failed to commit transaction'); + throw new DatabaseException('Failed to commit transaction'); } return $document; @@ -662,7 +663,7 @@ protected function getSQLIndexType(string $type): string return 'UNIQUE INDEX'; default: - throw new Exception('Unknown Index Type:' . $type); + throw new DatabaseException('Unknown index type: ' . $type . '. Must be one of ' . Database::INDEX_KEY . ', ' . Database::INDEX_UNIQUE . ', ' . Database::INDEX_ARRAY . ', ' . Database::INDEX_FULLTEXT); } } @@ -693,7 +694,7 @@ protected function getSQLIndex(string $collection, string $id, string $type, arr break; default: - throw new Exception('Unknown Index Type:' . $type); + throw new DatabaseException('Unknown index type: ' . $type . '. Must be one of ' . Database::INDEX_KEY . ', ' . Database::INDEX_UNIQUE . ', ' . Database::INDEX_ARRAY . ', ' . Database::INDEX_FULLTEXT); } $attributes = \array_map(fn ($attribute) => match ($attribute) { diff --git a/src/Database/Database.php b/src/Database/Database.php index 0093663f3..986f263d5 100644 --- a/src/Database/Database.php +++ b/src/Database/Database.php @@ -4,8 +4,8 @@ use Exception; use InvalidArgumentException; -use Throwable; use Utopia\Cache\Cache; +use Utopia\Database\Exception as DatabaseException; use Utopia\Database\Exception\Authorization as AuthorizationException; use Utopia\Database\Exception\Conflict as ConflictException; use Utopia\Database\Exception\Duplicate as DuplicateException; @@ -484,7 +484,7 @@ public function setNamespace(string $namespace): self * * @return string * - * @throws Exception + * @throws DatabaseException */ public function getNamespace(): string { @@ -636,7 +636,7 @@ public function createCollection(string $id, array $attributes = [], array $inde $collection = $this->silent(fn () => $this->getCollection($id)); if (!$collection->isEmpty() && $id !== self::METADATA) { - throw new DuplicateException('Collection ' . $id . ' Exists!'); + throw new DuplicateException('Collection ' . $id . ' already exists'); } $this->adapter->createCollection($id, $attributes, $indexes); @@ -719,7 +719,7 @@ public function updateCollection(string $id, array $permissions, bool $documentS * @param string $id * * @return Document - * @throws Exception + * @throws DatabaseException */ public function getCollection(string $id): Document { @@ -802,17 +802,18 @@ public function deleteCollection(string $id): bool * * @return bool * @throws AuthorizationException + * @throws ConflictException + * @throws DatabaseException * @throws DuplicateException * @throws LimitException * @throws StructureException - * @throws Throwable */ public function createAttribute(string $collection, string $id, string $type, int $size, bool $required, mixed $default = null, bool $signed = true, bool $array = false, string $format = null, array $formatOptions = [], array $filters = []): bool { $collection = $this->silent(fn () => $this->getCollection($collection)); if ($collection->isEmpty()) { - throw new Exception('Collection not found'); + throw new DatabaseException('Collection not found'); } // attribute IDs are case insensitive @@ -827,7 +828,7 @@ public function createAttribute(string $collection, string $id, string $type, in /** Ensure required filters for the attribute are passed */ $requiredFilters = $this->getRequiredFilters($type); if (!empty(array_diff($requiredFilters, $filters))) { - throw new Exception("Attribute of type: $type requires the following filters: " . implode(",", $requiredFilters)); + throw new DatabaseException("Attribute of type: $type requires the following filters: " . implode(",", $requiredFilters)); } if ( @@ -839,7 +840,7 @@ public function createAttribute(string $collection, string $id, string $type, in if ($format) { if (!Structure::hasFormat($format, $type)) { - throw new Exception('Format ("' . $format . '") not available for this attribute type ("' . $type . '")'); + throw new DatabaseException('Format ("' . $format . '") not available for this attribute type ("' . $type . '")'); } } @@ -869,14 +870,14 @@ public function createAttribute(string $collection, string $id, string $type, in switch ($type) { case self::VAR_STRING: if ($size > $this->adapter->getLimitForString()) { - throw new Exception('Max size allowed for string is: ' . number_format($this->adapter->getLimitForString())); + throw new DatabaseException('Max size allowed for string is: ' . number_format($this->adapter->getLimitForString())); } break; case self::VAR_INTEGER: $limit = ($signed) ? $this->adapter->getLimitForInt() / 2 : $this->adapter->getLimitForInt(); if ($size > $limit) { - throw new Exception('Max size allowed for int is: ' . number_format($limit)); + throw new DatabaseException('Max size allowed for int is: ' . number_format($limit)); } break; case self::VAR_FLOAT: @@ -885,13 +886,13 @@ public function createAttribute(string $collection, string $id, string $type, in case self::VAR_RELATIONSHIP: break; default: - throw new Exception('Unknown attribute type: ' . $type); + throw new DatabaseException('Unknown attribute type: ' . $type . '. Must be one of ' . self::VAR_STRING . ', ' . self::VAR_INTEGER . ', ' . self::VAR_FLOAT . ', ' . self::VAR_BOOLEAN . ', ' . self::VAR_DATETIME . ', ' . self::VAR_RELATIONSHIP); } // only execute when $default is given if (!\is_null($default)) { if ($required === true) { - throw new Exception('Cannot set a default value on a required attribute'); + throw new DatabaseException('Cannot set a default value on a required attribute'); } $this->validateDefaultTypes($type, $default); @@ -900,7 +901,7 @@ public function createAttribute(string $collection, string $id, string $type, in $created = $this->adapter->createAttribute($collection->getId(), $id, $type, $size, $signed, $array); if (!$created) { - throw new Exception('Failed to create attribute'); + throw new DatabaseException('Failed to create attribute'); } if ($collection->getId() !== self::METADATA) { @@ -958,16 +959,16 @@ protected function validateDefaultTypes(string $type, mixed $default): void case self::VAR_FLOAT: case self::VAR_BOOLEAN: if ($type !== $defaultType) { - throw new Exception('Default value ' . $default . ' does not match given type ' . $type); + throw new DatabaseException('Default value ' . $default . ' does not match given type ' . $type); } break; case self::VAR_DATETIME: if ($defaultType !== self::VAR_STRING) { - throw new Exception('Default value ' . $default . ' does not match given type ' . $type); + throw new DatabaseException('Default value ' . $default . ' does not match given type ' . $type); } break; default: - throw new Exception('Unknown attribute type: ' . $type); + throw new DatabaseException('Unknown attribute type: ' . $type . '. Must be one of ' . self::VAR_STRING . ', ' . self::VAR_INTEGER . ', ' . self::VAR_FLOAT . ', ' . self::VAR_BOOLEAN . ', ' . self::VAR_DATETIME . ', ' . self::VAR_RELATIONSHIP); } } @@ -979,28 +980,28 @@ protected function validateDefaultTypes(string $type, mixed $default): void * @param callable $updateCallback method that receives document, and returns it with changes applied * * @return Document - * @throws Exception - * @throws Throwable + * @throws ConflictException + * @throws DatabaseException */ private function updateIndexMeta(string $collection, string $id, callable $updateCallback): Document { $collection = $this->silent(fn () => $this->getCollection($collection)); if ($collection->getId() === self::METADATA) { - throw new Exception('Can not update metadata attributes'); + throw new DatabaseException('Cannot update metadata attributes'); } $indexes = $collection->getAttribute('indexes', []); $index = \array_search($id, \array_map(fn ($index) => $index['$id'], $indexes)); if ($index === false) { - throw new Exception('Index not found'); + throw new DatabaseException('Index not found'); } // Execute update from callback $updateCallback($indexes[$index], $collection, $index); // Save - $collection->setAttribute('indexes', $indexes, Document::SET_TYPE_ASSIGN); + $collection->setAttribute('indexes', $indexes); $this->silent(fn () => $this->updateDocument(self::METADATA, $collection->getId(), $collection)); @@ -1017,21 +1018,21 @@ private function updateIndexMeta(string $collection, string $id, callable $updat * @param callable $updateCallback method that receives document, and returns it with changes applied * * @return Document - * @throws Exception - * @throws Throwable + * @throws ConflictException + * @throws DatabaseException */ private function updateAttributeMeta(string $collection, string $id, callable $updateCallback): Document { $collection = $this->silent(fn () => $this->getCollection($collection)); if ($collection->getId() === self::METADATA) { - throw new Exception('Can not update metadata attributes'); + throw new DatabaseException('Cannot update metadata attributes'); } $attributes = $collection->getAttribute('attributes', []); $index = \array_search($id, \array_map(fn ($attribute) => $attribute['$id'], $attributes)); if ($index === false) { - throw new Exception('Attribute not found'); + throw new DatabaseException('Attribute not found'); } // Execute update from callback @@ -1078,7 +1079,7 @@ public function updateAttributeFormat(string $collection, string $id, string $fo { return $this->updateAttributeMeta($collection, $id, function ($attribute) use ($format) { if (!Structure::hasFormat($format, $attribute->getAttribute('type'))) { - throw new Exception('Format ("' . $format . '") not available for this attribute type ("' . $attribute->getAttribute('type') . '")'); + throw new DatabaseException('Format "' . $format . '" not available for attribute type "' . $attribute->getAttribute('type') . '"'); } $attribute->setAttribute('format', $format); @@ -1133,7 +1134,7 @@ public function updateAttributeDefault(string $collection, string $id, mixed $de { return $this->updateAttributeMeta($collection, $id, function ($attribute) use ($default) { if ($attribute->getAttribute('required') === true) { - throw new Exception('Cannot set a default value on a required attribute'); + throw new DatabaseException('Cannot set a default value on a required attribute'); } $this->validateDefaultTypes($attribute->getAttribute('type'), $default); @@ -1183,46 +1184,46 @@ public function updateAttribute(string $collection, string $id, string $type = n switch ($type) { case self::VAR_STRING: if (empty($size)) { - throw new Exception('Size length is required'); + throw new DatabaseException('Size length is required'); } if ($size > $this->adapter->getLimitForString()) { - throw new Exception('Max size allowed for string is: ' . number_format($this->adapter->getLimitForString())); + throw new DatabaseException('Max size allowed for string is: ' . number_format($this->adapter->getLimitForString())); } break; case self::VAR_INTEGER: $limit = ($signed) ? $this->adapter->getLimitForInt() / 2 : $this->adapter->getLimitForInt(); if ($size > $limit) { - throw new Exception('Max size allowed for int is: ' . number_format($limit)); + throw new DatabaseException('Max size allowed for int is: ' . number_format($limit)); } break; case self::VAR_FLOAT: case self::VAR_BOOLEAN: case self::VAR_DATETIME: if (!empty($size)) { - throw new Exception('Size must be empty'); + throw new DatabaseException('Size must be empty'); } break; default: - throw new Exception('Unknown attribute type: ' . $type); + throw new DatabaseException('Unknown attribute type: ' . $type . '. Must be one of ' . self::VAR_STRING . ', ' . self::VAR_INTEGER . ', ' . self::VAR_FLOAT . ', ' . self::VAR_BOOLEAN . ', ' . self::VAR_DATETIME . ', ' . self::VAR_RELATIONSHIP); } /** Ensure required filters for the attribute are passed */ $requiredFilters = $this->getRequiredFilters($type); if (!empty(array_diff($requiredFilters, $filters))) { - throw new Exception("Attribute of type: $type requires the following filters: " . implode(",", $requiredFilters)); + throw new DatabaseException("Attribute of type: $type requires the following filters: " . implode(",", $requiredFilters)); } if ($format) { if (!Structure::hasFormat($format, $type)) { - throw new Exception('Format ("' . $format . '") not available for this attribute type ("' . $type . '")'); + throw new DatabaseException('Format ("' . $format . '") not available for this attribute type ("' . $type . '")'); } } if (!\is_null($default)) { if ($required) { - throw new Exception('Cannot set a default value on a required attribute'); + throw new DatabaseException('Cannot set a default value on a required attribute'); } $this->validateDefaultTypes($type, $default); @@ -1254,7 +1255,7 @@ public function updateAttribute(string $collection, string $id, string $type = n $updated = $this->adapter->updateAttribute($collection, $id, $type, $size, $signed, $array); if (!$updated) { - throw new Exception('Failed to update attribute'); + throw new DatabaseException('Failed to update attribute'); } $this->deleteCachedCollection($collection); @@ -1305,8 +1306,8 @@ public function checkAttribute(Document $collection, Document $attribute): bool * @param string $id * * @return bool - * @throws Exception - * @throws Throwable + * @throws ConflictException + * @throws DatabaseException */ public function deleteAttribute(string $collection, string $id): bool { @@ -1325,11 +1326,11 @@ public function deleteAttribute(string $collection, string $id): bool } if (\is_null($attribute)) { - throw new Exception('Attribute not found'); + throw new DatabaseException('Attribute not found'); } if ($attribute['type'] === self::VAR_RELATIONSHIP) { - throw new Exception('Cannot delete relationship as an attribute, use deleteRelationship instead'); + throw new DatabaseException('Cannot delete relationship as an attribute'); } foreach ($indexes as $indexKey => $index) { @@ -1347,7 +1348,7 @@ public function deleteAttribute(string $collection, string $id): bool $deleted = $this->adapter->deleteAttribute($collection->getId(), $id); if (!$deleted) { - throw new Exception('Failed to delete attribute'); + throw new DatabaseException('Failed to delete attribute'); } $collection->setAttribute('attributes', \array_values($attributes)); @@ -1370,9 +1371,10 @@ public function deleteAttribute(string $collection, string $id): bool * @param string $new * @return bool * @throws AuthorizationException + * @throws ConflictException + * @throws DatabaseException * @throws DuplicateException * @throws StructureException - * @throws Throwable */ public function renameAttribute(string $collection, string $old, string $new): bool { @@ -1383,7 +1385,7 @@ public function renameAttribute(string $collection, string $old, string $new): b $attribute = \in_array($old, \array_map(fn ($attribute) => $attribute['$id'], $attributes)); if ($attribute === false) { - throw new Exception('Attribute not found'); + throw new DatabaseException('Attribute not found'); } $attributeNew = \in_array($new, \array_map(fn ($attribute) => $attribute['$id'], $attributes)); @@ -1435,10 +1437,11 @@ public function renameAttribute(string $collection, string $old, string $new): b * @param string $onDelete * @return bool * @throws AuthorizationException + * @throws ConflictException + * @throws DatabaseException * @throws DuplicateException * @throws LimitException * @throws StructureException - * @throws Throwable */ public function createRelationship( string $collection, @@ -1452,13 +1455,13 @@ public function createRelationship( $collection = $this->silent(fn () => $this->getCollection($collection)); if ($collection->isEmpty()) { - throw new Exception('Collection not found'); + throw new DatabaseException('Collection not found'); } $relatedCollection = $this->silent(fn () => $this->getCollection($relatedCollection)); if ($relatedCollection->isEmpty()) { - throw new Exception('Related collection not found'); + throw new DatabaseException('Related collection not found'); } $id ??= $relatedCollection->getId(); @@ -1572,7 +1575,7 @@ public function createRelationship( ); if (!$created) { - throw new Exception('Failed to create attribute'); + throw new DatabaseException('Failed to create relationship'); } $this->silent(function () use ($collection, $relatedCollection, $type, $twoWay, $id, $twoWayKey) { @@ -1599,7 +1602,7 @@ public function createRelationship( // Indexes created on junction collection creation break; default: - throw new Exception('Invalid relationship type.'); + throw new DatabaseException('Invalid relationship type.'); } }); @@ -1618,7 +1621,8 @@ public function createRelationship( * @param bool|null $twoWay * @param string|null $onDelete * @return bool - * @throws Throwable + * @throws ConflictException + * @throws DatabaseException */ public function updateRelationship( string $collection, @@ -1722,7 +1726,7 @@ public function updateRelationship( ); if (!$updated) { - throw new Exception('Failed to update relationship'); + throw new DatabaseException('Failed to update relationship'); } } @@ -1772,7 +1776,7 @@ public function updateRelationship( } break; default: - throw new Exception('Invalid relationship type.'); + throw new DatabaseException('Invalid relationship type.'); } }); @@ -1787,8 +1791,9 @@ public function updateRelationship( * * @return bool * @throws AuthorizationException + * @throws ConflictException + * @throws DatabaseException * @throws StructureException - * @throws Throwable */ public function deleteRelationship(string $collection, string $id): bool { @@ -1805,7 +1810,7 @@ public function deleteRelationship(string $collection, string $id): bool } if (\is_null($relationship)) { - throw new Exception('Attribute not found'); + throw new DatabaseException('Attribute not found'); } $collection->setAttribute('attributes', \array_values($attributes)); @@ -1866,7 +1871,7 @@ public function deleteRelationship(string $collection, string $id): bool $this->deleteDocument(self::METADATA, $junction); break; default: - throw new Exception('Invalid relationship type.'); + throw new DatabaseException('Invalid relationship type.'); } }); @@ -1881,7 +1886,7 @@ public function deleteRelationship(string $collection, string $id): bool ); if (!$deleted) { - throw new Exception('Failed to delete relationship'); + throw new DatabaseException('Failed to delete relationship'); } $this->deleteCachedCollection($collection->getId()); @@ -1901,9 +1906,10 @@ public function deleteRelationship(string $collection, string $id): bool * * @return bool * @throws AuthorizationException + * @throws ConflictException + * @throws DatabaseException * @throws DuplicateException * @throws StructureException - * @throws Throwable */ public function renameIndex(string $collection, string $old, string $new): bool { @@ -1914,7 +1920,7 @@ public function renameIndex(string $collection, string $old, string $new): bool $index = \in_array($old, \array_map(fn ($index) => $index['$id'], $indexes)); if ($index === false) { - throw new Exception('Index not found'); + throw new DatabaseException('Index not found'); } $indexNew = \in_array($new, \array_map(fn ($index) => $index['$id'], $indexes)); @@ -1957,22 +1963,23 @@ public function renameIndex(string $collection, string $old, string $new): bool * * @return bool * @throws AuthorizationException + * @throws ConflictException + * @throws DatabaseException * @throws DuplicateException * @throws LimitException * @throws StructureException - * @throws Throwable */ public function createIndex(string $collection, string $id, string $type, array $attributes, array $lengths = [], array $orders = []): bool { if (empty($attributes)) { - throw new Exception('Missing attributes'); + throw new DatabaseException('Missing attributes'); } $collection = $this->silent(fn () => $this->getCollection($collection)); $validator = new IndexValidator($collection); if (!$validator->isValid(['type' => $type, 'attributes' => $attributes])) { - throw new Exception($validator->getDescription()); + throw new DatabaseException($validator->getDescription()); } // index IDs are case-insensitive @@ -1992,24 +1999,24 @@ public function createIndex(string $collection, string $id, string $type, array switch ($type) { case self::INDEX_KEY: if (!$this->adapter->getSupportForIndex()) { - throw new Exception('Key index is not supported'); + throw new DatabaseException('Key index is not supported'); } break; case self::INDEX_UNIQUE: if (!$this->adapter->getSupportForUniqueIndex()) { - throw new Exception('Unique index is not supported'); + throw new DatabaseException('Unique index is not supported'); } break; case self::INDEX_FULLTEXT: if (!$this->adapter->getSupportForUniqueIndex()) { - throw new Exception('Fulltext index is not supported'); + throw new DatabaseException('Fulltext index is not supported'); } break; default: - throw new Exception('Unknown index type: ' . $type); + throw new DatabaseException('Unknown index type: ' . $type . '. Must be one of ' . Database::INDEX_KEY . ', ' . Database::INDEX_UNIQUE . ', ' . Database::INDEX_ARRAY . ', ' . Database::INDEX_FULLTEXT); } $index = $this->adapter->createIndex($collection->getId(), $id, $type, $attributes, $lengths, $orders); @@ -2039,6 +2046,10 @@ public function createIndex(string $collection, string $id, string $type, array * @param string $id * * @return bool + * @throws AuthorizationException + * @throws ConflictException + * @throws DatabaseException + * @throws StructureException */ public function deleteIndex(string $collection, string $id): bool { @@ -2075,7 +2086,7 @@ public function deleteIndex(string $collection, string $id): bool * @param Query[] $queries * * @return Document - * @throws Exception|Throwable + * @throws DatabaseException */ public function getDocument(string $collection, string $id, array $queries = []): Document { @@ -2084,7 +2095,7 @@ public function getDocument(string $collection, string $id, array $queries = []) } if (empty($collection)) { - throw new Exception('Collection not found'); + throw new DatabaseException('Collection not found'); } if (empty($id)) { @@ -2236,7 +2247,7 @@ public function getDocument(string $collection, string $id, array $queries = []) * @param Document $document * @param array $queries * @return Document - * @throws Throwable + * @throws DatabaseException */ private function populateDocumentRelationships(Document $collection, Document $document, array $queries = []): Document { @@ -2473,8 +2484,8 @@ private function populateDocumentRelationships(Document $collection, Document $d * @return Document * * @throws AuthorizationException + * @throws DatabaseException * @throws StructureException - * @throws Exception|Throwable */ public function createDocument(string $collection, Document $document): Document { @@ -2528,8 +2539,7 @@ public function createDocument(string $collection, Document $document): Document * @param Document $collection * @param Document $document * @return Document - * @throws Exception - * @throws Throwable + * @throws DatabaseException */ private function createDocumentRelationships(Document $collection, Document $document): Document { @@ -2568,7 +2578,7 @@ private function createDocumentRelationships(Document $collection, Document $doc switch (\gettype($related)) { case 'object': if (!$related instanceof Document) { - throw new Exception('Invalid relationship value. Must be either a document, document ID, or an array of documents or document IDs.'); + throw new DatabaseException('Invalid relationship value. Must be either a document, document ID, or an array of documents or document IDs.'); } $this->relateDocuments( $collection, @@ -2596,14 +2606,14 @@ private function createDocumentRelationships(Document $collection, Document $doc ); break; default: - throw new Exception('Invalid relationship value. Must be either a document, document ID, or an array of documents or document IDs.'); + throw new DatabaseException('Invalid relationship value. Must be either a document, document ID, or an array of documents or document IDs.'); } } $document->removeAttribute($key); break; case 'object': if (!$value instanceof Document) { - throw new Exception('Invalid relationship value. Must be either a document, document ID, or an array of documents or document IDs.'); + throw new DatabaseException('Invalid relationship value. Must be either a document, document ID, or an array of documents or document IDs.'); } $relatedId = $this->relateDocuments( $collection, @@ -2638,7 +2648,7 @@ private function createDocumentRelationships(Document $collection, Document $doc // No related document break; default: - throw new Exception('Invalid relationship value. Must be either a document, document ID, or an array of documents or document IDs.'); + throw new DatabaseException('Invalid relationship value. Must be either a document, document ID, or an array of documents or document IDs.'); } } finally { \array_pop($this->relationshipWriteStack); @@ -2649,11 +2659,21 @@ private function createDocumentRelationships(Document $collection, Document $doc } /** + * @param Document $collection + * @param Document $relatedCollection + * @param string $key + * @param Document $document + * @param Document $relation + * @param string $relationType + * @param bool $twoWay + * @param string $twoWayKey + * @param string $side * @return string related document ID * * @throws AuthorizationException - * @throws Throwable + * @throws ConflictException * @throws StructureException + * @throws Exception */ private function relateDocuments( Document $collection, @@ -2720,6 +2740,22 @@ private function relateDocuments( return $related->getId(); } + /** + * @param Document $collection + * @param Document $relatedCollection + * @param string $key + * @param string $documentId + * @param string $relationId + * @param string $relationType + * @param bool $twoWay + * @param string $twoWayKey + * @param string $side + * @return void + * @throws AuthorizationException + * @throws ConflictException + * @throws StructureException + * @throws Exception + */ private function relateDocumentsById( Document $collection, Document $relatedCollection, @@ -2784,13 +2820,14 @@ private function relateDocumentsById( * @return Document * * @throws AuthorizationException + * @throws ConflictException + * @throws DatabaseException * @throws StructureException - * @throws Throwable */ public function updateDocument(string $collection, string $id, Document $document): Document { if (!$document->getId() || !$id) { - throw new Exception('Must define $id attribute'); + throw new DatabaseException('Must define $id attribute'); } $time = DateTime::now(); @@ -2853,8 +2890,10 @@ public function updateDocument(string $collection, string $id, Document $documen * * @return Document * @throws AuthorizationException + * @throws ConflictException + * @throws DatabaseException + * @throws DuplicateException * @throws StructureException - * @throws Throwable */ private function updateDocumentRelationships(Document $collection, Document $old, Document $document): Document { @@ -2980,7 +3019,7 @@ private function updateDocumentRelationships(Document $collection, Document $old } break; default: - throw new Exception('Invalid type for relationship. Must be either a document, document ID or null.'); + throw new DatabaseException('Invalid type for relationship. Must be either a document, document ID or null.'); } break; case Database::RELATION_ONE_TO_MANY: @@ -2994,7 +3033,7 @@ private function updateDocumentRelationships(Document $collection, Document $old } if (!\is_array($value)) { - throw new Exception('Invalid value for relationship'); + throw new DatabaseException('Invalid value for relationship'); } $oldIds = \array_map(fn ($document) => $document->getId(), $oldValue); @@ -3005,7 +3044,7 @@ private function updateDocumentRelationships(Document $collection, Document $old } elseif ($item instanceof Document) { return $item->getId(); } else { - throw new Exception('Invalid value for relationship'); + throw new DatabaseException('Invalid value for relationship'); } }, $value); @@ -3055,7 +3094,7 @@ private function updateDocumentRelationships(Document $collection, Document $old ); } } else { - throw new Exception('Invalid value for relationship'); + throw new DatabaseException('Invalid value for relationship'); } } @@ -3089,7 +3128,7 @@ private function updateDocumentRelationships(Document $collection, Document $old } elseif (\is_null($value)) { break; } else { - throw new Exception('Invalid value for relationship'); + throw new DatabaseException('Invalid value for relationship'); } break; @@ -3098,7 +3137,7 @@ private function updateDocumentRelationships(Document $collection, Document $old break; } if (!\is_array($value)) { - throw new Exception('Invalid value for relationship'); + throw new DatabaseException('Invalid value for relationship'); } $oldIds = \array_map(fn ($document) => $document->getId(), $oldValue); @@ -3109,7 +3148,7 @@ private function updateDocumentRelationships(Document $collection, Document $old } elseif ($item instanceof Document) { return $item->getId(); } else { - throw new Exception('Invalid value for relationship'); + throw new DatabaseException('Invalid value for relationship'); } }, $value); @@ -3159,7 +3198,7 @@ private function updateDocumentRelationships(Document $collection, Document $old $relation = $related->getId(); } else { - throw new Exception('Invalid value for relationship'); + throw new DatabaseException('Invalid value for relationship'); } $this->skipRelationships(fn () => $this->createDocument( @@ -3205,13 +3244,12 @@ private function getJunctionCollection(Document $collection, Document $relatedCo * @return bool * * @throws AuthorizationException - * @throws Exception - * @throws Throwable + * @throws DatabaseException */ public function increaseDocumentAttribute(string $collection, string $id, string $attribute, int|float $value = 1, int|float|null $max = null): bool { if ($value <= 0) { // Can be a float - throw new Exception('Value must be numeric and greater than 0'); + throw new DatabaseException('Value must be numeric and greater than 0'); } $validator = new Authorization(self::PERMISSION_UPDATE); @@ -3235,7 +3273,7 @@ public function increaseDocumentAttribute(string $collection, string $id, string }); if (empty($attr)) { - throw new Exception('Attribute not found'); + throw new DatabaseException('Attribute not found'); } $whiteList = [self::VAR_INTEGER, self::VAR_FLOAT]; @@ -3245,11 +3283,11 @@ public function increaseDocumentAttribute(string $collection, string $id, string */ $attr = end($attr); if (!in_array($attr->getAttribute('type'), $whiteList)) { - throw new Exception('Attribute type must be one of: ' . implode(',', $whiteList)); + throw new DatabaseException('Attribute type must be one of: ' . implode(',', $whiteList)); } if ($max && ($document->getAttribute($attribute) + $value > $max)) { - throw new Exception('Attribute value exceeds maximum limit: ' . $max); + throw new DatabaseException('Attribute value exceeds maximum limit: ' . $max); } $max = $max ? $max - $value : null; @@ -3273,12 +3311,12 @@ public function increaseDocumentAttribute(string $collection, string $id, string * @return bool * * @throws AuthorizationException - * @throws Exception|Throwable + * @throws DatabaseException */ public function decreaseDocumentAttribute(string $collection, string $id, string $attribute, int|float $value = 1, int|float|null $min = null): bool { if ($value <= 0) { // Can be a float - throw new Exception('Value must be numeric and greater than 0'); + throw new DatabaseException('Value must be numeric and greater than 0'); } $validator = new Authorization(self::PERMISSION_UPDATE); @@ -3302,7 +3340,7 @@ public function decreaseDocumentAttribute(string $collection, string $id, string }); if (empty($attr)) { - throw new Exception('Attribute not found'); + throw new DatabaseException('Attribute not found'); } $whiteList = [self::VAR_INTEGER, self::VAR_FLOAT]; @@ -3312,11 +3350,11 @@ public function decreaseDocumentAttribute(string $collection, string $id, string */ $attr = end($attr); if (!in_array($attr->getAttribute('type'), $whiteList)) { - throw new Exception('Attribute type must be one of: ' . implode(',', $whiteList)); + throw new DatabaseException('Attribute type must be one of: ' . implode(',', $whiteList)); } if ($min && ($document->getAttribute($attribute) - $value < $min)) { - throw new Exception('Attribute value Exceeds minimum limit ' . $min); + throw new DatabaseException('Attribute value Exceeds minimum limit ' . $min); } $min = $min ? $min + $value : null; @@ -3337,8 +3375,9 @@ public function decreaseDocumentAttribute(string $collection, string $id, string * * @throws AuthorizationException * @throws ConflictException - * @throws Exception - * @throws Throwable + * @throws DatabaseException + * @throws RestrictedException + * @throws StructureException */ public function deleteDocument(string $collection, string $id): bool { @@ -3358,7 +3397,12 @@ public function deleteDocument(string $collection, string $id): bool } // Check if document was updated after the request timestamp - $oldUpdatedAt = new \DateTime($document->getUpdatedAt()); + try { + $oldUpdatedAt = new \DateTime($document->getUpdatedAt()); + } catch (Exception $e) { + throw new DatabaseException($e->getMessage(), $e->getCode(), $e); + } + if (!is_null($this->timestamp) && $oldUpdatedAt > $this->timestamp) { throw new ConflictException('Document was updated after the request timestamp'); } @@ -3378,8 +3422,14 @@ public function deleteDocument(string $collection, string $id): bool } /** - * @throws Exception - * @throws Throwable + * @param Document $collection + * @param Document $document + * @return Document + * @throws AuthorizationException + * @throws ConflictException + * @throws DatabaseException + * @throws RestrictedException + * @throws StructureException */ private function deleteDocumentRelationships(Document $collection, Document $document): Document { @@ -3463,8 +3513,18 @@ private function deleteDocumentRelationships(Document $collection, Document $doc } /** - * @throws Exception - * @throws Throwable + * @param Document $relatedCollection + * @param Document $document + * @param mixed $value + * @param string $relationType + * @param bool $twoWay + * @param string $twoWayKey + * @param string $side + * @throws AuthorizationException + * @throws ConflictException + * @throws DatabaseException + * @throws RestrictedException + * @throws StructureException */ private function deleteRestrict( Document $relatedCollection, @@ -3524,16 +3584,24 @@ private function deleteRestrict( } } - private function deleteSetNull( - Document $collection, - Document $relatedCollection, - Document $document, - mixed $value, - string $relationType, - bool $twoWay, - string $twoWayKey, - string $side - ): void { + /** + * @param Document $collection + * @param Document $relatedCollection + * @param Document $document + * @param mixed $value + * @param string $relationType + * @param bool $twoWay + * @param string $twoWayKey + * @param string $side + * @return void + * @throws AuthorizationException + * @throws ConflictException + * @throws DatabaseException + * @throws RestrictedException + * @throws StructureException + */ + private function deleteSetNull(Document $collection, Document $relatedCollection, Document $document, mixed $value, string $relationType, bool $twoWay, string $twoWayKey, string $side): void + { switch ($relationType) { case Database::RELATION_ONE_TO_ONE: if (!$twoWay && $side === Database::RELATION_SIDE_PARENT) { @@ -3621,21 +3689,24 @@ private function deleteSetNull( } /** + * @param Document $collection + * @param Document $relatedCollection + * @param Document $document + * @param string $key + * @param mixed $value + * @param string $relationType + * @param string $twoWayKey + * @param string $side + * @param Document $relationship + * @return void * @throws AuthorizationException * @throws ConflictException - * @throws Throwable + * @throws DatabaseException + * @throws RestrictedException + * @throws StructureException */ - private function deleteCascade( - Document $collection, - Document $relatedCollection, - Document $document, - string $key, - mixed $value, - string $relationType, - string $twoWayKey, - string $side, - Document $relationship - ): void { + private function deleteCascade(Document $collection, Document $relatedCollection, Document $document, string $key, mixed $value, string $relationType, string $twoWayKey, string $side, Document $relationship): void + { switch ($relationType) { case Database::RELATION_ONE_TO_ONE: if ($value !== null) { @@ -3722,7 +3793,7 @@ private function deleteCascade( * @param string $collection * * @return bool - * @throws Exception + * @throws DatabaseException */ public function deleteCachedCollection(string $collection): bool { @@ -3736,6 +3807,7 @@ public function deleteCachedCollection(string $collection): bool * @param string $id * * @return bool + * @throws DatabaseException */ public function deleteCachedDocument(string $collection, string $id): bool { @@ -3750,13 +3822,12 @@ public function deleteCachedDocument(string $collection, string $id): bool * @param int|null $timeout * * @return array - * @throws Exception - * @throws Throwable + * @throws DatabaseException */ public function find(string $collection, array $queries = [], ?int $timeout = null): array { if (!\is_null($timeout) && $timeout <= 0) { - throw new Exception('Timeout must be greater than 0'); + throw new DatabaseException('Timeout must be greater than 0'); } $collection = $this->silent(fn () => $this->getCollection($collection)); @@ -3780,7 +3851,7 @@ public function find(string $collection, array $queries = [], ?int $timeout = nu $cursorDirection = $grouped['cursorDirection']; if (!empty($cursor) && $cursor->getCollection() !== $collection->getId()) { - throw new Exception("cursor Document must be from the same Collection."); + throw new DatabaseException("cursor Document must be from the same Collection."); } $cursor = empty($cursor) ? [] : $this->encode($collection, $cursor)->getArrayCopy(); @@ -3873,7 +3944,6 @@ public function find(string $collection, array $queries = [], ?int $timeout = nu return $results; } - /** * @param array $results * @param array $queries @@ -3930,19 +4000,19 @@ private function applyNestedQueries(array $results, array $queries, array $relat } } break; - case Query::TYPE_NOTEQUAL: + case Query::TYPE_NOT_EQUAL: $matched = $value !== $query->getValue(); break; case Query::TYPE_GREATER: $matched = $value > $query->getValue(); break; - case Query::TYPE_GREATEREQUAL: + case Query::TYPE_GREATER_EQUAL: $matched = $value >= $query->getValue(); break; case Query::TYPE_LESSER: $matched = $value < $query->getValue(); break; - case Query::TYPE_LESSEREQUAL: + case Query::TYPE_LESSER_EQUAL: $matched = $value <= $query->getValue(); break; case Query::TYPE_CONTAINS: @@ -3984,7 +4054,7 @@ private function applyNestedQueries(array $results, array $queries, array $relat * @param string $collection * @param array $queries * @return bool|Document - * @throws Exception + * @throws DatabaseException */ public function findOne(string $collection, array $queries = []): bool|Document { @@ -3999,21 +4069,21 @@ public function findOne(string $collection, array $queries = []): bool|Document /** * Count Documents * - * Count the number of documents. Pass $max=0 for unlimited count + * Count the number of documents. * * @param string $collection * @param array $queries * @param int|null $max * * @return int - * @throws Exception + * @throws DatabaseException */ public function count(string $collection, array $queries = [], ?int $max = null): int { $collection = $this->silent(fn () => $this->getCollection($collection)); if ($collection->isEmpty()) { - throw new Exception("Collection not found"); + throw new DatabaseException("Collection not found"); } $authorization = new Authorization(self::PERMISSION_READ); @@ -4043,14 +4113,14 @@ public function count(string $collection, array $queries = [], ?int $max = null) * @param int|null $max * * @return int|float - * @throws Exception + * @throws DatabaseException */ public function sum(string $collection, string $attribute, array $queries = [], ?int $max = null): float|int { $collection = $this->silent(fn () => $this->getCollection($collection)); if ($collection->isEmpty()) { - throw new Exception("Collection not found"); + throw new DatabaseException("Collection not found"); } $queries = self::convertQueries($collection, $queries); @@ -4080,7 +4150,7 @@ public static function addFilter(string $name, callable $encode, callable $decod /** * @return array - * @throws Exception + * @throws DatabaseException */ public function getInternalAttributes(): array { @@ -4098,7 +4168,7 @@ public function getInternalAttributes(): array * @param Document $document * * @return Document - * @throws Exception|Throwable + * @throws DatabaseException */ public function encode(Document $collection, Document $document): Document { @@ -4148,9 +4218,9 @@ public function encode(Document $collection, Document $document): Document * * @param Document $collection * @param Document $document - * @param string[] $selections + * @param array $selections * @return Document - * @throws Exception + * @throws DatabaseException */ public function decode(Document $collection, Document $document, array $selections = []): Document { @@ -4279,12 +4349,12 @@ public function casting(Document $collection, Document $document): Document * @param Document $document * * @return mixed - * @throws Throwable + * @throws DatabaseException */ protected function encodeAttribute(string $name, $value, Document $document): mixed { if (!array_key_exists($name, self::$filters) && !array_key_exists($name, $this->instanceFilters)) { - throw new Exception("Filter: {$name} not found"); + throw new DatabaseException("Filter: {$name} not found"); } try { @@ -4294,7 +4364,7 @@ protected function encodeAttribute(string $name, $value, Document $document): mi $value = self::$filters[$name]['encode']($value, $document, $this); } } catch (\Throwable $th) { - throw $th; + throw new DatabaseException($th->getMessage(), $th->getCode(), $th); } return $value; @@ -4311,12 +4381,12 @@ protected function encodeAttribute(string $name, $value, Document $document): mi * @param Document $document * * @return mixed - * @throws Exception + * @throws DatabaseException */ protected function decodeAttribute(string $name, mixed $value, Document $document): mixed { if (!array_key_exists($name, self::$filters) && !array_key_exists($name, $this->instanceFilters)) { - throw new Exception('Filter not found'); + throw new DatabaseException('Filter not found'); } if (array_key_exists($name, $this->instanceFilters)) { @@ -4334,7 +4404,7 @@ protected function decodeAttribute(string $name, mixed $value, Document $documen * @param Document $collection * @param array $queries * @return array - * @throws Exception + * @throws DatabaseException */ private function validateSelections(Document $collection, array $queries): array { @@ -4366,7 +4436,7 @@ private function validateSelections(Document $collection, array $queries): array $invalid = \array_diff($selections, $keys); if (!empty($invalid) && !\in_array('*', $invalid)) { - throw new \Exception('Can not select attributes: ' . \implode(', ', $invalid)); + throw new DatabaseException('Cannot select attributes: ' . \implode(', ', $invalid)); } $selections = \array_merge($selections, $relationshipSelections); @@ -4387,7 +4457,7 @@ private function validateSelections(Document $collection, array $queries): array * * @return int */ - public function getLimitForAttributes() + public function getLimitForAttributes(): int { // If negative, return 0 // -1 ==> virtual columns count as total, so treat as buffer @@ -4399,7 +4469,7 @@ public function getLimitForAttributes() * * @return int */ - public function getLimitForIndexes() + public function getLimitForIndexes(): int { return $this->adapter->getLimitForIndexes() - $this->adapter->getCountOfDefaultIndexes(); } @@ -4428,27 +4498,24 @@ public function getAdapter(): Adapter * @param Document $collection * @param array $queries * @return array - * @throws Exception + * @throws DatabaseException */ public static function convertQueries(Document $collection, array $queries): array { $attributes = $collection->getAttribute('attributes', []); - foreach ($attributes as $v) { - /* @var $v Document */ - switch ($v->getAttribute('type')) { - case Database::VAR_DATETIME: - foreach ($queries as $qk => $q) { - if ($q->getAttribute() === $v->getId()) { - $arr = $q->getValues(); - foreach ($arr as $vk => $vv) { - $arr[$vk] = DateTime::setTimezone($vv); - } - $q->setValues($arr); - $queries[$qk] = $q; + foreach ($attributes as $attribute) { + if ($attribute->getAttribute('type') == Database::VAR_DATETIME) { + foreach ($queries as $index => $query) { + if ($query->getAttribute() === $attribute->getId()) { + $values = $query->getValues(); + foreach ($values as $valueIndex => $value) { + $values[$valueIndex] = DateTime::setTimezone($value); } + $query->setValues($values); + $queries[$index] = $query; } - break; + } } } return $queries; @@ -4458,7 +4525,7 @@ public static function convertQueries(Document $collection, array $queries): arr * @param Document $collection * @param string $id * @return void - * @throws Exception + * @throws DatabaseException */ private function purgeRelatedDocuments(Document $collection, string $id): void { diff --git a/src/Database/DateTime.php b/src/Database/DateTime.php index 45a35dd96..b85e9f0b3 100644 --- a/src/Database/DateTime.php +++ b/src/Database/DateTime.php @@ -2,7 +2,7 @@ namespace Utopia\Database; -use Exception; +use Utopia\Database\Exception as DatabaseException; class DateTime { @@ -35,14 +35,14 @@ public static function format(\DateTime $date): string * @param \DateTime $date * @param int $seconds * @return string - * @throws Exception + * @throws DatabaseException */ public static function addSeconds(\DateTime $date, int $seconds): string { $interval = \DateInterval::createFromDateString($seconds . ' seconds'); if (!$interval) { - throw new Exception('Invalid interval'); + throw new DatabaseException('Invalid interval'); } $date->add($interval); @@ -53,13 +53,17 @@ public static function addSeconds(\DateTime $date, int $seconds): string /** * @param string $datetime * @return string - * @throws Exception + * @throws DatabaseException */ public static function setTimezone(string $datetime): string { - $value = new \DateTime($datetime); - $value->setTimezone(new \DateTimeZone(date_default_timezone_get())); - return DateTime::format($value); + try { + $value = new \DateTime($datetime); + $value->setTimezone(new \DateTimeZone(date_default_timezone_get())); + return DateTime::format($value); + } catch (\Exception $e) { + throw new DatabaseException($e->getMessage(), $e->getCode(), $e); + } } /** @@ -75,7 +79,7 @@ public static function formatTz(?string $dbFormat): ?string try { $value = new \DateTime($dbFormat); return $value->format(self::$formatTz); - } catch (\Throwable $th) { + } catch (\Throwable) { return $dbFormat; } } diff --git a/src/Database/Document.php b/src/Database/Document.php index 844bb00e3..2c3531e76 100644 --- a/src/Database/Document.php +++ b/src/Database/Document.php @@ -3,7 +3,7 @@ namespace Utopia\Database; use ArrayObject; -use Exception; +use Utopia\Database\Exception as DatabaseException; use Utopia\Database\Helpers\Permission; use Utopia\Database\Helpers\Role; @@ -22,14 +22,14 @@ class Document extends ArrayObject * Construct a new fields object * * @param array $input - * @throws Exception + * @throws DatabaseException * @see ArrayObject::__construct * */ public function __construct(array $input = []) { if (isset($input['$permissions']) && !is_array($input['$permissions'])) { - throw new Exception('$permissions must be of type array'); + throw new DatabaseException('$permissions must be of type array'); } foreach ($input as $key => &$value) { diff --git a/src/Database/Exception.php b/src/Database/Exception.php new file mode 100644 index 000000000..663abce47 --- /dev/null +++ b/src/Database/Exception.php @@ -0,0 +1,7 @@ + 0) { - $bytes = \random_bytes(\max(1, (int)\ceil(($padding / 2)))); // one byte expands to two chars + try { + $bytes = \random_bytes(\max(1, (int)\ceil(($padding / 2)))); // one byte expands to two chars + } catch (\Exception $e) { + throw new DatabaseException($e->getMessage(), $e->getCode(), $e); + } + $uniqid .= \substr(\bin2hex($bytes), 0, $padding); } diff --git a/src/Database/Helpers/Permission.php b/src/Database/Helpers/Permission.php index 70f32e859..bb850cc6a 100644 --- a/src/Database/Helpers/Permission.php +++ b/src/Database/Helpers/Permission.php @@ -4,6 +4,7 @@ use Exception; use Utopia\Database\Database; +use Utopia\Database\Exception as DatabaseException; class Permission { @@ -84,7 +85,7 @@ public static function parse(string $permission): self $permissionParts = \explode('("', $permission); if (\count($permissionParts) !== 2) { - throw new Exception('Invalid permission string format: "' . $permission . '".'); + throw new DatabaseException('Invalid permission string format: "' . $permission . '".'); } $permission = $permissionParts[0]; @@ -107,14 +108,14 @@ public static function parse(string $permission): self if (!$hasIdentifier) { $dimensionParts = \explode('/', $fullRole); if (\count($dimensionParts) !== 2) { - throw new Exception('Only one dimension can be provided.'); + throw new DatabaseException('Only one dimension can be provided'); } $role = $dimensionParts[0]; $dimension = $dimensionParts[1]; if (empty($dimension)) { - throw new Exception('Dimension must not be empty.'); + throw new DatabaseException('Dimension must not be empty'); } return new self($permission, $role, '', $dimension); } @@ -122,14 +123,14 @@ public static function parse(string $permission): self // Has both identifier and dimension $dimensionParts = \explode('/', $roleParts[1]); if (\count($dimensionParts) !== 2) { - throw new Exception('Only one dimension can be provided.'); + throw new DatabaseException('Only one dimension can be provided'); } $identifier = $dimensionParts[0]; $dimension = $dimensionParts[1]; if (empty($dimension)) { - throw new Exception('Dimension must not be empty.'); + throw new DatabaseException('Dimension must not be empty'); } return new self($permission, $role, $identifier, $dimension); diff --git a/src/Database/Helpers/Role.php b/src/Database/Helpers/Role.php index 82a7e0353..5553ba878 100644 --- a/src/Database/Helpers/Role.php +++ b/src/Database/Helpers/Role.php @@ -78,14 +78,14 @@ public static function parse(string $role): self if (!$hasIdentifier) { $dimensionParts = \explode('/', $role); if (\count($dimensionParts) !== 2) { - throw new \Exception('Only one dimension can be provided.'); + throw new \Exception('Only one dimension can be provided'); } $role = $dimensionParts[0]; $dimension = $dimensionParts[1]; if (empty($dimension)) { - throw new \Exception('Dimension must not be empty.'); + throw new \Exception('Dimension must not be empty'); } return new self($role, '', $dimension); } @@ -93,14 +93,14 @@ public static function parse(string $role): self // Has both identifier and dimension $dimensionParts = \explode('/', $roleParts[1]); if (\count($dimensionParts) !== 2) { - throw new \Exception('Only one dimension can be provided.'); + throw new \Exception('Only one dimension can be provided'); } $identifier = $dimensionParts[0]; $dimension = $dimensionParts[1]; if (empty($dimension)) { - throw new \Exception('Dimension must not be empty.'); + throw new \Exception('Dimension must not be empty'); } return new self($role, $identifier, $dimension); } diff --git a/src/Database/Query.php b/src/Database/Query.php index d92c6adec..a7fafa7b5 100644 --- a/src/Database/Query.php +++ b/src/Database/Query.php @@ -2,17 +2,17 @@ namespace Utopia\Database; -use Exception; +use Utopia\Database\Exception as DatabaseException; class Query { // Filter methods public const TYPE_EQUAL = 'equal'; - public const TYPE_NOTEQUAL = 'notEqual'; + public const TYPE_NOT_EQUAL = 'notEqual'; public const TYPE_LESSER = 'lessThan'; - public const TYPE_LESSEREQUAL = 'lessThanEqual'; + public const TYPE_LESSER_EQUAL = 'lessThanEqual'; public const TYPE_GREATER = 'greaterThan'; - public const TYPE_GREATEREQUAL = 'greaterThanEqual'; + public const TYPE_GREATER_EQUAL = 'greaterThanEqual'; public const TYPE_CONTAINS = 'contains'; public const TYPE_SEARCH = 'search'; public const TYPE_IS_NULL = 'isNull'; @@ -160,11 +160,11 @@ public static function isMethod(string $value): bool { return match (static::getMethodFromAlias($value)) { self::TYPE_EQUAL, - self::TYPE_NOTEQUAL, + self::TYPE_NOT_EQUAL, self::TYPE_LESSER, - self::TYPE_LESSEREQUAL, + self::TYPE_LESSER_EQUAL, self::TYPE_GREATER, - self::TYPE_GREATEREQUAL, + self::TYPE_GREATER_EQUAL, self::TYPE_CONTAINS, self::TYPE_SEARCH, self::TYPE_ORDERASC, @@ -200,7 +200,7 @@ public static function parse(string $filter): self $paramsStart = mb_strpos($filter, static::CHAR_PARENTHESES_START); if ($paramsStart === false) { - throw new Exception("Invalid query"); + throw new DatabaseException("Invalid query"); } $method = mb_substr($filter, 0, $paramsStart); @@ -211,7 +211,7 @@ public static function parse(string $filter): self // Check for deprecated query syntax if (\str_contains($method, '.')) { - throw new Exception("Invalid query method"); + throw new DatabaseException("Invalid query method"); } $currentParam = ""; // We build param here before pushing when it's ended @@ -329,11 +329,11 @@ public static function parse(string $filter): self $method = static::getMethodFromAlias($method); switch ($method) { case self::TYPE_EQUAL: - case self::TYPE_NOTEQUAL: + case self::TYPE_NOT_EQUAL: case self::TYPE_LESSER: - case self::TYPE_LESSEREQUAL: + case self::TYPE_LESSER_EQUAL: case self::TYPE_GREATER: - case self::TYPE_GREATEREQUAL: + case self::TYPE_GREATER_EQUAL: case self::TYPE_CONTAINS: case self::TYPE_SEARCH: case self::TYPE_IS_NULL: @@ -497,7 +497,7 @@ public static function equal(string $attribute, array $values): self */ public static function notEqual(string $attribute, mixed $value): self { - return new self(self::TYPE_NOTEQUAL, $attribute, [$value]); + return new self(self::TYPE_NOT_EQUAL, $attribute, [$value]); } /** @@ -521,7 +521,7 @@ public static function lessThan(string $attribute, mixed $value): self */ public static function lessThanEqual(string $attribute, mixed $value): self { - return new self(self::TYPE_LESSEREQUAL, $attribute, [$value]); + return new self(self::TYPE_LESSER_EQUAL, $attribute, [$value]); } /** @@ -545,7 +545,7 @@ public static function greaterThan(string $attribute, mixed $value): self */ public static function greaterThanEqual(string $attribute, mixed$value): self { - return new self(self::TYPE_GREATEREQUAL, $attribute, [$value]); + return new self(self::TYPE_GREATER_EQUAL, $attribute, [$value]); } /** @@ -823,7 +823,7 @@ public static function parseQueries(array $queries): array try { $parsed[] = Query::parse($query); } catch (\Throwable $th) { - throw new Exception("Invalid query: ${query}", previous: $th); + throw new DatabaseException("Invalid query: ${query}", previous: $th); } } diff --git a/src/Database/Validator/Structure.php b/src/Database/Validator/Structure.php index 42ecc65ca..14223b1a5 100644 --- a/src/Database/Validator/Structure.php +++ b/src/Database/Validator/Structure.php @@ -6,12 +6,13 @@ use Exception; use Utopia\Database\Database; use Utopia\Database\Document; +use Utopia\Database\Exception as DatabaseException; +use Utopia\Database\Validator\Datetime as DatetimeValidator; use Utopia\Validator; use Utopia\Validator\Boolean; use Utopia\Validator\FloatValidator; use Utopia\Validator\Integer; use Utopia\Validator\Text; -use Utopia\Database\Validator\Datetime as DatetimeValidator; class Structure extends Validator { @@ -154,13 +155,13 @@ public static function getFormat(string $name, string $type): array { if (isset(self::$formats[$name])) { if (self::$formats[$name]['type'] !== $type) { - throw new Exception('Format ("'.$name.'") not available for this attribute type ("'.$type.'")'); + throw new DatabaseException('Format "'.$name.'" not available for attribute type "'.$type.'"'); } return self::$formats[$name]; } - throw new Exception('Unknown format validator: "'.$name.'"'); + throw new DatabaseException('Unknown format validator "'.$name.'"'); } /** diff --git a/tests/Database/Base.php b/tests/Database/Base.php index cca1848ba..edbaf7d84 100644 --- a/tests/Database/Base.php +++ b/tests/Database/Base.php @@ -3778,7 +3778,7 @@ public function testOneToOneOneWayRelationship(): void static::getDatabase()->deleteAttribute('person', 'library'); $this->fail('Failed to throw Exception'); } catch (Exception $e) { - $this->assertEquals('Cannot delete relationship as an attribute, use deleteRelationship instead', $e->getMessage()); + $this->assertEquals('Cannot delete relationship as an attribute', $e->getMessage()); } // Create document with relationship with nested data diff --git a/tests/Database/QueryTest.php b/tests/Database/QueryTest.php index 81f51d4e0..a7e497959 100644 --- a/tests/Database/QueryTest.php +++ b/tests/Database/QueryTest.php @@ -455,11 +455,11 @@ public function testisMethod(): void $this->assertTrue(Query::isMethod('select')); $this->assertTrue(Query::isMethod(Query::TYPE_EQUAL)); - $this->assertTrue(Query::isMethod(Query::TYPE_NOTEQUAL)); + $this->assertTrue(Query::isMethod(Query::TYPE_NOT_EQUAL)); $this->assertTrue(Query::isMethod(Query::TYPE_LESSER)); - $this->assertTrue(Query::isMethod(Query::TYPE_LESSEREQUAL)); + $this->assertTrue(Query::isMethod(Query::TYPE_LESSER_EQUAL)); $this->assertTrue(Query::isMethod(Query::TYPE_GREATER)); - $this->assertTrue(Query::isMethod(Query::TYPE_GREATEREQUAL)); + $this->assertTrue(Query::isMethod(Query::TYPE_GREATER_EQUAL)); $this->assertTrue(Query::isMethod(Query::TYPE_CONTAINS)); $this->assertTrue(Query::isMethod(QUERY::TYPE_SEARCH)); $this->assertTrue(Query::isMethod(QUERY::TYPE_ORDERASC)); diff --git a/tests/Database/Validator/PermissionsTest.php b/tests/Database/Validator/PermissionsTest.php index 1809f0025..a42b10910 100644 --- a/tests/Database/Validator/PermissionsTest.php +++ b/tests/Database/Validator/PermissionsTest.php @@ -2,12 +2,13 @@ namespace Utopia\Tests\Validator; +use PHPUnit\Framework\TestCase; use Utopia\Database\Document; +use Utopia\Database\Exception as DatabaseException; use Utopia\Database\Helpers\ID; use Utopia\Database\Helpers\Permission; use Utopia\Database\Helpers\Role; use Utopia\Database\Validator\Permissions; -use PHPUnit\Framework\TestCase; use Utopia\Database\Validator\Roles; class PermissionsTest extends TestCase @@ -21,7 +22,7 @@ public function tearDown(): void } /** - * @throws \Exception + * @throws DatabaseException */ public function testSingleMethodSingleValue(): void { @@ -276,13 +277,13 @@ public function testInvalidPermissions(): void $this->assertFalse($object->isValid([Permission::read(Role::team(ID::custom('_abcd')))])); $this->assertEquals('Role "team" identifier value is invalid: Parameter must contain at most 36 chars. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char', $object->getDescription()); $this->assertFalse($object->isValid([Permission::read(Role::team(ID::custom('abcd/')))])); - $this->assertEquals('Dimension must not be empty.', $object->getDescription()); + $this->assertEquals('Dimension must not be empty', $object->getDescription()); $this->assertFalse($object->isValid([Permission::read(Role::team(ID::custom(''), 'abcd'))])); $this->assertEquals('Role "team" must have an ID value.', $object->getDescription()); $this->assertFalse($object->isValid([Permission::read(Role::team(ID::custom('abcd'), '/efgh'))])); - $this->assertEquals('Only one dimension can be provided.', $object->getDescription()); + $this->assertEquals('Only one dimension can be provided', $object->getDescription()); $this->assertFalse($object->isValid([Permission::read(Role::team(ID::custom('abcd'), 'e/fgh'))])); - $this->assertEquals('Only one dimension can be provided.', $object->getDescription()); + $this->assertEquals('Only one dimension can be provided', $object->getDescription()); $this->assertFalse($object->isValid([Permission::read(Role::team(ID::custom('ab&cd3'), 'efgh'))])); $this->assertEquals('Role "team" identifier value is invalid: Parameter must contain at most 36 chars. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char', $object->getDescription()); $this->assertFalse($object->isValid([Permission::read(Role::team(ID::custom('abcd'), 'ef*gh'))]));