From 082d7b8991bd15044d0b0ac99ce3c9b00c9cbe67 Mon Sep 17 00:00:00 2001 From: fogelito Date: Mon, 8 May 2023 14:36:35 +0300 Subject: [PATCH 01/43] getSupportForFulltextIndex typo --- src/Database/Database.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Database/Database.php b/src/Database/Database.php index e9bd5b1b6..ef71dd831 100644 --- a/src/Database/Database.php +++ b/src/Database/Database.php @@ -1998,7 +1998,7 @@ public function createIndex(string $collection, string $id, string $type, array break; case self::INDEX_FULLTEXT: - if (!$this->adapter->getSupportForUniqueIndex()) { + if (!$this->adapter->getSupportForFulltextIndex()) { throw new Exception('Fulltext index is not supported'); } break; From fd194d5a6f80ce4e94ec3a7ac878bcb82f72895e Mon Sep 17 00:00:00 2001 From: fogelito Date: Tue, 9 May 2023 12:53:57 +0300 Subject: [PATCH 02/43] Index validators --- phpunit.xml | 2 +- src/Database/Database.php | 34 +- src/Database/Validator/Index.php | 97 +- tests/Database/Adapter/MariaDBTest.php | 130 +- tests/Database/Adapter/MongoDBTest.php | 204 +- tests/Database/Adapter/MySQLTest.php | 152 +- tests/Database/Adapter/PostgresTest.php | 126 +- tests/Database/Adapter/SQLiteTest.php | 146 +- tests/Database/Validator/IndexTest.php | 126 + .../Database/Validator/config/collections.php | 3580 +++++++++++++++++ 10 files changed, 4183 insertions(+), 414 deletions(-) create mode 100644 tests/Database/Validator/IndexTest.php create mode 100644 tests/Database/Validator/config/collections.php diff --git a/phpunit.xml b/phpunit.xml index 31b947dd6..3833748e0 100755 --- a/phpunit.xml +++ b/phpunit.xml @@ -7,7 +7,7 @@ convertNoticesToExceptions="true" convertWarningsToExceptions="true" processIsolation="false" - stopOnFailure="false"> + stopOnFailure="true"> ./tests/ diff --git a/src/Database/Database.php b/src/Database/Database.php index ef71dd831..774212e20 100644 --- a/src/Database/Database.php +++ b/src/Database/Database.php @@ -616,6 +616,7 @@ public function delete(string $name): bool * * @return Document * @throws DuplicateException + * @throws Exception|Throwable */ public function createCollection(string $id, array $attributes = [], array $indexes = [], array $permissions = null, bool $documentSecurity = true): Document { @@ -634,12 +635,6 @@ public function createCollection(string $id, array $attributes = [], array $inde throw new DuplicateException('Collection ' . $id . ' Exists!'); } - $this->adapter->createCollection($id, $attributes, $indexes); - - if ($id === self::METADATA) { - return new Document($this->collection); - } - $collection = new Document([ '$id' => ID::custom($id), '$permissions' => $permissions, @@ -649,6 +644,19 @@ public function createCollection(string $id, array $attributes = [], array $inde 'documentSecurity' => $documentSecurity ]); + $validator = new IndexValidator(); + if (!$validator->isValid($collection)) { + throw new Exception($validator->getDescription()); + } + + $this->adapter->createCollection($id, $attributes, $indexes); + + if ($id === self::METADATA) { + return new Document($this->collection); + } + + + // Check index limits, if given if ($indexes && $this->adapter->getCountOfIndexes($collection) > $this->adapter->getLimitForIndexes()) { throw new LimitException('Index limit of ' . $this->adapter->getLimitForIndexes() . ' exceeded. Cannot create collection.'); @@ -1965,11 +1973,6 @@ public function createIndex(string $collection, string $id, string $type, array $collection = $this->silent(fn () => $this->getCollection($collection)); - $validator = new IndexValidator($collection); - if (!$validator->isValid(['type' => $type, 'attributes' => $attributes])) { - throw new Exception($validator->getDescription()); - } - // index IDs are case-insensitive $indexes = $collection->getAttribute('indexes', []); @@ -2007,8 +2010,6 @@ public function createIndex(string $collection, string $id, string $type, array throw new Exception('Unknown index type: ' . $type); } - $index = $this->adapter->createIndex($collection->getId(), $id, $type, $attributes, $lengths, $orders); - $collection->setAttribute('indexes', new Document([ '$id' => ID::custom($id), 'key' => $id, @@ -2018,6 +2019,13 @@ public function createIndex(string $collection, string $id, string $type, array 'orders' => $orders, ]), Document::SET_TYPE_APPEND); + $validator = new IndexValidator(); + if (!$validator->isValid($collection)) { + throw new Exception($validator->getDescription()); + } + + $index = $this->adapter->createIndex($collection->getId(), $id, $type, $attributes, $lengths, $orders); + if ($collection->getId() !== self::METADATA) { $this->silent(fn () => $this->updateDocument(self::METADATA, $collection->getId(), $collection)); } diff --git a/src/Database/Validator/Index.php b/src/Database/Validator/Index.php index 0097990de..884739e95 100644 --- a/src/Database/Validator/Index.php +++ b/src/Database/Validator/Index.php @@ -9,11 +9,14 @@ class Index extends Validator { protected string $message = 'Invalid Index'; - protected Document $collection; - public function __construct(Document $collection) + /** + * @var array $attributes + */ + protected array $attributes = []; + + public function __construct() { - $this->collection = $collection; } /** @@ -25,36 +28,88 @@ public function getDescription(): string return $this->message; } + /** + * @param Document $collection + * @return bool + */ + public function checkFulltextIndexNonString(Document $collection): bool + { + foreach ($collection->getAttribute('indexes', []) as $index){ + if($index->getAttribute('type') === Database::INDEX_FULLTEXT){ + foreach ($index->getAttribute('attributes', []) as $ia) { + $attribute = $this->attributes[$ia]; + if ($attribute->getAttribute('type') !== Database::VAR_STRING) { + $this->message = 'Attribute "'.$attribute->getAttribute('key', $attribute->getAttribute('$id')).'" cannot be part of a FULLTEXT index'; + return false; + } + } + } + } + + return true; + } + + /** + * @param Document $collection + * @return bool + */ + public function checkIndexLength(Document $collection): bool + { + + foreach ($collection->getAttribute('indexes', []) as $index){ + + $total = 0; + + foreach ($index->getAttribute('attributes', []) as $ik => $ia){ + $attribute = $this->attributes[$ia]; + $attributeSize = $attribute->getAttribute('size', 0); + $indexLength = isset($index->getAttribute('lengths')[$ik]) ? $index->getAttribute('lengths')[$ik] : 0; + + // Todo: find tuning for Index type && Attribute type ... + // $index->getAttribute('type') === 'key' + + var_dump($attributeSize); + var_dump($ia); + var_dump($indexLength); + + $total += $attributeSize; + + if($total > $attributeSize){ + $this->message = 'Index Length is longer that the attribute size (' . $attributeSize . ')'; + return false; + } + + if($total > 768){ + $this->message = 'Index Length is longer that the max 768)'; + return false; + } + } + } + + return true; + + } + /** * Is valid. * * Returns true index if valid. - * @param mixed $value as index options + * @param Document $value * @return bool */ public function isValid($value): bool { - $indexType = $value['type']; - $indexAttributes = $value['attributes']; + foreach ($value->getAttribute('attributes', []) as $attribute){ + $this->attributes[$attribute->getAttribute('$id', $value->getAttribute('key'))] = $attribute; + } - if (empty($indexAttributes)) { - $this->message = 'Missing attributes'; + if(!$this->checkFulltextIndexNonString($value)){ return false; } - if ($indexType === Database::INDEX_FULLTEXT) { - $collectionAttributes = $this->collection->getAttributes()['attributes']; - foreach ($collectionAttributes as $collectionAttribute) { - foreach ($indexAttributes as $indexAttribute) { - if ($indexAttribute === $collectionAttribute['key']) { - if ($collectionAttribute['type'] !== Database::VAR_STRING) { - $this->message = 'Attribute "'.$collectionAttribute['key'].'" cannot be part of a FULLTEXT index'; - return false; - } - } - } - } - } +// if(!$this->checkIndexLength($value)){ +// return false; +// } return true; } diff --git a/tests/Database/Adapter/MariaDBTest.php b/tests/Database/Adapter/MariaDBTest.php index 9cc3fc781..f14b2882f 100644 --- a/tests/Database/Adapter/MariaDBTest.php +++ b/tests/Database/Adapter/MariaDBTest.php @@ -1,66 +1,66 @@ connect('redis', 6379); - $redis->flushAll(); - $cache = new Cache(new RedisAdapter($redis)); - - $database = new Database(new MariaDB($pdo), $cache); - $database->setDefaultDatabase('utopiaTests'); - $database->setNamespace('myapp_'.uniqid()); - - if ($database->exists('utopiaTests')) { - $database->delete('utopiaTests'); - } - - $database->create(); - - return self::$database = $database; - } - - public static function killDatabase(): void - { - self::$database = null; - } -} +// +//namespace Utopia\Tests\Adapter; +// +//use PDO; +//use Redis; +//use Utopia\Database\Database; +//use Utopia\Database\Adapter\MariaDB; +//use Utopia\Cache\Cache; +//use Utopia\Cache\Adapter\Redis as RedisAdapter; +//use Utopia\Tests\Base; +// +//class MariaDBTest extends Base +//{ +// public static ?Database $database = null; +// +// // TODO@kodumbeats hacky way to identify adapters for tests +// // Remove once all methods are implemented +// /** +// * Return name of adapter +// * +// * @return string +// */ +// public static function getAdapterName(): string +// { +// return "mariadb"; +// } +// +// /** +// * @return Database +// */ +// public static function getDatabase(): Database +// { +// if (!is_null(self::$database)) { +// return self::$database; +// } +// +// $dbHost = 'mariadb'; +// $dbPort = '3306'; +// $dbUser = 'root'; +// $dbPass = 'password'; +// +// $pdo = new PDO("mysql:host={$dbHost};port={$dbPort};charset=utf8mb4", $dbUser, $dbPass, MariaDB::getPDOAttributes()); +// $redis = new Redis(); +// $redis->connect('redis', 6379); +// $redis->flushAll(); +// $cache = new Cache(new RedisAdapter($redis)); +// +// $database = new Database(new MariaDB($pdo), $cache); +// $database->setDefaultDatabase('utopiaTests'); +// $database->setNamespace('myapp_'.uniqid()); +// +// if ($database->exists('utopiaTests')) { +// $database->delete('utopiaTests'); +// } +// +// $database->create(); +// +// return self::$database = $database; +// } +// +// public static function killDatabase(): void +// { +// self::$database = null; +// } +//} diff --git a/tests/Database/Adapter/MongoDBTest.php b/tests/Database/Adapter/MongoDBTest.php index 563837006..646318a71 100644 --- a/tests/Database/Adapter/MongoDBTest.php +++ b/tests/Database/Adapter/MongoDBTest.php @@ -1,103 +1,103 @@ connect('redis', 6379); - $redis->flushAll(); - $cache = new Cache(new RedisAdapter($redis)); - - $schema = 'utopiaTests'; // same as $this->testDatabase - $client = new Client( - $schema, - 'mongo', - 27017, - 'root', - 'example', - false - ); - - $database = new Database(new Mongo($client), $cache); - $database->setDefaultDatabase($schema); - $database->setNamespace('myapp_' . uniqid()); - - if ($database->exists('utopiaTests')) { - $database->delete('utopiaTests'); - } - - $database->create(); - - return self::$database = $database; - } - - /** - * @throws Exception - */ - public function testCreateExistsDelete(): void - { - // Mongo creates databases on the fly, so exists would always pass. So we override this test to remove the exists check. - $this->assertNotNull(static::getDatabase()->create()); - $this->assertEquals(true, static::getDatabase()->delete($this->testDatabase)); - $this->assertEquals(true, static::getDatabase()->create()); - $this->assertEquals(true, static::getDatabase()->setDefaultDatabase($this->testDatabase)); - } - - public function testRenameAttribute(): void - { - $this->assertTrue(true); - } - - public function testRenameAttributeExisting(): void - { - $this->assertTrue(true); - } - - public function testUpdateAttributeStructure(): void - { - $this->assertTrue(true); - } - - public function testKeywords(): void - { - $this->assertTrue(true); - } - - public static function killDatabase(): void - { - self::$database = null; - } -} +// +//namespace Utopia\Tests\Adapter; +// +//use Exception; +//use Redis; +//use Utopia\Cache\Cache; +//use Utopia\Cache\Adapter\Redis as RedisAdapter; +//use Utopia\Database\Adapter\Mongo; +//use Utopia\Database\Database; +//use Utopia\Mongo\Client; +//use Utopia\Tests\Base; +// +//class MongoDBTest extends Base +//{ +// public static ?Database $database = null; +// +// +// /** +// * Return name of adapter +// * +// * @return string +// */ +// public static function getAdapterName(): string +// { +// return "mongodb"; +// } +// +// /** +// * @return Database +// * @throws Exception +// */ +// public static function getDatabase(): Database +// { +// if (!is_null(self::$database)) { +// return self::$database; +// } +// +// $redis = new Redis(); +// $redis->connect('redis', 6379); +// $redis->flushAll(); +// $cache = new Cache(new RedisAdapter($redis)); +// +// $schema = 'utopiaTests'; // same as $this->testDatabase +// $client = new Client( +// $schema, +// 'mongo', +// 27017, +// 'root', +// 'example', +// false +// ); +// +// $database = new Database(new Mongo($client), $cache); +// $database->setDefaultDatabase($schema); +// $database->setNamespace('myapp_' . uniqid()); +// +// if ($database->exists('utopiaTests')) { +// $database->delete('utopiaTests'); +// } +// +// $database->create(); +// +// return self::$database = $database; +// } +// +// /** +// * @throws Exception +// */ +// public function testCreateExistsDelete(): void +// { +// // Mongo creates databases on the fly, so exists would always pass. So we override this test to remove the exists check. +// $this->assertNotNull(static::getDatabase()->create()); +// $this->assertEquals(true, static::getDatabase()->delete($this->testDatabase)); +// $this->assertEquals(true, static::getDatabase()->create()); +// $this->assertEquals(true, static::getDatabase()->setDefaultDatabase($this->testDatabase)); +// } +// +// public function testRenameAttribute(): void +// { +// $this->assertTrue(true); +// } +// +// public function testRenameAttributeExisting(): void +// { +// $this->assertTrue(true); +// } +// +// public function testUpdateAttributeStructure(): void +// { +// $this->assertTrue(true); +// } +// +// public function testKeywords(): void +// { +// $this->assertTrue(true); +// } +// +// public static function killDatabase(): void +// { +// self::$database = null; +// } +//} diff --git a/tests/Database/Adapter/MySQLTest.php b/tests/Database/Adapter/MySQLTest.php index f0ee5822b..fd5cf3232 100644 --- a/tests/Database/Adapter/MySQLTest.php +++ b/tests/Database/Adapter/MySQLTest.php @@ -1,77 +1,77 @@ connect('redis', 6379); - $redis->flushAll(); - - $cache = new Cache(new RedisAdapter($redis)); - - $database = new Database(new MySQL($pdo), $cache); - $database->setDefaultDatabase('utopiaTests'); - $database->setNamespace('myapp_'.uniqid()); - - if ($database->exists('utopiaTests')) { - $database->delete('utopiaTests'); - } - - $database->create(); - - return self::$database = $database; - } - - public static function killDatabase(): void - { - self::$database = null; - } -} +// +//namespace Utopia\Tests\Adapter; +// +//use PDO; +//use Redis; +//use Utopia\Cache\Cache; +//use Utopia\Database\Database; +//use Utopia\Database\Adapter\MySQL; +//use Utopia\Cache\Adapter\Redis as RedisAdapter; +//use Utopia\Tests\Base; +// +//class MySQLTest extends Base +//{ +// public static ?Database $database = null; +// +// // TODO@kodumbeats hacky way to identify adapters for tests +// // Remove once all methods are implemented +// /** +// * Return name of adapter +// * +// * @return string +// */ +// public static function getAdapterName(): string +// { +// return "mysql"; +// } +// +// /** +// * +// * @return int +// */ +// public static function getUsedIndexes(): int +// { +// return MySQL::getCountOfDefaultIndexes(); +// } +// +// /** +// * @return Database +// */ +// public static function getDatabase(): Database +// { +// if (!is_null(self::$database)) { +// return self::$database; +// } +// +// $dbHost = 'mysql'; +// $dbPort = '3307'; +// $dbUser = 'root'; +// $dbPass = 'password'; +// +// $pdo = new PDO("mysql:host={$dbHost};port={$dbPort};charset=utf8mb4", $dbUser, $dbPass, MySQL::getPDOAttributes()); +// +// $redis = new Redis(); +// $redis->connect('redis', 6379); +// $redis->flushAll(); +// +// $cache = new Cache(new RedisAdapter($redis)); +// +// $database = new Database(new MySQL($pdo), $cache); +// $database->setDefaultDatabase('utopiaTests'); +// $database->setNamespace('myapp_'.uniqid()); +// +// if ($database->exists('utopiaTests')) { +// $database->delete('utopiaTests'); +// } +// +// $database->create(); +// +// return self::$database = $database; +// } +// +// public static function killDatabase(): void +// { +// self::$database = null; +// } +//} diff --git a/tests/Database/Adapter/PostgresTest.php b/tests/Database/Adapter/PostgresTest.php index 4b5451341..a635ef7f0 100644 --- a/tests/Database/Adapter/PostgresTest.php +++ b/tests/Database/Adapter/PostgresTest.php @@ -1,64 +1,64 @@ connect('redis', 6379); - $redis->flushAll(); - $cache = new Cache(new RedisAdapter($redis)); - - $database = new Database(new Postgres($pdo), $cache); - $database->setDefaultDatabase('utopiaTests'); - $database->setNamespace('myapp_'.uniqid()); - - if ($database->exists('utopiaTests')) { - $database->delete('utopiaTests'); - } - - $database->create(); - - return self::$database = $database; - } - - public static function killDatabase(): void - { - self::$database = null; - } -} +// +//namespace Utopia\Tests\Adapter; +// +//use PDO; +//use Redis; +//use Utopia\Database\Database; +//use Utopia\Database\Adapter\Postgres; +//use Utopia\Cache\Cache; +//use Utopia\Cache\Adapter\Redis as RedisAdapter; +//use Utopia\Tests\Base; +// +//class PostgresTest extends Base +//{ +// public static ?Database $database = null; +// +// /** +// * Return name of adapter +// * +// * @return string +// */ +// public static function getAdapterName(): string +// { +// return "postgres"; +// } +// +// /** +// * @reture Adapter +// */ +// public static function getDatabase(): Database +// { +// if (!is_null(self::$database)) { +// return self::$database; +// } +// +// $dbHost = 'postgres'; +// $dbPort = '5432'; +// $dbUser = 'root'; +// $dbPass = 'password'; +// +// $pdo = new PDO("pgsql:host={$dbHost};port={$dbPort};", $dbUser, $dbPass, Postgres::getPDOAttributes()); +// $redis = new Redis(); +// $redis->connect('redis', 6379); +// $redis->flushAll(); +// $cache = new Cache(new RedisAdapter($redis)); +// +// $database = new Database(new Postgres($pdo), $cache); +// $database->setDefaultDatabase('utopiaTests'); +// $database->setNamespace('myapp_'.uniqid()); +// +// if ($database->exists('utopiaTests')) { +// $database->delete('utopiaTests'); +// } +// +// $database->create(); +// +// return self::$database = $database; +// } +// +// public static function killDatabase(): void +// { +// self::$database = null; +// } +//} diff --git a/tests/Database/Adapter/SQLiteTest.php b/tests/Database/Adapter/SQLiteTest.php index b704d58e1..7afc3b63d 100644 --- a/tests/Database/Adapter/SQLiteTest.php +++ b/tests/Database/Adapter/SQLiteTest.php @@ -1,74 +1,74 @@ connect('redis', 6379); - $redis->flushAll(); - - $cache = new Cache(new RedisAdapter($redis)); - - $database = new Database(new SQLite($pdo), $cache); - $database->setDefaultDatabase('utopiaTests'); - $database->setNamespace('myapp_'.uniqid()); - - return self::$database = $database; - } - - public static function killDatabase(): void - { - self::$database = null; - } -} +// +//namespace Utopia\Tests\Adapter; +// +//use PDO; +//use Redis; +//use Utopia\Cache\Cache; +//use Utopia\Database\Database; +//use Utopia\Cache\Adapter\Redis as RedisAdapter; +//use Utopia\Database\Adapter\SQLite; +//use Utopia\Tests\Base; +// +//class SQLiteTest extends Base +//{ +// public static ?Database $database = null; +// +// // TODO@kodumbeats hacky way to identify adapters for tests +// // Remove once all methods are implemented +// /** +// * Return name of adapter +// * +// * @return string +// */ +// public static function getAdapterName(): string +// { +// return "sqlite"; +// } +// +// /** +// * +// * @return int +// */ +// public static function getUsedIndexes(): int +// { +// return SQLite::getCountOfDefaultIndexes(); +// } +// +// /** +// * @return Database +// */ +// public static function getDatabase(): Database +// { +// if (!is_null(self::$database)) { +// return self::$database; +// } +// +// $sqliteDir = __DIR__."/database.sql"; +// +// if (file_exists($sqliteDir)) { +// unlink($sqliteDir); +// } +// +// $dsn = $sqliteDir; +// $dsn = 'memory'; // Overwrite for fast tests +// $pdo = new PDO("sqlite:" . $dsn, null, null, SQLite::getPDOAttributes()); +// +// $redis = new Redis(); +// $redis->connect('redis', 6379); +// $redis->flushAll(); +// +// $cache = new Cache(new RedisAdapter($redis)); +// +// $database = new Database(new SQLite($pdo), $cache); +// $database->setDefaultDatabase('utopiaTests'); +// $database->setNamespace('myapp_'.uniqid()); +// +// return self::$database = $database; +// } +// +// public static function killDatabase(): void +// { +// self::$database = null; +// } +//} diff --git a/tests/Database/Validator/IndexTest.php b/tests/Database/Validator/IndexTest.php new file mode 100644 index 000000000..bca80bb98 --- /dev/null +++ b/tests/Database/Validator/IndexTest.php @@ -0,0 +1,126 @@ +setAttribute('attributes', new Document([ + '$id' => ID::custom($attribute['$id']), + 'type' => $attribute['type'], + 'size' => $attribute['size'], + 'required' => $attribute['required'], + 'signed' => $attribute['signed'], + 'array' => $attribute['array'], + 'filters' => $attribute['filters'], + 'default' => $attribute['default'] ?? null, + 'format' => $attribute['format'] ?? '' + ]), Document::SET_TYPE_APPEND); + } + + foreach ($collection['indexes'] as $index) { + $document->setAttribute('indexes', new Document([ + '$id' => ID::custom($index['$id']), + 'type' => $index['type'], + 'attributes' => $index['attributes'], + 'lengths' => $index['lengths'], + 'orders' => $index['orders'], + ]), Document::SET_TYPE_APPEND); + } + + return $document; + } + + /** + * @throws Exception + */ + public function testAppwriteCollection(): void + { + $validator = new Index(); + + /** @var array $configs */ + $collections = include __DIR__ . '/config/collections.php'; + + foreach ($collections as $collection) { + $collection = $this->convertToCollection($collection); + $this->assertTrue($validator->isValid($collection)); + } + } + + /** + * @throws Exception + */ + public function testFulltextWithNonString(): void + { + $validator = new Index(); + + $collection = [ + '$id' => ID::custom('test'), + 'name' => 'test', + 'attributes' => [ + [ + '$id' => ID::custom('title'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 255, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('date'), + 'type' => Database::VAR_DATETIME, + 'format' => '', + 'size' => 0, + 'signed' => false, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => ['datetime'], + ], + ], + 'indexes' => [ + [ + '$id' => ID::custom('index1'), + 'type' => Database::INDEX_FULLTEXT, + 'attributes' => ['title', 'date'], + 'lengths' => [255], + 'orders' => [Database::ORDER_ASC], + ], + ], + ]; + + $collection = $this->convertToCollection($collection); + $this->assertFalse($validator->isValid($collection)); + $this->assertEquals('Attribute "date" cannot be part of a FULLTEXT index', $validator->getDescription()); + + } + +} diff --git a/tests/Database/Validator/config/collections.php b/tests/Database/Validator/config/collections.php new file mode 100644 index 000000000..bb4954e1d --- /dev/null +++ b/tests/Database/Validator/config/collections.php @@ -0,0 +1,3580 @@ + id of the parent collection where this will be inserted + * $id => id of this collection + * name => name of this collection + * project => whether this collection should be created per project + * attributes => list of attributes + * indexes => list of indexes + */ + +$collections = [ + 'databases' => [ + '$collection' => ID::custom(Database::METADATA), + '$id' => ID::custom('databases'), + 'name' => 'Databases', + 'attributes' => [ + [ + '$id' => ID::custom('name'), + 'type' => Database::VAR_STRING, + 'size' => 256, + 'required' => true, + 'signed' => true, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('search'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 16384, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + ], + 'indexes' => [ + [ + '$id' => ID::custom('_fulltext_search'), + 'type' => Database::INDEX_FULLTEXT, + 'attributes' => ['search'], + 'lengths' => [], + 'orders' => [], + ], + [ + '$id' => ID::custom('_key_name'), + 'type' => Database::INDEX_KEY, + 'attributes' => ['name'], + 'lengths' => [256], + 'orders' => [Database::ORDER_ASC], + ], + ], + ], + 'collections' => [ + '$collection' => ID::custom('databases'), + '$id' => ID::custom('collections'), + 'name' => 'Collections', + 'attributes' => [ + [ + '$id' => ID::custom('databaseInternalId'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => Database::LENGTH_KEY, + 'signed' => true, + 'required' => true, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('databaseId'), + 'type' => Database::VAR_STRING, + 'signed' => true, + 'size' => Database::LENGTH_KEY, + 'format' => '', + 'filters' => [], + 'required' => true, + 'default' => null, + 'array' => false, + ], + [ + '$id' => ID::custom('name'), + 'type' => Database::VAR_STRING, + 'size' => 256, + 'required' => true, + 'signed' => true, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('enabled'), + 'type' => Database::VAR_BOOLEAN, + 'signed' => true, + 'size' => 0, + 'format' => '', + 'filters' => [], + 'required' => true, + 'default' => null, + 'array' => false, + ], + [ + '$id' => ID::custom('documentSecurity'), + 'type' => Database::VAR_BOOLEAN, + 'signed' => true, + 'size' => 0, + 'format' => '', + 'filters' => [], + 'required' => true, + 'default' => null, + 'array' => false, + ], + [ + '$id' => ID::custom('attributes'), + 'type' => Database::VAR_STRING, + 'size' => 1000000, + 'required' => false, + 'signed' => true, + 'array' => false, + 'filters' => ['subQueryAttributes'], + ], + [ + '$id' => ID::custom('indexes'), + 'type' => Database::VAR_STRING, + 'size' => 1000000, + 'required' => false, + 'signed' => true, + 'array' => false, + 'filters' => ['subQueryIndexes'], + ], + [ + '$id' => ID::custom('search'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 16384, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + ], + 'indexes' => [ + [ + '$id' => ID::custom('_fulltext_search'), + 'type' => Database::INDEX_FULLTEXT, + 'attributes' => ['search'], + 'lengths' => [], + 'orders' => [], + ], + [ + '$id' => ID::custom('_key_name'), + 'type' => Database::INDEX_KEY, + 'attributes' => ['name'], + 'lengths' => [256], + 'orders' => [Database::ORDER_ASC], + ], + [ + '$id' => ID::custom('_key_enabled'), + 'type' => Database::INDEX_KEY, + 'attributes' => ['enabled'], + 'lengths' => [], + 'orders' => [Database::ORDER_ASC], + ], + [ + '$id' => ID::custom('_key_documentSecurity'), + 'type' => Database::INDEX_KEY, + 'attributes' => ['documentSecurity'], + 'lengths' => [], + 'orders' => [Database::ORDER_ASC], + ], + ], + ], + + 'attributes' => [ + '$collection' => ID::custom(Database::METADATA), + '$id' => ID::custom('attributes'), + 'name' => 'Attributes', + 'attributes' => [ + [ + '$id' => ID::custom('databaseInternalId'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => Database::LENGTH_KEY, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('databaseId'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => Database::LENGTH_KEY, + 'signed' => false, + 'required' => true, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('collectionInternalId'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => Database::LENGTH_KEY, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('collectionId'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => Database::LENGTH_KEY, + 'signed' => true, + 'required' => true, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('key'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => Database::LENGTH_KEY, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('type'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 256, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('status'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 16, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('size'), + 'type' => Database::VAR_INTEGER, + 'format' => '', + 'size' => 0, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('required'), + 'type' => Database::VAR_BOOLEAN, + 'format' => '', + 'size' => 0, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('default'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 16384, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => ['casting'], + ], + [ + '$id' => ID::custom('signed'), + 'type' => Database::VAR_BOOLEAN, + 'size' => 0, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('array'), + 'type' => Database::VAR_BOOLEAN, + 'size' => 0, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('format'), + 'type' => Database::VAR_STRING, + 'size' => 64, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('formatOptions'), + 'type' => Database::VAR_STRING, + 'size' => 16384, + 'signed' => true, + 'required' => false, + 'default' => new stdClass(), + 'array' => false, + 'filters' => ['json', 'range', 'enum'], + ], + [ + '$id' => ID::custom('filters'), + 'type' => Database::VAR_STRING, + 'size' => 64, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => true, + 'filters' => [], + ], + [ + '$id' => ID::custom('options'), + 'type' => Database::VAR_STRING, + 'size' => 16384, + 'signed' => false, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => ['json'], + ], + ], + 'indexes' => [ + [ + '$id' => ID::custom('_key_db_collection'), + 'type' => Database::INDEX_KEY, + 'attributes' => ['databaseInternalId', 'collectionInternalId'], + 'lengths' => [Database::LENGTH_KEY, Database::LENGTH_KEY], + 'orders' => [Database::ORDER_ASC, Database::ORDER_ASC], + ], + ], + ], + + 'indexes' => [ + '$collection' => ID::custom(Database::METADATA), + '$id' => ID::custom('indexes'), + 'name' => 'Indexes', + 'attributes' => [ + [ + '$id' => ID::custom('databaseInternalId'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => Database::LENGTH_KEY, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('databaseId'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => Database::LENGTH_KEY, + 'signed' => false, + 'required' => true, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('collectionInternalId'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => Database::LENGTH_KEY, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('collectionId'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => Database::LENGTH_KEY, + 'signed' => true, + 'required' => true, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('key'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => Database::LENGTH_KEY, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('type'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 16, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('status'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 16, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('attributes'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => Database::LENGTH_KEY, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => true, + 'filters' => [], + ], + [ + '$id' => ID::custom('lengths'), + 'type' => Database::VAR_INTEGER, + 'format' => '', + 'size' => 0, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => true, + 'filters' => [], + ], + [ + '$id' => ID::custom('orders'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 4, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => true, + 'filters' => [], + ], + ], + 'indexes' => [ + [ + '$id' => ID::custom('_key_db_collection'), + 'type' => Database::INDEX_KEY, + 'attributes' => ['databaseInternalId', 'collectionInternalId'], + 'lengths' => [Database::LENGTH_KEY, Database::LENGTH_KEY], + 'orders' => [Database::ORDER_ASC, Database::ORDER_ASC], + ], + ], + ], + + 'projects' => [ + '$collection' => ID::custom(Database::METADATA), + '$id' => ID::custom('projects'), + 'name' => 'Projects', + 'attributes' => [ + [ + '$id' => ID::custom('teamInternalId'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => Database::LENGTH_KEY, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('teamId'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => Database::LENGTH_KEY, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('name'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 128, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('region'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 128, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('description'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 256, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('logo'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => Database::LENGTH_KEY, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('url'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 16384, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('version'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 16, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('legalName'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 256, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('legalCountry'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 256, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('legalState'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 256, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('legalCity'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 256, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('legalAddress'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 256, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('legalTaxId'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 256, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('services'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 16384, + 'signed' => true, + 'required' => false, + 'default' => [], + 'array' => false, + 'filters' => ['json'], + ], + [ + '$id' => ID::custom('auths'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 16384, + 'signed' => true, + 'required' => false, + 'default' => [], + 'array' => false, + 'filters' => ['json'], + ], + [ + '$id' => ID::custom('authProviders'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 16384, + 'signed' => true, + 'required' => false, + 'default' => [], + 'array' => false, + 'filters' => ['json', 'encrypt'], + ], + [ + '$id' => ID::custom('platforms'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 16384, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => ['subQueryPlatforms'], + ], + [ + '$id' => ID::custom('webhooks'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 16384, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => ['subQueryWebhooks'], + ], + [ + '$id' => ID::custom('keys'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 16384, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => ['subQueryKeys'], + ], + [ + '$id' => ID::custom('domains'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 16384, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => ['subQueryDomains'], + ], + [ + '$id' => ID::custom('search'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 16384, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + ], + 'indexes' => [ + [ + '$id' => ID::custom('_key_search'), + 'type' => Database::INDEX_FULLTEXT, + 'attributes' => ['search'], + 'lengths' => [], + 'orders' => [], + ], + [ + '$id' => ID::custom('_key_name'), + 'type' => Database::INDEX_KEY, + 'attributes' => ['name'], + 'lengths' => [128], + 'orders' => [Database::ORDER_ASC], + ], + [ + '$id' => ID::custom('_key_team'), + 'type' => Database::INDEX_KEY, + 'attributes' => ['teamId'], + 'lengths' => [Database::LENGTH_KEY], + 'orders' => [Database::ORDER_ASC], + ], + ], + ], + + 'platforms' => [ + '$collection' => ID::custom(Database::METADATA), + '$id' => ID::custom('platforms'), + 'name' => 'platforms', + 'attributes' => [ + [ + '$id' => ID::custom('projectInternalId'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => Database::LENGTH_KEY, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('projectId'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => Database::LENGTH_KEY, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('type'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 16, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('name'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 256, + 'signed' => true, + 'required' => true, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('key'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => Database::LENGTH_KEY, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('store'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 256, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('hostname'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 256, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ] + ], + 'indexes' => [ + [ + '$id' => ID::custom('_key_project'), + 'type' => Database::INDEX_KEY, + 'attributes' => ['projectInternalId'], + 'lengths' => [Database::LENGTH_KEY], + 'orders' => [Database::ORDER_ASC], + ], + ], + ], + + 'domains' => [ + '$collection' => ID::custom(Database::METADATA), + '$id' => ID::custom('domains'), + 'name' => 'domains', + 'attributes' => [ + [ + '$id' => ID::custom('projectInternalId'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => Database::LENGTH_KEY, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('projectId'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => Database::LENGTH_KEY, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('updated'), + 'type' => Database::VAR_DATETIME, + 'format' => '', + 'size' => 0, + 'signed' => false, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => ['datetime'], + ], + [ + '$id' => ID::custom('domain'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => Database::LENGTH_KEY, + 'signed' => true, + 'required' => true, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('tld'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => Database::LENGTH_KEY, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('registerable'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => Database::LENGTH_KEY, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('verification'), + 'type' => Database::VAR_BOOLEAN, + 'format' => '', + 'size' => 0, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('certificateId'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => Database::LENGTH_KEY, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + ], + 'indexes' => [ + [ + '$id' => ID::custom('_key_project'), + 'type' => Database::INDEX_KEY, + 'attributes' => ['projectInternalId'], + 'lengths' => [Database::LENGTH_KEY], + 'orders' => [Database::ORDER_ASC], + ], + ], + ], + + 'keys' => [ + '$collection' => ID::custom(Database::METADATA), + '$id' => ID::custom('keys'), + 'name' => 'keys', + 'attributes' => [ + [ + '$id' => ID::custom('projectInternalId'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => Database::LENGTH_KEY, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('projectId'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => Database::LENGTH_KEY, + 'signed' => true, + 'required' => false, + 'default' => 0, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('name'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => Database::LENGTH_KEY, + 'signed' => true, + 'required' => true, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('scopes'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => Database::LENGTH_KEY, + 'signed' => true, + 'required' => true, + 'default' => null, + 'array' => true, + 'filters' => [], + ], + [ + '$id' => ID::custom('secret'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 512, // var_dump of \bin2hex(\random_bytes(128)) => string(256) doubling for encryption + 'signed' => true, + 'required' => true, + 'default' => null, + 'array' => false, + 'filters' => ['encrypt'], + ], + [ + '$id' => ID::custom('expire'), + 'type' => Database::VAR_DATETIME, + 'format' => '', + 'size' => 0, + 'signed' => false, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => ['datetime'], + ], + [ + '$id' => ID::custom('accessedAt'), + 'type' => Database::VAR_DATETIME, + 'format' => '', + 'size' => 0, + 'signed' => false, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => ['datetime'], + ], + [ + '$id' => ID::custom('sdks'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => Database::LENGTH_KEY, + 'signed' => true, + 'required' => true, + 'default' => null, + 'array' => true, + 'filters' => [], + ], + ], + 'indexes' => [ + [ + '$id' => ID::custom('_key_project'), + 'type' => Database::INDEX_KEY, + 'attributes' => ['projectInternalId'], + 'lengths' => [Database::LENGTH_KEY], + 'orders' => [Database::ORDER_ASC], + ], + [ + '$id' => '_key_accessedAt', + 'type' => Database::INDEX_KEY, + 'attributes' => ['accessedAt'], + 'lengths' => [], + 'orders' => [], + ], + ], + ], + + 'webhooks' => [ + '$collection' => ID::custom(Database::METADATA), + '$id' => ID::custom('webhooks'), + 'name' => 'webhooks', + 'attributes' => [ + [ + '$id' => ID::custom('projectInternalId'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => Database::LENGTH_KEY, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('projectId'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => Database::LENGTH_KEY, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('name'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => Database::LENGTH_KEY, + 'signed' => true, + 'required' => true, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('url'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => Database::LENGTH_KEY, + 'signed' => true, + 'required' => true, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('httpUser'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => Database::LENGTH_KEY, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('httpPass'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => Database::LENGTH_KEY, // TODO will the length suffice after encryption? + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => ['encrypt'], + ], + [ + '$id' => ID::custom('security'), + 'type' => Database::VAR_BOOLEAN, + 'format' => '', + 'size' => 0, + 'signed' => true, + 'required' => true, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('events'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => Database::LENGTH_KEY, + 'signed' => true, + 'required' => true, + 'default' => null, + 'array' => true, + 'filters' => [], + ], + [ + '$id' => ID::custom('signatureKey'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 2048, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + ], + 'indexes' => [ + [ + '$id' => ID::custom('_key_project'), + 'type' => Database::INDEX_KEY, + 'attributes' => ['projectInternalId'], + 'lengths' => [Database::LENGTH_KEY], + 'orders' => [Database::ORDER_ASC], + ] + ], + ], + + 'users' => [ + '$collection' => ID::custom(Database::METADATA), + '$id' => ID::custom('users'), + 'name' => 'Users', + 'attributes' => [ + [ + '$id' => ID::custom('name'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 256, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('email'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 320, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('phone'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 16, // leading '+' and 15 digitts maximum by E.164 format + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('status'), + 'type' => Database::VAR_BOOLEAN, + 'format' => '', + 'size' => 0, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('passwordHistory'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 16384, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => true, + 'filters' => [], + ], + [ + '$id' => ID::custom('password'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 16384, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => ['encrypt'], + ], + [ + '$id' => 'hash', // Hashing algorithm used to hash the password + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 256, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('hashOptions'), // Configuration of hashing algorithm + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 65535, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => ['json'], + ], + [ + '$id' => ID::custom('passwordUpdate'), + 'type' => Database::VAR_DATETIME, + 'format' => '', + 'size' => 0, + 'signed' => false, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => ['datetime'], + ], + [ + '$id' => ID::custom('prefs'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 65535, + 'signed' => true, + 'required' => false, + 'default' => new \stdClass(), + 'array' => false, + 'filters' => ['json'], + ], + [ + '$id' => ID::custom('registration'), + 'type' => Database::VAR_DATETIME, + 'format' => '', + 'size' => 0, + 'signed' => false, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => ['datetime'], + ], + [ + '$id' => ID::custom('emailVerification'), + 'type' => Database::VAR_BOOLEAN, + 'format' => '', + 'size' => 0, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('phoneVerification'), + 'type' => Database::VAR_BOOLEAN, + 'format' => '', + 'size' => 0, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('reset'), + 'type' => Database::VAR_BOOLEAN, + 'format' => '', + 'size' => 0, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('sessions'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 16384, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => ['subQuerySessions'], + ], + [ + '$id' => ID::custom('tokens'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 16384, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => ['subQueryTokens'], + ], + [ + '$id' => ID::custom('memberships'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 16384, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => ['subQueryMemberships'], + ], + [ + '$id' => ID::custom('search'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 16384, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ] + ], + 'indexes' => [ + [ + '$id' => ID::custom('_key_name'), + 'type' => Database::INDEX_KEY, + 'attributes' => ['name'], + 'lengths' => [256], + 'orders' => [Database::ORDER_ASC], + ], + [ + '$id' => ID::custom('_key_email'), + 'type' => Database::INDEX_UNIQUE, + 'attributes' => ['email'], + 'lengths' => [320], + 'orders' => [Database::ORDER_ASC], + ], + [ + '$id' => ID::custom('_key_phone'), + 'type' => Database::INDEX_UNIQUE, + 'attributes' => ['phone'], + 'lengths' => [16], + 'orders' => [Database::ORDER_ASC], + ], + [ + '$id' => ID::custom('_key_status'), + 'type' => Database::INDEX_KEY, + 'attributes' => ['status'], + 'lengths' => [], + 'orders' => [Database::ORDER_ASC], + ], + [ + '$id' => ID::custom('_key_passwordUpdate'), + 'type' => Database::INDEX_KEY, + 'attributes' => ['passwordUpdate'], + 'lengths' => [], + 'orders' => [Database::ORDER_ASC], + ], + [ + '$id' => ID::custom('_key_registration'), + 'type' => Database::INDEX_KEY, + 'attributes' => ['registration'], + 'lengths' => [], + 'orders' => [Database::ORDER_ASC], + ], + [ + '$id' => ID::custom('_key_emailVerification'), + 'type' => Database::INDEX_KEY, + 'attributes' => ['emailVerification'], + 'lengths' => [], + 'orders' => [Database::ORDER_ASC], + ], + [ + '$id' => ID::custom('_key_phoneVerification'), + 'type' => Database::INDEX_KEY, + 'attributes' => ['phoneVerification'], + 'lengths' => [], + 'orders' => [Database::ORDER_ASC], + ], + [ + '$id' => ID::custom('_key_search'), + 'type' => Database::INDEX_FULLTEXT, + 'attributes' => ['search'], + 'lengths' => [], + 'orders' => [], + ] + ], + ], + + 'tokens' => [ + '$collection' => ID::custom(Database::METADATA), + '$id' => ID::custom('tokens'), + 'name' => 'Tokens', + 'attributes' => [ + [ + '$id' => ID::custom('userInternalId'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => Database::LENGTH_KEY, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('userId'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => Database::LENGTH_KEY, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('type'), + 'type' => Database::VAR_INTEGER, + 'format' => '', + 'size' => 0, + 'signed' => true, + 'required' => true, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('secret'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 512, // https://www.tutorialspoint.com/how-long-is-the-sha256-hash-in-mysql (512 for encryption) + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => ['encrypt'], + ], + [ + '$id' => ID::custom('expire'), + 'type' => Database::VAR_DATETIME, + 'format' => '', + 'size' => 0, + 'signed' => false, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => ['datetime'], + ], + [ + '$id' => ID::custom('userAgent'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 16384, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('ip'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 45, // https://stackoverflow.com/a/166157/2299554 + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ] + ], + 'indexes' => [ + [ + '$id' => ID::custom('_key_user'), + 'type' => Database::INDEX_KEY, + 'attributes' => ['userInternalId'], + 'lengths' => [Database::LENGTH_KEY], + 'orders' => [Database::ORDER_ASC], + ], + ], + ], + + 'sessions' => [ + '$collection' => ID::custom(Database::METADATA), + '$id' => ID::custom('sessions'), + 'name' => 'Sessions', + 'attributes' => [ + [ + '$id' => ID::custom('userInternalId'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => Database::LENGTH_KEY, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('userId'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => Database::LENGTH_KEY, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('provider'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 128, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('providerUid'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 2048, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('providerAccessToken'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 16384, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => ['encrypt'], + ], + [ + '$id' => ID::custom('providerAccessTokenExpiry'), + 'type' => Database::VAR_DATETIME, + 'format' => '', + 'size' => 0, + 'signed' => false, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => ['datetime'], + ], + [ + '$id' => ID::custom('providerRefreshToken'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 16384, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => ['encrypt'], + ], + [ + '$id' => ID::custom('secret'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 512, // https://www.tutorialspoint.com/how-long-is-the-sha256-hash-in-mysql (512 for encryption) + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => ['encrypt'], + ], + [ + '$id' => ID::custom('userAgent'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 16384, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('ip'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 45, // https://stackoverflow.com/a/166157/2299554 + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('countryCode'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 2, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('osCode'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 256, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('osName'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 256, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('osVersion'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 256, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('clientType'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 256, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('clientCode'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 256, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('clientName'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 256, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('clientVersion'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 256, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('clientEngine'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 256, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('clientEngineVersion'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 256, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('deviceName'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 256, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('deviceBrand'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 256, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('deviceModel'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 256, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + ], + 'indexes' => [ + [ + '$id' => ID::custom('_key_provider_providerUid'), + 'type' => Database::INDEX_KEY, + 'attributes' => ['provider', 'providerUid'], + 'lengths' => [100, 100], + 'orders' => [Database::ORDER_ASC, Database::ORDER_ASC], + ], + [ + '$id' => ID::custom('_key_user'), + 'type' => Database::INDEX_KEY, + 'attributes' => ['userInternalId'], + 'lengths' => [Database::LENGTH_KEY], + 'orders' => [Database::ORDER_ASC], + ], + ], + ], + + 'teams' => [ + '$collection' => ID::custom(Database::METADATA), + '$id' => ID::custom('teams'), + 'name' => 'Teams', + 'attributes' => [ + [ + '$id' => ID::custom('name'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 128, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('total'), + 'type' => Database::VAR_INTEGER, + 'format' => '', + 'size' => 0, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('search'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 16384, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('prefs'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 65535, + 'signed' => true, + 'required' => false, + 'default' => new \stdClass(), + 'array' => false, + 'filters' => ['json'], + ], + ], + 'indexes' => [ + [ + '$id' => ID::custom('_key_search'), + 'type' => Database::INDEX_FULLTEXT, + 'attributes' => ['search'], + 'lengths' => [], + 'orders' => [], + ], + [ + '$id' => ID::custom('_key_name'), + 'type' => Database::INDEX_KEY, + 'attributes' => ['name'], + 'lengths' => [128], + 'orders' => [Database::ORDER_ASC], + ], + [ + '$id' => ID::custom('_key_total'), + 'type' => Database::INDEX_KEY, + 'attributes' => ['total'], + 'lengths' => [], + 'orders' => [Database::ORDER_ASC], + ], + ], + ], + + 'memberships' => [ + '$collection' => ID::custom(Database::METADATA), + '$id' => ID::custom('memberships'), + 'name' => 'Memberships', + 'attributes' => [ + [ + '$id' => ID::custom('userInternalId'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => Database::LENGTH_KEY, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('userId'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => Database::LENGTH_KEY, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('teamInternalId'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => Database::LENGTH_KEY, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('teamId'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => Database::LENGTH_KEY, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('roles'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 128, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => true, + 'filters' => [], + ], + [ + '$id' => ID::custom('invited'), + 'type' => Database::VAR_DATETIME, + 'format' => '', + 'size' => 0, + 'signed' => false, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => ['datetime'], + ], + [ + '$id' => ID::custom('joined'), + 'type' => Database::VAR_DATETIME, + 'format' => '', + 'size' => 0, + 'signed' => false, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => ['datetime'], + ], + [ + '$id' => ID::custom('confirm'), + 'type' => Database::VAR_BOOLEAN, + 'format' => '', + 'size' => 0, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('secret'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 256, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => ['encrypt'], + ], + [ + '$id' => ID::custom('search'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 16384, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + ], + 'indexes' => [ + [ + '$id' => ID::custom('_key_unique'), + 'type' => Database::INDEX_UNIQUE, + 'attributes' => ['teamInternalId', 'userInternalId'], + 'lengths' => [Database::LENGTH_KEY, Database::LENGTH_KEY], + 'orders' => [Database::ORDER_ASC, Database::ORDER_ASC], + ], + [ + '$id' => ID::custom('_key_user'), + 'type' => Database::INDEX_KEY, + 'attributes' => ['userInternalId'], + 'lengths' => [Database::LENGTH_KEY], + 'orders' => [Database::ORDER_ASC], + ], + [ + '$id' => ID::custom('_key_team'), + 'type' => Database::INDEX_KEY, + 'attributes' => ['teamInternalId'], + 'lengths' => [Database::LENGTH_KEY], + 'orders' => [Database::ORDER_ASC], + ], + [ + '$id' => ID::custom('_key_search'), + 'type' => Database::INDEX_FULLTEXT, + 'attributes' => ['search'], + 'lengths' => [], + 'orders' => [], + ], + [ + '$id' => ID::custom('_key_userId'), + 'type' => Database::INDEX_KEY, + 'attributes' => ['userId'], + 'lengths' => [Database::LENGTH_KEY], + 'orders' => [Database::ORDER_ASC], + ], + [ + '$id' => ID::custom('_key_teamId'), + 'type' => Database::INDEX_KEY, + 'attributes' => ['teamId'], + 'lengths' => [Database::LENGTH_KEY], + 'orders' => [Database::ORDER_ASC], + ], + [ + '$id' => ID::custom('_key_invited'), + 'type' => Database::INDEX_KEY, + 'attributes' => ['invited'], + 'lengths' => [], + 'orders' => [Database::ORDER_ASC], + ], + [ + '$id' => ID::custom('_key_joined'), + 'type' => Database::INDEX_KEY, + 'attributes' => ['joined'], + 'lengths' => [], + 'orders' => [Database::ORDER_ASC], + ], + [ + '$id' => ID::custom('_key_confirm'), + 'type' => Database::INDEX_KEY, + 'attributes' => ['confirm'], + 'lengths' => [], + 'orders' => [Database::ORDER_ASC], + ], + ], + ], + + 'functions' => [ + '$collection' => ID::custom(Database::METADATA), + '$id' => ID::custom('functions'), + 'name' => 'Functions', + 'attributes' => [ + [ + '$id' => ID::custom('execute'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 128, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => true, + 'filters' => [], + ], + [ + '$id' => ID::custom('name'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 2048, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('enabled'), + 'type' => Database::VAR_BOOLEAN, + 'signed' => true, + 'size' => 0, + 'format' => '', + 'filters' => [], + 'required' => true, + 'array' => false, + ], + [ + '$id' => ID::custom('runtime'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 2048, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('deployment'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => Database::LENGTH_KEY, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('vars'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 16384, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => ['subQueryVariables'], + ], + [ + '$id' => ID::custom('events'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 256, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => true, + 'filters' => [], + ], + [ + '$id' => ID::custom('schedule'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 128, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('scheduleUpdatedAt'), // Used to fix duplicate executions bug. Can be removed once new queue library is used + 'type' => Database::VAR_DATETIME, + 'format' => '', + 'size' => 0, + 'signed' => false, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => ['datetime'], + ], + [ + '$id' => ID::custom('schedulePrevious'), + 'type' => Database::VAR_DATETIME, + 'format' => '', + 'size' => 0, + 'signed' => false, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => ['datetime'], + ], + [ + '$id' => ID::custom('scheduleNext'), + 'type' => Database::VAR_DATETIME, + 'format' => '', + 'size' => 0, + 'signed' => false, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => ['datetime'], + ], + [ + '$id' => ID::custom('timeout'), + 'type' => Database::VAR_INTEGER, + 'format' => '', + 'size' => 0, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('search'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 16384, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + ], + 'indexes' => [ + [ + '$id' => ID::custom('_key_search'), + 'type' => Database::INDEX_FULLTEXT, + 'attributes' => ['search'], + 'lengths' => [], + 'orders' => [Database::ORDER_ASC], + ], + [ + '$id' => ID::custom('_key_name'), + 'type' => Database::INDEX_KEY, + 'attributes' => ['name'], + 'lengths' => [768], + 'orders' => [Database::ORDER_ASC], + ], + [ + '$id' => ID::custom('_key_enabled'), + 'type' => Database::INDEX_KEY, + 'attributes' => ['enabled'], + 'lengths' => [], + 'orders' => [Database::ORDER_ASC], + ], + [ + '$id' => ID::custom('_key_runtime'), + 'type' => Database::INDEX_KEY, + 'attributes' => ['runtime'], + 'lengths' => [768], + 'orders' => [Database::ORDER_ASC], + ], + [ + '$id' => ID::custom('_key_deployment'), + 'type' => Database::INDEX_KEY, + 'attributes' => ['deployment'], + 'lengths' => [], + 'orders' => [Database::ORDER_ASC], + ], + [ + '$id' => ID::custom('_key_schedule'), + 'type' => Database::INDEX_KEY, + 'attributes' => ['schedule'], + 'lengths' => [], + 'orders' => [Database::ORDER_ASC], + ], + [ + '$id' => ID::custom('_key_scheduleNext'), + 'type' => Database::INDEX_KEY, + 'attributes' => ['scheduleNext'], + 'lengths' => [], + 'orders' => [Database::ORDER_ASC], + ], + [ + '$id' => ID::custom('_key_schedulePrevious'), + 'type' => Database::INDEX_KEY, + 'attributes' => ['schedulePrevious'], + 'lengths' => [], + 'orders' => [Database::ORDER_ASC], + ], + [ + '$id' => ID::custom('_key_timeout'), + 'type' => Database::INDEX_KEY, + 'attributes' => ['timeout'], + 'lengths' => [], + 'orders' => [Database::ORDER_ASC], + ], + ], + ], + + 'deployments' => [ + '$collection' => ID::custom(Database::METADATA), + '$id' => ID::custom('deployments'), + 'name' => 'Deployments', + 'attributes' => [ + [ + '$id' => ID::custom('resourceId'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => Database::LENGTH_KEY, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('resourceType'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => Database::LENGTH_KEY, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('buildId'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => Database::LENGTH_KEY, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + 'array' => false, + '$id' => ID::custom('entrypoint'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 2048, + 'signed' => true, + 'required' => false, + 'default' => null, + 'filters' => [], + ], + [ + '$id' => ID::custom('path'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 2048, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('size'), + 'type' => Database::VAR_INTEGER, + 'format' => '', + 'size' => 0, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('metadata'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 16384, // https://tools.ietf.org/html/rfc4288#section-4.2 + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => ['json'], + ], + [ + '$id' => ID::custom('chunksTotal'), + 'type' => Database::VAR_INTEGER, + 'format' => '', + 'size' => 0, + 'signed' => false, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('chunksUploaded'), + 'type' => Database::VAR_INTEGER, + 'format' => '', + 'size' => 0, + 'signed' => false, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('search'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 16384, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('activate'), + 'type' => Database::VAR_BOOLEAN, + 'format' => '', + 'size' => 0, + 'signed' => true, + 'required' => false, + 'default' => false, + 'array' => false, + 'filters' => [], + ] + ], + 'indexes' => [ + [ + '$id' => ID::custom('_key_resource'), + 'type' => Database::INDEX_KEY, + 'attributes' => ['resourceId'], + 'lengths' => [], + 'orders' => [Database::ORDER_ASC], + ], + [ + '$id' => ID::custom('_key_resource_type'), + 'type' => Database::INDEX_KEY, + 'attributes' => ['resourceType'], + 'lengths' => [], + 'orders' => [Database::ORDER_ASC], + ], + [ + '$id' => ID::custom('_key_search'), + 'type' => Database::INDEX_FULLTEXT, + 'attributes' => ['search'], + 'lengths' => [], + 'orders' => [], + ], + [ + '$id' => ID::custom('_key_entrypoint'), + 'type' => Database::INDEX_KEY, + 'attributes' => ['entrypoint'], + 'lengths' => [768], + 'orders' => [Database::ORDER_ASC], + ], + [ + '$id' => ID::custom('_key_size'), + 'type' => Database::INDEX_KEY, + 'attributes' => ['size'], + 'lengths' => [], + 'orders' => [Database::ORDER_ASC], + ], + [ + '$id' => ID::custom('_key_buildId'), + 'type' => Database::INDEX_KEY, + 'attributes' => ['buildId'], + 'lengths' => [], + 'orders' => [Database::ORDER_ASC], + ], + [ + '$id' => ID::custom('_key_activate'), + 'type' => Database::INDEX_KEY, + 'attributes' => ['activate'], + 'lengths' => [], + 'orders' => [Database::ORDER_ASC], + ], + ], + ], + + 'builds' => [ + '$collection' => ID::custom(Database::METADATA), + '$id' => ID::custom('builds'), + 'name' => 'Builds', + 'attributes' => [ + [ + '$id' => ID::custom('startTime'), + 'type' => Database::VAR_DATETIME, + 'format' => '', + 'size' => 0, + 'signed' => false, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => ['datetime'], + ], + [ + '$id' => ID::custom('endTime'), + 'type' => Database::VAR_DATETIME, + 'format' => '', + 'size' => 0, + 'signed' => false, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => ['datetime'], + ], + [ + '$id' => ID::custom('duration'), + 'type' => Database::VAR_INTEGER, + 'format' => '', + 'size' => 0, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('deploymentId'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => Database::LENGTH_KEY, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('runtime'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 2048, + 'signed' => true, + 'required' => true, + 'default' => '', + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('status'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 256, + 'signed' => true, + 'required' => true, + 'default' => 'processing', + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('outputPath'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 2048, + 'signed' => true, + 'required' => false, + 'default' => '', + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('stderr'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 1000000, + 'signed' => true, + 'required' => false, + 'default' => '', + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('stdout'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 1000000, + 'signed' => true, + 'required' => false, + 'default' => '', + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('sourceType'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 2048, + 'signed' => true, + 'required' => true, + 'default' => 'local', + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('source'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 2048, + 'signed' => true, + 'required' => true, + 'default' => '', + 'array' => false, + 'filters' => [], + ] + ], + 'indexes' => [ + [ + '$id' => ID::custom('_key_deployment'), + 'type' => Database::INDEX_KEY, + 'attributes' => ['deploymentId'], + 'lengths' => [Database::LENGTH_KEY], + 'orders' => [Database::ORDER_ASC], + ] + ], + ], + + 'executions' => [ + '$collection' => ID::custom(Database::METADATA), + '$id' => ID::custom('executions'), + 'name' => 'Executions', + 'attributes' => [ + [ + '$id' => ID::custom('functionId'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => Database::LENGTH_KEY, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('deploymentId'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => Database::LENGTH_KEY, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + 'array' => false, + '$id' => ID::custom('trigger'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 128, + 'signed' => true, + 'required' => false, + 'default' => null, + 'filters' => [], + ], + [ + '$id' => ID::custom('status'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 128, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('response'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 1000000, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('stderr'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 1000000, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('stdout'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 1000000, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('statusCode'), + 'type' => Database::VAR_INTEGER, + 'format' => '', + 'size' => 0, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('duration'), + 'type' => Database::VAR_FLOAT, + 'format' => '', + 'size' => 0, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('search'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 16384, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + ], + 'indexes' => [ + [ + '$id' => ID::custom('_key_function'), + 'type' => Database::INDEX_KEY, + 'attributes' => ['functionId'], + 'lengths' => [Database::LENGTH_KEY], + 'orders' => [Database::ORDER_ASC], + ], + [ + '$id' => ID::custom('_fulltext_search'), + 'type' => Database::INDEX_FULLTEXT, + 'attributes' => ['search'], + 'lengths' => [], + 'orders' => [], + ], + [ + '$id' => ID::custom('_key_trigger'), + 'type' => Database::INDEX_KEY, + 'attributes' => ['trigger'], + 'lengths' => [128], + 'orders' => [Database::ORDER_ASC], + ], + [ + '$id' => ID::custom('_key_status'), + 'type' => Database::INDEX_KEY, + 'attributes' => ['status'], + 'lengths' => [128], + 'orders' => [Database::ORDER_ASC], + ], + [ + '$id' => ID::custom('_key_statusCode'), + 'type' => Database::INDEX_KEY, + 'attributes' => ['statusCode'], + 'lengths' => [], + 'orders' => [Database::ORDER_ASC], + ], + [ + '$id' => ID::custom('_key_duration'), + 'type' => Database::INDEX_KEY, + 'attributes' => ['duration'], + 'lengths' => [], + 'orders' => [Database::ORDER_ASC], + ], + ], + ], + + 'certificates' => [ + '$collection' => ID::custom(Database::METADATA), + '$id' => ID::custom('certificates'), + 'name' => 'Certificates', + 'attributes' => [ + [ + '$id' => ID::custom('domain'), + 'type' => Database::VAR_STRING, + 'format' => '', + // The maximum total length of a domain name or number is 255 characters. + // https://datatracker.ietf.org/doc/html/rfc2821#section-4.5.3.1 + // https://datatracker.ietf.org/doc/html/rfc5321#section-4.5.3.1.2 + 'size' => 255, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('issueDate'), + 'type' => Database::VAR_DATETIME, + 'format' => '', + 'size' => 0, + 'signed' => false, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => ['datetime'], + ], + [ + '$id' => ID::custom('renewDate'), + 'type' => Database::VAR_DATETIME, + 'format' => '', + 'size' => 0, + 'signed' => false, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => ['datetime'], + ], + [ + '$id' => ID::custom('attempts'), + 'type' => Database::VAR_INTEGER, + 'format' => '', + 'size' => 0, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('log'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 16384, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('updated'), + 'type' => Database::VAR_DATETIME, + 'format' => '', + 'size' => 0, + 'signed' => false, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => ['datetime'], + ], + ], + 'indexes' => [ + [ + '$id' => ID::custom('_key_domain'), + 'type' => Database::INDEX_KEY, + 'attributes' => ['domain'], + 'lengths' => [255], + 'orders' => [Database::ORDER_ASC], + ], + ], + ], + + 'buckets' => [ + '$collection' => ID::custom(Database::METADATA), + '$id' => ID::custom('buckets'), + 'name' => 'Buckets', + 'attributes' => [ + [ + '$id' => ID::custom('enabled'), + 'type' => Database::VAR_BOOLEAN, + 'signed' => true, + 'size' => 0, + 'format' => '', + 'filters' => [], + 'required' => true, + 'array' => false, + ], + [ + '$id' => ID::custom('name'), + 'type' => Database::VAR_STRING, + 'signed' => true, + 'size' => 128, + 'format' => '', + 'filters' => [], + 'required' => true, + 'array' => false, + ], + [ + '$id' => ID::custom('fileSecurity'), + 'type' => Database::VAR_BOOLEAN, + 'signed' => true, + 'size' => 1, + 'format' => '', + 'filters' => [], + 'required' => false, + 'array' => false, + ], + [ + '$id' => ID::custom('maximumFileSize'), + 'type' => Database::VAR_INTEGER, + 'signed' => false, + 'size' => 8, + 'format' => '', + 'filters' => [], + 'required' => true, + 'array' => false, + ], + [ + '$id' => ID::custom('allowedFileExtensions'), + 'type' => Database::VAR_STRING, + 'signed' => true, + 'size' => 64, + 'format' => '', + 'filters' => [], + 'required' => true, + 'array' => true, + ], + [ + '$id' => 'compression', + 'type' => Database::VAR_STRING, + 'signed' => true, + 'size' => 10, + 'format' => '', + 'filters' => [], + 'required' => true, + 'array' => false, + ], + [ + '$id' => ID::custom('encryption'), + 'type' => Database::VAR_BOOLEAN, + 'signed' => true, + 'size' => 0, + 'format' => '', + 'filters' => [], + 'required' => true, + 'array' => false, + ], + [ + '$id' => ID::custom('antivirus'), + 'type' => Database::VAR_BOOLEAN, + 'signed' => true, + 'size' => 0, + 'format' => '', + 'filters' => [], + 'required' => true, + 'array' => false, + ], + [ + '$id' => ID::custom('search'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 16384, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + ], + 'indexes' => [ + [ + '$id' => ID::custom('_fulltext_name'), + 'type' => Database::INDEX_FULLTEXT, + 'attributes' => ['name'], + 'lengths' => [], + 'orders' => [Database::ORDER_ASC], + ], + [ + '$id' => ID::custom('_key_search'), + 'type' => Database::INDEX_FULLTEXT, + 'attributes' => ['search'], + 'lengths' => [], + 'orders' => [Database::ORDER_ASC], + ], + [ + '$id' => ID::custom('_key_enabled'), + 'type' => Database::INDEX_KEY, + 'attributes' => ['enabled'], + 'lengths' => [], + 'orders' => [Database::ORDER_ASC], + ], + [ + '$id' => ID::custom('_key_name'), + 'type' => Database::INDEX_KEY, + 'attributes' => ['name'], + 'lengths' => [], + 'orders' => [Database::ORDER_ASC], + ], + [ + '$id' => ID::custom('_key_fileSecurity'), + 'type' => Database::INDEX_KEY, + 'attributes' => ['fileSecurity'], + 'lengths' => [], + 'orders' => [Database::ORDER_ASC], + ], + [ + '$id' => ID::custom('_key_maximumFileSize'), + 'type' => Database::INDEX_KEY, + 'attributes' => ['maximumFileSize'], + 'lengths' => [], + 'orders' => [Database::ORDER_ASC], + ], + [ + '$id' => ID::custom('_key_encryption'), + 'type' => Database::INDEX_KEY, + 'attributes' => ['encryption'], + 'lengths' => [], + 'orders' => [Database::ORDER_ASC], + ], + [ + '$id' => ID::custom('_key_antivirus'), + 'type' => Database::INDEX_KEY, + 'attributes' => ['antivirus'], + 'lengths' => [], + 'orders' => [Database::ORDER_ASC], + ], + ] + ], + + 'stats' => [ + '$collection' => ID::custom(Database::METADATA), + '$id' => ID::custom('stats'), + 'name' => 'Stats', + 'attributes' => [ + [ + '$id' => ID::custom('metric'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 255, + 'signed' => true, + 'required' => true, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('region'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 255, + 'signed' => true, + 'required' => true, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('value'), + 'type' => Database::VAR_INTEGER, + 'format' => '', + 'size' => 8, + 'signed' => false, + 'required' => true, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('time'), + 'type' => Database::VAR_DATETIME, + 'format' => '', + 'size' => 0, + 'signed' => false, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => ['datetime'], + ], + [ + '$id' => ID::custom('period'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 4, + 'signed' => true, + 'required' => true, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('type'), + 'type' => Database::VAR_INTEGER, + 'format' => '', + 'size' => 1, + 'signed' => false, + 'required' => true, + 'default' => 0, // 0 -> count, 1 -> sum + 'array' => false, + 'filters' => [], + ], + ], + 'indexes' => [ + [ + '$id' => ID::custom('_key_time'), + 'type' => Database::INDEX_KEY, + 'attributes' => ['time'], + 'lengths' => [], + 'orders' => [Database::ORDER_DESC], + ], + [ + '$id' => ID::custom('_key_period_time'), + 'type' => Database::INDEX_KEY, + 'attributes' => ['period', 'time'], + 'lengths' => [], + 'orders' => [Database::ORDER_ASC], + ], + [ + '$id' => ID::custom('_key_metric_period_time'), + 'type' => Database::INDEX_KEY, + 'attributes' => ['metric', 'period', 'time'], + 'lengths' => [], + 'orders' => [Database::ORDER_DESC], + ], + ], + ], + + 'realtime' => [ + '$collection' => ID::custom(Database::METADATA), + '$id' => ID::custom('realtime'), + 'name' => 'Realtime Connections', + 'attributes' => [ + [ + '$id' => ID::custom('container'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => Database::LENGTH_KEY, + 'signed' => true, + 'required' => true, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('timestamp'), + 'type' => Database::VAR_DATETIME, + 'format' => '', + 'size' => 0, + 'signed' => false, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => ['datetime'], + ], + [ + '$id' => ID::custom('value'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 16384, + 'signed' => true, + 'required' => true, + 'default' => null, + 'array' => false, + 'filters' => [], //TODO: use json filter + ] + ], + 'indexes' => [ + [ + '$id' => ID::custom('_key_timestamp'), + 'type' => Database::INDEX_KEY, + 'attributes' => ['timestamp'], + 'lengths' => [], + 'orders' => [Database::ORDER_DESC], + ], + ] + ], + 'cache' => [ + '$collection' => Database::METADATA, + '$id' => 'cache', + 'name' => 'Cache', + 'attributes' => [ + [ + '$id' => 'resource', + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 255, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => 'accessedAt', + 'type' => Database::VAR_DATETIME, + 'format' => '', + 'size' => 0, + 'signed' => false, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => ['datetime'], + ], + [ + '$id' => 'signature', + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 255, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + ], + 'indexes' => [ + [ + '$id' => '_key_accessedAt', + 'type' => Database::INDEX_KEY, + 'attributes' => ['accessedAt'], + 'lengths' => [], + 'orders' => [], + ], + [ + '$id' => '_key_resource', + 'type' => Database::INDEX_KEY, + 'attributes' => ['resource'], + 'lengths' => [], + 'orders' => [], + ], + ], + ], + 'files' => [ + '$collection' => ID::custom('buckets'), + '$id' => ID::custom('files'), + '$name' => 'Files', + 'attributes' => [ + [ + 'array' => false, + '$id' => ID::custom('bucketId'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => Database::LENGTH_KEY, + 'signed' => true, + 'required' => false, + 'default' => null, + 'filters' => [], + ], + [ + '$id' => ID::custom('name'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 2048, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('path'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 2048, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('signature'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 2048, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('mimeType'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 255, // https://tools.ietf.org/html/rfc4288#section-4.2 + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('metadata'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 16384, // https://tools.ietf.org/html/rfc4288#section-4.2 + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => ['json'], + ], + [ + '$id' => ID::custom('sizeOriginal'), + 'type' => Database::VAR_INTEGER, + 'format' => '', + 'size' => 8, + 'signed' => false, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('sizeActual'), + 'type' => Database::VAR_INTEGER, + 'format' => '', + 'size' => 8, + 'signed' => false, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('algorithm'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 255, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('comment'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 2048, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('openSSLVersion'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 64, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('openSSLCipher'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 64, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('openSSLTag'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 2048, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('openSSLIV'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 2048, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('chunksTotal'), + 'type' => Database::VAR_INTEGER, + 'format' => '', + 'size' => 0, + 'signed' => false, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('chunksUploaded'), + 'type' => Database::VAR_INTEGER, + 'format' => '', + 'size' => 0, + 'signed' => false, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('search'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 16384, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + ], + 'indexes' => [ + [ + '$id' => ID::custom('_key_search'), + 'type' => Database::INDEX_FULLTEXT, + 'attributes' => ['search'], + 'lengths' => [], + 'orders' => [Database::ORDER_ASC], + ], + [ + '$id' => ID::custom('_key_bucket'), + 'type' => Database::INDEX_KEY, + 'attributes' => ['bucketId'], + 'lengths' => [Database::LENGTH_KEY], + 'orders' => [Database::ORDER_ASC], + ], + [ + '$id' => ID::custom('_key_name'), + 'type' => Database::INDEX_KEY, + 'attributes' => ['name'], + 'lengths' => [768], + 'orders' => [Database::ORDER_ASC], + ], + [ + '$id' => ID::custom('_key_signature'), + 'type' => Database::INDEX_KEY, + 'attributes' => ['signature'], + 'lengths' => [768], + 'orders' => [Database::ORDER_ASC], + ], + [ + '$id' => ID::custom('_key_mimeType'), + 'type' => Database::INDEX_KEY, + 'attributes' => ['mimeType'], + 'lengths' => [], + 'orders' => [Database::ORDER_ASC], + ], + [ + '$id' => ID::custom('_key_sizeOriginal'), + 'type' => Database::INDEX_KEY, + 'attributes' => ['sizeOriginal'], + 'lengths' => [], + 'orders' => [Database::ORDER_ASC], + ], + [ + '$id' => ID::custom('_key_chunksTotal'), + 'type' => Database::INDEX_KEY, + 'attributes' => ['chunksTotal'], + 'lengths' => [], + 'orders' => [Database::ORDER_ASC], + ], + [ + '$id' => ID::custom('_key_chunksUploaded'), + 'type' => Database::INDEX_KEY, + 'attributes' => ['chunksUploaded'], + 'lengths' => [], + 'orders' => [Database::ORDER_ASC], + ], + ] + ], + + 'variables' => [ + '$collection' => Database::METADATA, + '$id' => 'variables', + 'name' => 'variables', + 'attributes' => [ + [ + '$id' => 'functionInternalId', + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => Database::LENGTH_KEY, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => 'functionId', + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => Database::LENGTH_KEY, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => 'key', + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => Database::LENGTH_KEY, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => 'value', + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 8192, + 'signed' => true, + 'required' => true, + 'default' => null, + 'array' => false, + 'filters' => [ 'encrypt' ] + ], + [ + '$id' => ID::custom('search'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 16384, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + ], + 'indexes' => [ + [ + '$id' => '_key_function', + 'type' => Database::INDEX_KEY, + 'attributes' => ['functionInternalId'], + 'lengths' => [Database::LENGTH_KEY], + 'orders' => [Database::ORDER_ASC], + ], + [ + '$id' => '_key_uniqueKey', + 'type' => Database::INDEX_UNIQUE, + 'attributes' => ['functionInternalId', 'key'], + 'lengths' => [Database::LENGTH_KEY, Database::LENGTH_KEY], + 'orders' => [Database::ORDER_ASC, Database::ORDER_ASC], + ], + [ + '$id' => '_key_key', + 'type' => Database::INDEX_KEY, + 'attributes' => ['key'], + 'lengths' => [Database::LENGTH_KEY], + 'orders' => [Database::ORDER_ASC], + ], + [ + '$id' => ID::custom('_fulltext_search'), + 'type' => Database::INDEX_FULLTEXT, + 'attributes' => ['search'], + 'lengths' => [], + 'orders' => [], + ], + ], + ], +]; + +return $collections; From 5722d3cbf254869d298b3563f719a65499b5c57b Mon Sep 17 00:00:00 2001 From: fogelito Date: Tue, 9 May 2023 15:22:55 +0300 Subject: [PATCH 03/43] Index validators testFulltextWithNonString --- tests/Database/Validator/IndexTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/Database/Validator/IndexTest.php b/tests/Database/Validator/IndexTest.php index bca80bb98..d85e680e8 100644 --- a/tests/Database/Validator/IndexTest.php +++ b/tests/Database/Validator/IndexTest.php @@ -111,8 +111,8 @@ public function testFulltextWithNonString(): void '$id' => ID::custom('index1'), 'type' => Database::INDEX_FULLTEXT, 'attributes' => ['title', 'date'], - 'lengths' => [255], - 'orders' => [Database::ORDER_ASC], + 'lengths' => [], + 'orders' => [], ], ], ]; From eb9008b7d5d99cf8606ebcf969338258ba59afa2 Mon Sep 17 00:00:00 2001 From: fogelito Date: Tue, 9 May 2023 16:45:44 +0300 Subject: [PATCH 04/43] Index Length test --- src/Database/Database.php | 8 +- src/Database/Validator/Index.php | 32 +++--- tests/Database/Adapter/MySQLTest.php | 152 ++++++++++++------------- tests/Database/Base.php | 60 +++++++++- tests/Database/Validator/IndexTest.php | 61 ++++++++-- 5 files changed, 207 insertions(+), 106 deletions(-) diff --git a/src/Database/Database.php b/src/Database/Database.php index 774212e20..0006918db 100644 --- a/src/Database/Database.php +++ b/src/Database/Database.php @@ -644,10 +644,10 @@ public function createCollection(string $id, array $attributes = [], array $inde 'documentSecurity' => $documentSecurity ]); - $validator = new IndexValidator(); - if (!$validator->isValid($collection)) { - throw new Exception($validator->getDescription()); - } +// $validator = new IndexValidator(); +// if (!$validator->isValid($collection)) { +// throw new Exception($validator->getDescription()); +// } $this->adapter->createCollection($id, $attributes, $indexes); diff --git a/src/Database/Validator/Index.php b/src/Database/Validator/Index.php index 884739e95..2d443cdfe 100644 --- a/src/Database/Validator/Index.php +++ b/src/Database/Validator/Index.php @@ -9,6 +9,7 @@ class Index extends Validator { protected string $message = 'Invalid Index'; + const MAX = 768; /** * @var array $attributes @@ -63,27 +64,32 @@ public function checkIndexLength(Document $collection): bool foreach ($index->getAttribute('attributes', []) as $ik => $ia){ $attribute = $this->attributes[$ia]; $attributeSize = $attribute->getAttribute('size', 0); + $indexLength = isset($index->getAttribute('lengths')[$ik]) ? $index->getAttribute('lengths')[$ik] : 0; + $indexLength = $indexLength === 0 ? $attributeSize : $indexLength; + + if($indexLength > $attributeSize){ + $this->message = 'Index length is longer than the key part for "'.$ia.'"'; + return false; + } // Todo: find tuning for Index type && Attribute type ... // $index->getAttribute('type') === 'key' - var_dump($attributeSize); var_dump($ia); + var_dump($attributeSize); var_dump($indexLength); - $total += $attributeSize; + $total += $indexLength; + } - if($total > $attributeSize){ - $this->message = 'Index Length is longer that the attribute size (' . $attributeSize . ')'; - return false; - } - if($total > 768){ - $this->message = 'Index Length is longer that the max 768)'; - return false; - } + + if($total > self::MAX){ + $this->message = 'Index Length is longer that the max ('.self::MAX.'))'; + return false; } + } return true; @@ -107,9 +113,9 @@ public function isValid($value): bool return false; } -// if(!$this->checkIndexLength($value)){ -// return false; -// } + if(!$this->checkIndexLength($value)){ + return false; + } return true; } diff --git a/tests/Database/Adapter/MySQLTest.php b/tests/Database/Adapter/MySQLTest.php index fd5cf3232..f0ee5822b 100644 --- a/tests/Database/Adapter/MySQLTest.php +++ b/tests/Database/Adapter/MySQLTest.php @@ -1,77 +1,77 @@ connect('redis', 6379); -// $redis->flushAll(); -// -// $cache = new Cache(new RedisAdapter($redis)); -// -// $database = new Database(new MySQL($pdo), $cache); -// $database->setDefaultDatabase('utopiaTests'); -// $database->setNamespace('myapp_'.uniqid()); -// -// if ($database->exists('utopiaTests')) { -// $database->delete('utopiaTests'); -// } -// -// $database->create(); -// -// return self::$database = $database; -// } -// -// public static function killDatabase(): void -// { -// self::$database = null; -// } -//} + +namespace Utopia\Tests\Adapter; + +use PDO; +use Redis; +use Utopia\Cache\Cache; +use Utopia\Database\Database; +use Utopia\Database\Adapter\MySQL; +use Utopia\Cache\Adapter\Redis as RedisAdapter; +use Utopia\Tests\Base; + +class MySQLTest extends Base +{ + public static ?Database $database = null; + + // TODO@kodumbeats hacky way to identify adapters for tests + // Remove once all methods are implemented + /** + * Return name of adapter + * + * @return string + */ + public static function getAdapterName(): string + { + return "mysql"; + } + + /** + * + * @return int + */ + public static function getUsedIndexes(): int + { + return MySQL::getCountOfDefaultIndexes(); + } + + /** + * @return Database + */ + public static function getDatabase(): Database + { + if (!is_null(self::$database)) { + return self::$database; + } + + $dbHost = 'mysql'; + $dbPort = '3307'; + $dbUser = 'root'; + $dbPass = 'password'; + + $pdo = new PDO("mysql:host={$dbHost};port={$dbPort};charset=utf8mb4", $dbUser, $dbPass, MySQL::getPDOAttributes()); + + $redis = new Redis(); + $redis->connect('redis', 6379); + $redis->flushAll(); + + $cache = new Cache(new RedisAdapter($redis)); + + $database = new Database(new MySQL($pdo), $cache); + $database->setDefaultDatabase('utopiaTests'); + $database->setNamespace('myapp_'.uniqid()); + + if ($database->exists('utopiaTests')) { + $database->delete('utopiaTests'); + } + + $database->create(); + + return self::$database = $database; + } + + public static function killDatabase(): void + { + self::$database = null; + } +} diff --git a/tests/Database/Base.php b/tests/Database/Base.php index 83f8b3027..013f13e9c 100644 --- a/tests/Database/Base.php +++ b/tests/Database/Base.php @@ -5,6 +5,7 @@ use Exception; use InvalidArgumentException; use PHPUnit\Framework\TestCase; +use Throwable; use Utopia\Database\Adapter\SQL; use Utopia\Database\Database; use Utopia\Database\DateTime; @@ -21,6 +22,7 @@ use Utopia\Database\Helpers\Role; use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\Datetime as DatetimeValidator; +use Utopia\Database\Validator\Index; use Utopia\Database\Validator\Structure; use Utopia\Validator\Range; use Utopia\Database\Exception\Structure as StructureException; @@ -75,6 +77,60 @@ public function testCreateExistsDelete(): void $this->assertEquals(true, static::getDatabase()->create()); } + /** + * @throws Exception|Throwable + */ + public function testIndexLength(): void + { + + $collection = new Document([ + '$id' => ID::custom('index_length'), + 'name' => 'test', + 'attributes' => [ + new Document([ + '$id' => ID::custom('title1'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 300, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ]), + new Document([ + '$id' => ID::custom('title2'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 500, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ]), + ], + 'indexes' => [ + new Document([ + '$id' => ID::custom('index1'), + 'type' => Database::INDEX_KEY, + 'attributes' => ['title1', 'title2'], + 'lengths' => [301,100], + 'orders' => [], + ]), + ], + ]); + + $validator = new Index(); + + $this->assertFalse($validator->isValid($collection)); + $this->assertEquals('Index length is longer than the key part for "title1"', $validator->getDescription()); + + $this->assertInstanceOf('Utopia\Database\Document', static::getDatabase()->createCollection($collection->getId(), $collection->getAttribute('attributes'), $collection->getAttribute('indexes'))); + + die; + } + public function testCreatedAtUpdatedAt(): void { $this->assertInstanceOf('Utopia\Database\Document', static::getDatabase()->createCollection('created_at')); @@ -717,7 +773,7 @@ public function testCreateDocumentDefaults(): void } /** - * @throws AuthorizationException|LimitException|DuplicateException|StructureException|Exception|\Throwable + * @throws AuthorizationException|LimitException|DuplicateException|StructureException|Exception|Throwable */ public function testIncreaseDecrease(): Document { @@ -4161,7 +4217,7 @@ public function testOneToOneOneWayRelationship(): void * @throws LimitException * @throws DuplicateException * @throws StructureException - * @throws \Throwable + * @throws Throwable */ public function testOneToOneTwoWayRelationship(): void { diff --git a/tests/Database/Validator/IndexTest.php b/tests/Database/Validator/IndexTest.php index d85e680e8..dca226c87 100644 --- a/tests/Database/Validator/IndexTest.php +++ b/tests/Database/Validator/IndexTest.php @@ -25,7 +25,7 @@ public function tearDown(): void * @return Document * @throws Exception */ - public function convertToCollection(array $collection): Document + public function collectionArrayToDocuments(array $collection): Document { $document = new Document(); @@ -67,7 +67,8 @@ public function testAppwriteCollection(): void $collections = include __DIR__ . '/config/collections.php'; foreach ($collections as $collection) { - $collection = $this->convertToCollection($collection); + $collection = $this->collectionArrayToDocuments($collection); + var_dump($collection); $this->assertTrue($validator->isValid($collection)); } } @@ -79,11 +80,11 @@ public function testFulltextWithNonString(): void { $validator = new Index(); - $collection = [ + $collection = new Document([ '$id' => ID::custom('test'), 'name' => 'test', 'attributes' => [ - [ + new Document([ '$id' => ID::custom('title'), 'type' => Database::VAR_STRING, 'format' => '', @@ -93,8 +94,8 @@ public function testFulltextWithNonString(): void 'default' => null, 'array' => false, 'filters' => [], - ], - [ + ]), + new Document([ '$id' => ID::custom('date'), 'type' => Database::VAR_DATETIME, 'format' => '', @@ -104,23 +105,61 @@ public function testFulltextWithNonString(): void 'default' => null, 'array' => false, 'filters' => ['datetime'], - ], + ]), ], 'indexes' => [ - [ + new Document([ '$id' => ID::custom('index1'), 'type' => Database::INDEX_FULLTEXT, 'attributes' => ['title', 'date'], 'lengths' => [], 'orders' => [], - ], + ]), ], - ]; + ]); - $collection = $this->convertToCollection($collection); $this->assertFalse($validator->isValid($collection)); $this->assertEquals('Attribute "date" cannot be part of a FULLTEXT index', $validator->getDescription()); } + /** + * @throws Exception + */ + public function testIndexLength(): void + { + $validator = new Index(); + + $collection = new Document([ + '$id' => ID::custom('test'), + 'name' => 'test', + 'attributes' => [ + new Document([ + '$id' => ID::custom('title'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 769, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ]), + ], + 'indexes' => [ + new Document([ + '$id' => ID::custom('index1'), + 'type' => Database::INDEX_KEY, + 'attributes' => ['title'], + 'lengths' => [], + 'orders' => [], + ]), + ], + ]); + + $this->assertFalse($validator->isValid($collection)); + $this->assertEquals('Attribute "date" cannot be part of a FULLTEXT index', $validator->getDescription()); + } + + } From dd3c1b4498a2e56240ec73eca8aecc1ba4f88f76 Mon Sep 17 00:00:00 2001 From: fogelito Date: Wed, 10 May 2023 12:11:21 +0300 Subject: [PATCH 05/43] key part validation --- src/Database/Database.php | 8 +-- src/Database/Validator/Index.php | 13 +++- tests/Database/Base.php | 101 ++++++++++++++++++++----------- 3 files changed, 79 insertions(+), 43 deletions(-) diff --git a/src/Database/Database.php b/src/Database/Database.php index 0006918db..774212e20 100644 --- a/src/Database/Database.php +++ b/src/Database/Database.php @@ -644,10 +644,10 @@ public function createCollection(string $id, array $attributes = [], array $inde 'documentSecurity' => $documentSecurity ]); -// $validator = new IndexValidator(); -// if (!$validator->isValid($collection)) { -// throw new Exception($validator->getDescription()); -// } + $validator = new IndexValidator(); + if (!$validator->isValid($collection)) { + throw new Exception($validator->getDescription()); + } $this->adapter->createCollection($id, $attributes, $indexes); diff --git a/src/Database/Validator/Index.php b/src/Database/Validator/Index.php index 2d443cdfe..67a7f0ed4 100644 --- a/src/Database/Validator/Index.php +++ b/src/Database/Validator/Index.php @@ -2,6 +2,7 @@ namespace Utopia\Database\Validator; +use Exception; use Utopia\Database\Database; use Utopia\Validator; use Utopia\Database\Document; @@ -32,14 +33,15 @@ public function getDescription(): string /** * @param Document $collection * @return bool + * @throws Exception */ public function checkFulltextIndexNonString(Document $collection): bool { foreach ($collection->getAttribute('indexes', []) as $index){ if($index->getAttribute('type') === Database::INDEX_FULLTEXT){ foreach ($index->getAttribute('attributes', []) as $ia) { - $attribute = $this->attributes[$ia]; - if ($attribute->getAttribute('type') !== Database::VAR_STRING) { + $attribute = $this->attributes[$ia] ?? new Document([]); + if ($attribute->getAttribute('type', '') !== Database::VAR_STRING) { $this->message = 'Attribute "'.$attribute->getAttribute('key', $attribute->getAttribute('$id')).'" cannot be part of a FULLTEXT index'; return false; } @@ -62,6 +64,11 @@ public function checkIndexLength(Document $collection): bool $total = 0; foreach ($index->getAttribute('attributes', []) as $ik => $ia){ + // Todo take care Internals... + if(in_array($ia, ['$id', '$createdAt', '$updatedAt'])){ + continue; + } + $attribute = $this->attributes[$ia]; $attributeSize = $attribute->getAttribute('size', 0); @@ -69,7 +76,7 @@ public function checkIndexLength(Document $collection): bool $indexLength = $indexLength === 0 ? $attributeSize : $indexLength; if($indexLength > $attributeSize){ - $this->message = 'Index length is longer than the key part for "'.$ia.'"'; + $this->message = 'Index length("'.$indexLength.'") is longer than the key part for "'.$ia.'("'.$attributeSize.'")"'; return false; } diff --git a/tests/Database/Base.php b/tests/Database/Base.php index 013f13e9c..85690d8c2 100644 --- a/tests/Database/Base.php +++ b/tests/Database/Base.php @@ -82,53 +82,82 @@ public function testCreateExistsDelete(): void */ public function testIndexLength(): void { + $attributes = [ + new Document([ + '$id' => ID::custom('title1'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 300, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ]), + new Document([ + '$id' => ID::custom('title2'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 500, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ]), + ]; + + $indexes = [ + new Document([ + '$id' => ID::custom('index1'), + 'type' => Database::INDEX_KEY, + 'attributes' => ['title1', 'title2'], + 'lengths' => [301,100], + 'orders' => [], + ]), + ]; $collection = new Document([ '$id' => ID::custom('index_length'), 'name' => 'test', - 'attributes' => [ - new Document([ - '$id' => ID::custom('title1'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 300, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ]), - new Document([ - '$id' => ID::custom('title2'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 500, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ]), - ], - 'indexes' => [ - new Document([ - '$id' => ID::custom('index1'), - 'type' => Database::INDEX_KEY, - 'attributes' => ['title1', 'title2'], - 'lengths' => [301,100], - 'orders' => [], - ]), - ], + 'attributes' => $attributes, + 'indexes' => $indexes ]); $validator = new Index(); $this->assertFalse($validator->isValid($collection)); - $this->assertEquals('Index length is longer than the key part for "title1"', $validator->getDescription()); + $this->assertEquals('Index length("301") is longer than the key part for "title1("300")"', $validator->getDescription()); - $this->assertInstanceOf('Utopia\Database\Document', static::getDatabase()->createCollection($collection->getId(), $collection->getAttribute('attributes'), $collection->getAttribute('indexes'))); + try { + static::getDatabase()->createCollection($collection->getId(), $attributes, $indexes); + $this->fail('Failed to throw exception'); + } catch (Exception $e) { + $this->assertEquals('Index length is longer than the key part for "title1"', $e->getMessage()); + } + + $indexes = [ + new Document([ + '$id' => ID::custom('index1'), + 'type' => Database::INDEX_KEY, + 'attributes' => ['title1', 'title2'], + 'lengths' => [300], + 'orders' => [], + ]), + ]; + + $collection->setAttribute('indexes', $indexes); + + $this->assertFalse($validator->isValid($collection)); + $this->assertEquals('Index Length is longer that the max (768))', $validator->getDescription()); + + try { + static::getDatabase()->createCollection($collection->getId(), $attributes, $indexes); + $this->fail('Failed to throw exception'); + } catch (Exception $e) { + $this->assertEquals('Index Length is longer that the max (768))', $e->getMessage()); + } - die; } public function testCreatedAtUpdatedAt(): void From 67c049d90acb8abe12b1f599308345823d3dae8f Mon Sep 17 00:00:00 2001 From: fogelito Date: Wed, 10 May 2023 15:11:01 +0300 Subject: [PATCH 06/43] Attribute "integer" cannot be part of a FULLTEXT index --- tests/Database/Base.php | 52 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 47 insertions(+), 5 deletions(-) diff --git a/tests/Database/Base.php b/tests/Database/Base.php index 85690d8c2..b67173aba 100644 --- a/tests/Database/Base.php +++ b/tests/Database/Base.php @@ -80,7 +80,7 @@ public function testCreateExistsDelete(): void /** * @throws Exception|Throwable */ - public function testIndexLength(): void + public function testIndexValidation(): void { $attributes = [ new Document([ @@ -126,14 +126,15 @@ public function testIndexLength(): void $validator = new Index(); + $errorMessage = 'Index length("301") is longer than the key part for "title1("300")"'; $this->assertFalse($validator->isValid($collection)); - $this->assertEquals('Index length("301") is longer than the key part for "title1("300")"', $validator->getDescription()); + $this->assertEquals($errorMessage, $validator->getDescription()); try { static::getDatabase()->createCollection($collection->getId(), $attributes, $indexes); $this->fail('Failed to throw exception'); } catch (Exception $e) { - $this->assertEquals('Index length is longer than the key part for "title1"', $e->getMessage()); + $this->assertEquals($errorMessage, $e->getMessage()); } $indexes = [ @@ -148,14 +149,55 @@ public function testIndexLength(): void $collection->setAttribute('indexes', $indexes); + $errorMessage = 'Index Length is longer that the max (768))'; $this->assertFalse($validator->isValid($collection)); - $this->assertEquals('Index Length is longer that the max (768))', $validator->getDescription()); + $this->assertEquals($errorMessage, $validator->getDescription()); try { static::getDatabase()->createCollection($collection->getId(), $attributes, $indexes); $this->fail('Failed to throw exception'); } catch (Exception $e) { - $this->assertEquals('Index Length is longer that the max (768))', $e->getMessage()); + $this->assertEquals($errorMessage, $e->getMessage()); + } + + $attributes[] = new Document([ + '$id' => ID::custom('integer'), + 'type' => Database::VAR_INTEGER, + 'format' => '', + 'size' => 10000, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ]); + + $indexes = [ + new Document([ + '$id' => ID::custom('index1'), + 'type' => Database::INDEX_FULLTEXT, + 'attributes' => ['title1', 'integer'], + 'lengths' => [], + 'orders' => [], + ]), + ]; + + $collection = new Document([ + '$id' => ID::custom('index_length'), + 'name' => 'test', + 'attributes' => $attributes, + 'indexes' => $indexes + ]); + + $errorMessage = 'Attribute "integer" cannot be part of a FULLTEXT index'; + $this->assertFalse($validator->isValid($collection)); + $this->assertEquals($errorMessage, $validator->getDescription()); + + try { + static::getDatabase()->createCollection($collection->getId(), $attributes, $indexes); + $this->fail('Failed to throw exception'); + } catch (Exception $e) { + $this->assertEquals($errorMessage, $e->getMessage()); } } From b81e0913b8af5e4c7ef4bea1db57a51766382872 Mon Sep 17 00:00:00 2001 From: fogelito Date: Wed, 10 May 2023 15:49:20 +0300 Subject: [PATCH 07/43] Update 16381 from 16383 for mysql limit --- src/Database/Adapter/MariaDB.php | 2 +- src/Database/Adapter/Postgres.php | 2 +- src/Database/Adapter/SQL.php | 2 +- tests/Database/Base.php | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Database/Adapter/MariaDB.php b/src/Database/Adapter/MariaDB.php index 6ea81b1c3..043e5c1d3 100644 --- a/src/Database/Adapter/MariaDB.php +++ b/src/Database/Adapter/MariaDB.php @@ -1276,7 +1276,7 @@ protected function getSQLType(string $type, int $size, bool $signed = true): str return 'MEDIUMTEXT'; } - if ($size > 16383) { + if ($size > 16381) { // 16381 mysql vs 16383 maria return 'TEXT'; } diff --git a/src/Database/Adapter/Postgres.php b/src/Database/Adapter/Postgres.php index 7a9a20ebd..f2ee376f9 100644 --- a/src/Database/Adapter/Postgres.php +++ b/src/Database/Adapter/Postgres.php @@ -1269,7 +1269,7 @@ protected function getSQLType(string $type, int $size, bool $signed = true): str switch ($type) { case Database::VAR_STRING: // $size = $size * 4; // Convert utf8mb4 size to bytes - if ($size > 16383) { + if ($size > 16381) { return 'TEXT'; } diff --git a/src/Database/Adapter/SQL.php b/src/Database/Adapter/SQL.php index 3ccb41c8d..74616f676 100644 --- a/src/Database/Adapter/SQL.php +++ b/src/Database/Adapter/SQL.php @@ -313,7 +313,7 @@ public function getAttributeWidth(Document $collection): int $total += 11; break; - case ($attribute['size'] > 16383): + case ($attribute['size'] > 16381): // 8 bytes length + 2 bytes for TEXT $total += 10; break; diff --git a/tests/Database/Base.php b/tests/Database/Base.php index b67173aba..6ecdbcb90 100644 --- a/tests/Database/Base.php +++ b/tests/Database/Base.php @@ -251,7 +251,7 @@ public function testCreateDeleteAttribute(): void static::getDatabase()->createCollection('attributes'); $this->assertEquals(true, static::getDatabase()->createAttribute('attributes', 'string1', Database::VAR_STRING, 128, true)); - $this->assertEquals(true, static::getDatabase()->createAttribute('attributes', 'string2', Database::VAR_STRING, 16383 + 1, true)); + $this->assertEquals(true, static::getDatabase()->createAttribute('attributes', 'string2', Database::VAR_STRING, 16381 + 1, true)); $this->assertEquals(true, static::getDatabase()->createAttribute('attributes', 'string3', Database::VAR_STRING, 65535 + 1, true)); $this->assertEquals(true, static::getDatabase()->createAttribute('attributes', 'string4', Database::VAR_STRING, 16777215 + 1, true)); $this->assertEquals(true, static::getDatabase()->createAttribute('attributes', 'integer', Database::VAR_INTEGER, 0, true)); From 6bebec975b53b1d6ce556091ff08126c899df188 Mon Sep 17 00:00:00 2001 From: fogelito Date: Thu, 11 May 2023 14:04:14 +0300 Subject: [PATCH 08/43] find tuning for Index type --- src/Database/Validator/Index.php | 69 ++++++++++++++++++++------ tests/Database/Validator/IndexTest.php | 44 +++++++++++++++- 2 files changed, 95 insertions(+), 18 deletions(-) diff --git a/src/Database/Validator/Index.php b/src/Database/Validator/Index.php index 67a7f0ed4..b4356dd3d 100644 --- a/src/Database/Validator/Index.php +++ b/src/Database/Validator/Index.php @@ -10,7 +10,7 @@ class Index extends Validator { protected string $message = 'Invalid Index'; - const MAX = 768; + const MAX = 768; // 3072 bytes / mb4 /** * @var array $attributes @@ -30,6 +30,23 @@ public function getDescription(): string return $this->message; } + /** + * @param Document $collection + * @return bool + * @throws Exception + */ + public function checkEmptyAttributes(Document $collection): bool + { + foreach ($collection->getAttribute('indexes', []) as $index){ + if(empty($index->getAttribute('attributes', []))){ + $this->message = 'No attributes provided for index'; + return false; + } + } + + return true; + } + /** * @param Document $collection * @return bool @@ -58,40 +75,56 @@ public function checkFulltextIndexNonString(Document $collection): bool */ public function checkIndexLength(Document $collection): bool { - foreach ($collection->getAttribute('indexes', []) as $index){ + if($index->getAttribute('type') === Database::INDEX_FULLTEXT){ + return true; + } + $total = 0; foreach ($index->getAttribute('attributes', []) as $ik => $ia){ - // Todo take care Internals... + + // Todo Add internals to attributes collection... if(in_array($ia, ['$id', '$createdAt', '$updatedAt'])){ continue; } $attribute = $this->attributes[$ia]; - $attributeSize = $attribute->getAttribute('size', 0); - - $indexLength = isset($index->getAttribute('lengths')[$ik]) ? $index->getAttribute('lengths')[$ik] : 0; - $indexLength = $indexLength === 0 ? $attributeSize : $indexLength; - if($indexLength > $attributeSize){ - $this->message = 'Index length("'.$indexLength.'") is longer than the key part for "'.$ia.'("'.$attributeSize.'")"'; - return false; + var_dump($attribute->getAttribute('type')); + + // Todo: find tuning for Index type && Attribute type .. + switch ($attribute->getAttribute('type')) { + case Database::VAR_STRING: + $attributeSize = $attribute->getAttribute('size', 0); + $indexLength = isset($index->getAttribute('lengths')[$ik]) ? $index->getAttribute('lengths')[$ik] : 0; + $indexLength = $indexLength === 0 ? $attributeSize : $indexLength; + break; + + case Database::VAR_FLOAT: + $attributeSize = 2; // 8 bytes / 4 mb4 + $indexLength = 2; + break; + + default: + $attributeSize = 1; // 4 bytes / 4 mb4 + $indexLength = 1; + break; } - // Todo: find tuning for Index type && Attribute type ... - // $index->getAttribute('type') === 'key' - var_dump($ia); var_dump($attributeSize); var_dump($indexLength); + if($indexLength > $attributeSize){ + $this->message = 'Index length("'.$indexLength.'") is longer than the key part for "'.$ia.'("'.$attributeSize.'")"'; + return false; + } + $total += $indexLength; } - - if($total > self::MAX){ $this->message = 'Index Length is longer that the max ('.self::MAX.'))'; return false; @@ -100,7 +133,6 @@ public function checkIndexLength(Document $collection): bool } return true; - } /** @@ -109,6 +141,7 @@ public function checkIndexLength(Document $collection): bool * Returns true index if valid. * @param Document $value * @return bool + * @throws Exception */ public function isValid($value): bool { @@ -116,6 +149,10 @@ public function isValid($value): bool $this->attributes[$attribute->getAttribute('$id', $value->getAttribute('key'))] = $attribute; } + if(!$this->checkEmptyAttributes($value)){ + return false; + } + if(!$this->checkFulltextIndexNonString($value)){ return false; } diff --git a/tests/Database/Validator/IndexTest.php b/tests/Database/Validator/IndexTest.php index dca226c87..131b9ed1b 100644 --- a/tests/Database/Validator/IndexTest.php +++ b/tests/Database/Validator/IndexTest.php @@ -61,6 +61,9 @@ public function collectionArrayToDocuments(array $collection): Document */ public function testAppwriteCollection(): void { + + // Todo: Move this test to Appwrite... + $validator = new Index(); /** @var array $configs */ @@ -68,7 +71,6 @@ public function testAppwriteCollection(): void foreach ($collections as $collection) { $collection = $this->collectionArrayToDocuments($collection); - var_dump($collection); $this->assertTrue($validator->isValid($collection)); } } @@ -158,7 +160,45 @@ public function testIndexLength(): void ]); $this->assertFalse($validator->isValid($collection)); - $this->assertEquals('Attribute "date" cannot be part of a FULLTEXT index', $validator->getDescription()); + $this->assertEquals('Index Length is longer that the max (768))', $validator->getDescription()); + } + + /** + * @throws Exception + */ + public function testEmptyAttributes(): void + { + $validator = new Index(); + + $collection = new Document([ + '$id' => ID::custom('test'), + 'name' => 'test', + 'attributes' => [ + new Document([ + '$id' => ID::custom('title'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 769, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ]), + ], + 'indexes' => [ + new Document([ + '$id' => ID::custom('index1'), + 'type' => Database::INDEX_KEY, + 'attributes' => [], + 'lengths' => [], + 'orders' => [], + ]), + ], + ]); + + $this->assertFalse($validator->isValid($collection)); + $this->assertEquals('No attributes provided for index', $validator->getDescription()); } From ae11c9a0163dd81b426ada9c6bf3e8cb370bf00a Mon Sep 17 00:00:00 2001 From: fogelito Date: Sun, 14 May 2023 11:43:29 +0300 Subject: [PATCH 09/43] Internal ids --- src/Database/Validator/Index.php | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/src/Database/Validator/Index.php b/src/Database/Validator/Index.php index b4356dd3d..803b46c51 100644 --- a/src/Database/Validator/Index.php +++ b/src/Database/Validator/Index.php @@ -35,7 +35,7 @@ public function getDescription(): string * @return bool * @throws Exception */ - public function checkEmptyAttributes(Document $collection): bool + public function checkEmptyIndexAttributes(Document $collection): bool { foreach ($collection->getAttribute('indexes', []) as $index){ if(empty($index->getAttribute('attributes', []))){ @@ -82,19 +82,9 @@ public function checkIndexLength(Document $collection): bool } $total = 0; - foreach ($index->getAttribute('attributes', []) as $ik => $ia){ - - // Todo Add internals to attributes collection... - if(in_array($ia, ['$id', '$createdAt', '$updatedAt'])){ - continue; - } - $attribute = $this->attributes[$ia]; - var_dump($attribute->getAttribute('type')); - - // Todo: find tuning for Index type && Attribute type .. switch ($attribute->getAttribute('type')) { case Database::VAR_STRING: $attributeSize = $attribute->getAttribute('size', 0); @@ -149,7 +139,22 @@ public function isValid($value): bool $this->attributes[$attribute->getAttribute('$id', $value->getAttribute('key'))] = $attribute; } - if(!$this->checkEmptyAttributes($value)){ + $this->attributes['$id'] = new Document([ + 'type' => Database::VAR_STRING, + 'size' => Database::LENGTH_KEY, + ]); + + $this->attributes['$createdAt'] = new Document([ + 'type' => Database::VAR_DATETIME, + 'size' => Database::LENGTH_KEY, + ]); + + $this->attributes['$updatedAt'] = new Document([ + 'type' => Database::VAR_DATETIME, + 'size' => Database::LENGTH_KEY, + ]); + + if(!$this->checkEmptyIndexAttributes($value)){ return false; } From fea3bf4c634b91f8a882cd809957781811f051f7 Mon Sep 17 00:00:00 2001 From: fogelito Date: Sun, 14 May 2023 12:20:16 +0300 Subject: [PATCH 10/43] lint + sqlite fulltext support + tests unremarked --- src/Database/Validator/Index.php | 43 +++-- tests/Database/Adapter/MariaDBTest.php | 130 +++++++-------- tests/Database/Adapter/MongoDBTest.php | 204 ++++++++++++------------ tests/Database/Adapter/PostgresTest.php | 126 +++++++-------- tests/Database/Adapter/SQLiteTest.php | 146 ++++++++--------- tests/Database/Base.php | 6 +- tests/Database/Validator/IndexTest.php | 9 +- 7 files changed, 329 insertions(+), 335 deletions(-) diff --git a/src/Database/Validator/Index.php b/src/Database/Validator/Index.php index 803b46c51..31179f276 100644 --- a/src/Database/Validator/Index.php +++ b/src/Database/Validator/Index.php @@ -10,7 +10,7 @@ class Index extends Validator { protected string $message = 'Invalid Index'; - const MAX = 768; // 3072 bytes / mb4 + public const MAX = 768; // 3072 bytes / mb4 /** * @var array $attributes @@ -37,8 +37,8 @@ public function getDescription(): string */ public function checkEmptyIndexAttributes(Document $collection): bool { - foreach ($collection->getAttribute('indexes', []) as $index){ - if(empty($index->getAttribute('attributes', []))){ + foreach ($collection->getAttribute('indexes', []) as $index) { + if (empty($index->getAttribute('attributes', []))) { $this->message = 'No attributes provided for index'; return false; } @@ -54,8 +54,8 @@ public function checkEmptyIndexAttributes(Document $collection): bool */ public function checkFulltextIndexNonString(Document $collection): bool { - foreach ($collection->getAttribute('indexes', []) as $index){ - if($index->getAttribute('type') === Database::INDEX_FULLTEXT){ + foreach ($collection->getAttribute('indexes', []) as $index) { + if ($index->getAttribute('type') === Database::INDEX_FULLTEXT) { foreach ($index->getAttribute('attributes', []) as $ia) { $attribute = $this->attributes[$ia] ?? new Document([]); if ($attribute->getAttribute('type', '') !== Database::VAR_STRING) { @@ -75,21 +75,21 @@ public function checkFulltextIndexNonString(Document $collection): bool */ public function checkIndexLength(Document $collection): bool { - foreach ($collection->getAttribute('indexes', []) as $index){ - - if($index->getAttribute('type') === Database::INDEX_FULLTEXT){ + foreach ($collection->getAttribute('indexes', []) as $index) { + if ($index->getAttribute('type') === Database::INDEX_FULLTEXT) { return true; } $total = 0; - foreach ($index->getAttribute('attributes', []) as $ik => $ia){ - $attribute = $this->attributes[$ia]; + $lengths = $index->getAttribute('lengths', []); + + foreach ($index->getAttribute('attributes', []) as $ik => $attributeKey) { + $attribute = $this->attributes[$attributeKey]; switch ($attribute->getAttribute('type')) { case Database::VAR_STRING: $attributeSize = $attribute->getAttribute('size', 0); - $indexLength = isset($index->getAttribute('lengths')[$ik]) ? $index->getAttribute('lengths')[$ik] : 0; - $indexLength = $indexLength === 0 ? $attributeSize : $indexLength; + $indexLength = $lengths[$ik] ?? $attributeSize; break; case Database::VAR_FLOAT: @@ -103,23 +103,18 @@ public function checkIndexLength(Document $collection): bool break; } - var_dump($ia); - var_dump($attributeSize); - var_dump($indexLength); - - if($indexLength > $attributeSize){ - $this->message = 'Index length("'.$indexLength.'") is longer than the key part for "'.$ia.'("'.$attributeSize.'")"'; + if ($indexLength > $attributeSize) { + $this->message = 'Index length("'.$indexLength.'") is longer than the key part for "'.$attributeKey.'("'.$attributeSize.'")"'; return false; } $total += $indexLength; } - if($total > self::MAX){ + if ($total > self::MAX) { $this->message = 'Index Length is longer that the max ('.self::MAX.'))'; return false; } - } return true; @@ -135,7 +130,7 @@ public function checkIndexLength(Document $collection): bool */ public function isValid($value): bool { - foreach ($value->getAttribute('attributes', []) as $attribute){ + foreach ($value->getAttribute('attributes', []) as $attribute) { $this->attributes[$attribute->getAttribute('$id', $value->getAttribute('key'))] = $attribute; } @@ -154,15 +149,15 @@ public function isValid($value): bool 'size' => Database::LENGTH_KEY, ]); - if(!$this->checkEmptyIndexAttributes($value)){ + if (!$this->checkEmptyIndexAttributes($value)) { return false; } - if(!$this->checkFulltextIndexNonString($value)){ + if (!$this->checkFulltextIndexNonString($value)) { return false; } - if(!$this->checkIndexLength($value)){ + if (!$this->checkIndexLength($value)) { return false; } diff --git a/tests/Database/Adapter/MariaDBTest.php b/tests/Database/Adapter/MariaDBTest.php index f14b2882f..9cc3fc781 100644 --- a/tests/Database/Adapter/MariaDBTest.php +++ b/tests/Database/Adapter/MariaDBTest.php @@ -1,66 +1,66 @@ connect('redis', 6379); -// $redis->flushAll(); -// $cache = new Cache(new RedisAdapter($redis)); -// -// $database = new Database(new MariaDB($pdo), $cache); -// $database->setDefaultDatabase('utopiaTests'); -// $database->setNamespace('myapp_'.uniqid()); -// -// if ($database->exists('utopiaTests')) { -// $database->delete('utopiaTests'); -// } -// -// $database->create(); -// -// return self::$database = $database; -// } -// -// public static function killDatabase(): void -// { -// self::$database = null; -// } -//} + +namespace Utopia\Tests\Adapter; + +use PDO; +use Redis; +use Utopia\Database\Database; +use Utopia\Database\Adapter\MariaDB; +use Utopia\Cache\Cache; +use Utopia\Cache\Adapter\Redis as RedisAdapter; +use Utopia\Tests\Base; + +class MariaDBTest extends Base +{ + public static ?Database $database = null; + + // TODO@kodumbeats hacky way to identify adapters for tests + // Remove once all methods are implemented + /** + * Return name of adapter + * + * @return string + */ + public static function getAdapterName(): string + { + return "mariadb"; + } + + /** + * @return Database + */ + public static function getDatabase(): Database + { + if (!is_null(self::$database)) { + return self::$database; + } + + $dbHost = 'mariadb'; + $dbPort = '3306'; + $dbUser = 'root'; + $dbPass = 'password'; + + $pdo = new PDO("mysql:host={$dbHost};port={$dbPort};charset=utf8mb4", $dbUser, $dbPass, MariaDB::getPDOAttributes()); + $redis = new Redis(); + $redis->connect('redis', 6379); + $redis->flushAll(); + $cache = new Cache(new RedisAdapter($redis)); + + $database = new Database(new MariaDB($pdo), $cache); + $database->setDefaultDatabase('utopiaTests'); + $database->setNamespace('myapp_'.uniqid()); + + if ($database->exists('utopiaTests')) { + $database->delete('utopiaTests'); + } + + $database->create(); + + return self::$database = $database; + } + + public static function killDatabase(): void + { + self::$database = null; + } +} diff --git a/tests/Database/Adapter/MongoDBTest.php b/tests/Database/Adapter/MongoDBTest.php index 646318a71..563837006 100644 --- a/tests/Database/Adapter/MongoDBTest.php +++ b/tests/Database/Adapter/MongoDBTest.php @@ -1,103 +1,103 @@ connect('redis', 6379); -// $redis->flushAll(); -// $cache = new Cache(new RedisAdapter($redis)); -// -// $schema = 'utopiaTests'; // same as $this->testDatabase -// $client = new Client( -// $schema, -// 'mongo', -// 27017, -// 'root', -// 'example', -// false -// ); -// -// $database = new Database(new Mongo($client), $cache); -// $database->setDefaultDatabase($schema); -// $database->setNamespace('myapp_' . uniqid()); -// -// if ($database->exists('utopiaTests')) { -// $database->delete('utopiaTests'); -// } -// -// $database->create(); -// -// return self::$database = $database; -// } -// -// /** -// * @throws Exception -// */ -// public function testCreateExistsDelete(): void -// { -// // Mongo creates databases on the fly, so exists would always pass. So we override this test to remove the exists check. -// $this->assertNotNull(static::getDatabase()->create()); -// $this->assertEquals(true, static::getDatabase()->delete($this->testDatabase)); -// $this->assertEquals(true, static::getDatabase()->create()); -// $this->assertEquals(true, static::getDatabase()->setDefaultDatabase($this->testDatabase)); -// } -// -// public function testRenameAttribute(): void -// { -// $this->assertTrue(true); -// } -// -// public function testRenameAttributeExisting(): void -// { -// $this->assertTrue(true); -// } -// -// public function testUpdateAttributeStructure(): void -// { -// $this->assertTrue(true); -// } -// -// public function testKeywords(): void -// { -// $this->assertTrue(true); -// } -// -// public static function killDatabase(): void -// { -// self::$database = null; -// } -//} + +namespace Utopia\Tests\Adapter; + +use Exception; +use Redis; +use Utopia\Cache\Cache; +use Utopia\Cache\Adapter\Redis as RedisAdapter; +use Utopia\Database\Adapter\Mongo; +use Utopia\Database\Database; +use Utopia\Mongo\Client; +use Utopia\Tests\Base; + +class MongoDBTest extends Base +{ + public static ?Database $database = null; + + + /** + * Return name of adapter + * + * @return string + */ + public static function getAdapterName(): string + { + return "mongodb"; + } + + /** + * @return Database + * @throws Exception + */ + public static function getDatabase(): Database + { + if (!is_null(self::$database)) { + return self::$database; + } + + $redis = new Redis(); + $redis->connect('redis', 6379); + $redis->flushAll(); + $cache = new Cache(new RedisAdapter($redis)); + + $schema = 'utopiaTests'; // same as $this->testDatabase + $client = new Client( + $schema, + 'mongo', + 27017, + 'root', + 'example', + false + ); + + $database = new Database(new Mongo($client), $cache); + $database->setDefaultDatabase($schema); + $database->setNamespace('myapp_' . uniqid()); + + if ($database->exists('utopiaTests')) { + $database->delete('utopiaTests'); + } + + $database->create(); + + return self::$database = $database; + } + + /** + * @throws Exception + */ + public function testCreateExistsDelete(): void + { + // Mongo creates databases on the fly, so exists would always pass. So we override this test to remove the exists check. + $this->assertNotNull(static::getDatabase()->create()); + $this->assertEquals(true, static::getDatabase()->delete($this->testDatabase)); + $this->assertEquals(true, static::getDatabase()->create()); + $this->assertEquals(true, static::getDatabase()->setDefaultDatabase($this->testDatabase)); + } + + public function testRenameAttribute(): void + { + $this->assertTrue(true); + } + + public function testRenameAttributeExisting(): void + { + $this->assertTrue(true); + } + + public function testUpdateAttributeStructure(): void + { + $this->assertTrue(true); + } + + public function testKeywords(): void + { + $this->assertTrue(true); + } + + public static function killDatabase(): void + { + self::$database = null; + } +} diff --git a/tests/Database/Adapter/PostgresTest.php b/tests/Database/Adapter/PostgresTest.php index a635ef7f0..4b5451341 100644 --- a/tests/Database/Adapter/PostgresTest.php +++ b/tests/Database/Adapter/PostgresTest.php @@ -1,64 +1,64 @@ connect('redis', 6379); -// $redis->flushAll(); -// $cache = new Cache(new RedisAdapter($redis)); -// -// $database = new Database(new Postgres($pdo), $cache); -// $database->setDefaultDatabase('utopiaTests'); -// $database->setNamespace('myapp_'.uniqid()); -// -// if ($database->exists('utopiaTests')) { -// $database->delete('utopiaTests'); -// } -// -// $database->create(); -// -// return self::$database = $database; -// } -// -// public static function killDatabase(): void -// { -// self::$database = null; -// } -//} + +namespace Utopia\Tests\Adapter; + +use PDO; +use Redis; +use Utopia\Database\Database; +use Utopia\Database\Adapter\Postgres; +use Utopia\Cache\Cache; +use Utopia\Cache\Adapter\Redis as RedisAdapter; +use Utopia\Tests\Base; + +class PostgresTest extends Base +{ + public static ?Database $database = null; + + /** + * Return name of adapter + * + * @return string + */ + public static function getAdapterName(): string + { + return "postgres"; + } + + /** + * @reture Adapter + */ + public static function getDatabase(): Database + { + if (!is_null(self::$database)) { + return self::$database; + } + + $dbHost = 'postgres'; + $dbPort = '5432'; + $dbUser = 'root'; + $dbPass = 'password'; + + $pdo = new PDO("pgsql:host={$dbHost};port={$dbPort};", $dbUser, $dbPass, Postgres::getPDOAttributes()); + $redis = new Redis(); + $redis->connect('redis', 6379); + $redis->flushAll(); + $cache = new Cache(new RedisAdapter($redis)); + + $database = new Database(new Postgres($pdo), $cache); + $database->setDefaultDatabase('utopiaTests'); + $database->setNamespace('myapp_'.uniqid()); + + if ($database->exists('utopiaTests')) { + $database->delete('utopiaTests'); + } + + $database->create(); + + return self::$database = $database; + } + + public static function killDatabase(): void + { + self::$database = null; + } +} diff --git a/tests/Database/Adapter/SQLiteTest.php b/tests/Database/Adapter/SQLiteTest.php index 7afc3b63d..b704d58e1 100644 --- a/tests/Database/Adapter/SQLiteTest.php +++ b/tests/Database/Adapter/SQLiteTest.php @@ -1,74 +1,74 @@ connect('redis', 6379); -// $redis->flushAll(); -// -// $cache = new Cache(new RedisAdapter($redis)); -// -// $database = new Database(new SQLite($pdo), $cache); -// $database->setDefaultDatabase('utopiaTests'); -// $database->setNamespace('myapp_'.uniqid()); -// -// return self::$database = $database; -// } -// -// public static function killDatabase(): void -// { -// self::$database = null; -// } -//} + +namespace Utopia\Tests\Adapter; + +use PDO; +use Redis; +use Utopia\Cache\Cache; +use Utopia\Database\Database; +use Utopia\Cache\Adapter\Redis as RedisAdapter; +use Utopia\Database\Adapter\SQLite; +use Utopia\Tests\Base; + +class SQLiteTest extends Base +{ + public static ?Database $database = null; + + // TODO@kodumbeats hacky way to identify adapters for tests + // Remove once all methods are implemented + /** + * Return name of adapter + * + * @return string + */ + public static function getAdapterName(): string + { + return "sqlite"; + } + + /** + * + * @return int + */ + public static function getUsedIndexes(): int + { + return SQLite::getCountOfDefaultIndexes(); + } + + /** + * @return Database + */ + public static function getDatabase(): Database + { + if (!is_null(self::$database)) { + return self::$database; + } + + $sqliteDir = __DIR__."/database.sql"; + + if (file_exists($sqliteDir)) { + unlink($sqliteDir); + } + + $dsn = $sqliteDir; + $dsn = 'memory'; // Overwrite for fast tests + $pdo = new PDO("sqlite:" . $dsn, null, null, SQLite::getPDOAttributes()); + + $redis = new Redis(); + $redis->connect('redis', 6379); + $redis->flushAll(); + + $cache = new Cache(new RedisAdapter($redis)); + + $database = new Database(new SQLite($pdo), $cache); + $database->setDefaultDatabase('utopiaTests'); + $database->setNamespace('myapp_'.uniqid()); + + return self::$database = $database; + } + + public static function killDatabase(): void + { + self::$database = null; + } +} diff --git a/tests/Database/Base.php b/tests/Database/Base.php index 6ecdbcb90..b5812ff73 100644 --- a/tests/Database/Base.php +++ b/tests/Database/Base.php @@ -199,7 +199,6 @@ public function testIndexValidation(): void } catch (Exception $e) { $this->assertEquals($errorMessage, $e->getMessage()); } - } public function testCreatedAtUpdatedAt(): void @@ -967,6 +966,11 @@ public function testGetDocumentSelect(Document $document): Document */ public function testFulltextIndexWithInteger(): void { + if (!$this->getDatabase()->getAdapter()->getSupportForFulltextIndex()) { + $this->expectNotToPerformAssertions(); + return; + } + $this->expectException(Exception::class); $this->expectExceptionMessage('Attribute "integer" cannot be part of a FULLTEXT index'); static::getDatabase()->createIndex('documents', 'fulltext_integer', Database::INDEX_FULLTEXT, ['string','integer']); diff --git a/tests/Database/Validator/IndexTest.php b/tests/Database/Validator/IndexTest.php index 131b9ed1b..2744f44b2 100644 --- a/tests/Database/Validator/IndexTest.php +++ b/tests/Database/Validator/IndexTest.php @@ -13,7 +13,6 @@ class IndexTest extends TestCase { public function setUp(): void { - } public function tearDown(): void @@ -21,7 +20,7 @@ public function tearDown(): void } /** - * @param array $collection + * @param array $collection * @return Document * @throws Exception */ @@ -61,12 +60,11 @@ public function collectionArrayToDocuments(array $collection): Document */ public function testAppwriteCollection(): void { - // Todo: Move this test to Appwrite... $validator = new Index(); - /** @var array $configs */ + /** @var array $collections */ $collections = include __DIR__ . '/config/collections.php'; foreach ($collections as $collection) { @@ -122,7 +120,6 @@ public function testFulltextWithNonString(): void $this->assertFalse($validator->isValid($collection)); $this->assertEquals('Attribute "date" cannot be part of a FULLTEXT index', $validator->getDescription()); - } /** @@ -200,6 +197,4 @@ public function testEmptyAttributes(): void $this->assertFalse($validator->isValid($collection)); $this->assertEquals('No attributes provided for index', $validator->getDescription()); } - - } From ad1de7ef38b32fb04b993f4a2321374a21d10d33 Mon Sep 17 00:00:00 2001 From: fogelito Date: Sun, 14 May 2023 13:50:12 +0300 Subject: [PATCH 11/43] testFulltextIndexWithInteger --- tests/Database/Base.php | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/tests/Database/Base.php b/tests/Database/Base.php index b5812ff73..6daa5b67c 100644 --- a/tests/Database/Base.php +++ b/tests/Database/Base.php @@ -966,13 +966,15 @@ public function testGetDocumentSelect(Document $document): Document */ public function testFulltextIndexWithInteger(): void { + $this->expectException(Exception::class); + if (!$this->getDatabase()->getAdapter()->getSupportForFulltextIndex()) { - $this->expectNotToPerformAssertions(); - return; + $this->expectExceptionMessage('Fulltext index is not supported'); + } + else { + $this->expectExceptionMessage('Attribute "integer" cannot be part of a FULLTEXT index'); } - $this->expectException(Exception::class); - $this->expectExceptionMessage('Attribute "integer" cannot be part of a FULLTEXT index'); static::getDatabase()->createIndex('documents', 'fulltext_integer', Database::INDEX_FULLTEXT, ['string','integer']); } From ba4c57903e3107765220ecb9a030b42b32f155df Mon Sep 17 00:00:00 2001 From: fogelito Date: Sun, 14 May 2023 17:53:19 +0300 Subject: [PATCH 12/43] lint --- tests/Database/Base.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/Database/Base.php b/tests/Database/Base.php index 6daa5b67c..3cc097fe3 100644 --- a/tests/Database/Base.php +++ b/tests/Database/Base.php @@ -970,8 +970,7 @@ public function testFulltextIndexWithInteger(): void if (!$this->getDatabase()->getAdapter()->getSupportForFulltextIndex()) { $this->expectExceptionMessage('Fulltext index is not supported'); - } - else { + } else { $this->expectExceptionMessage('Attribute "integer" cannot be part of a FULLTEXT index'); } From fde7ed23ad56e735437d90d8804248ca2249635f Mon Sep 17 00:00:00 2001 From: fogelito Date: Sun, 14 May 2023 18:50:21 +0300 Subject: [PATCH 13/43] typo --- src/Database/Validator/Index.php | 2 +- tests/Database/Base.php | 2 +- tests/Database/Validator/IndexTest.php | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Database/Validator/Index.php b/src/Database/Validator/Index.php index 31179f276..f713e33b7 100644 --- a/src/Database/Validator/Index.php +++ b/src/Database/Validator/Index.php @@ -112,7 +112,7 @@ public function checkIndexLength(Document $collection): bool } if ($total > self::MAX) { - $this->message = 'Index Length is longer that the max ('.self::MAX.'))'; + $this->message = 'Index Length is longer than max ('.self::MAX.'))'; return false; } } diff --git a/tests/Database/Base.php b/tests/Database/Base.php index 3cc097fe3..70782a442 100644 --- a/tests/Database/Base.php +++ b/tests/Database/Base.php @@ -149,7 +149,7 @@ public function testIndexValidation(): void $collection->setAttribute('indexes', $indexes); - $errorMessage = 'Index Length is longer that the max (768))'; + $errorMessage = 'Index Length is longer than max (768))'; $this->assertFalse($validator->isValid($collection)); $this->assertEquals($errorMessage, $validator->getDescription()); diff --git a/tests/Database/Validator/IndexTest.php b/tests/Database/Validator/IndexTest.php index 2744f44b2..86878d2fe 100644 --- a/tests/Database/Validator/IndexTest.php +++ b/tests/Database/Validator/IndexTest.php @@ -157,7 +157,7 @@ public function testIndexLength(): void ]); $this->assertFalse($validator->isValid($collection)); - $this->assertEquals('Index Length is longer that the max (768))', $validator->getDescription()); + $this->assertEquals('Index Length is longer than max (768))', $validator->getDescription()); } /** From 377011c8101394af2b119f1de7c1c610ca7aaada Mon Sep 17 00:00:00 2001 From: Shmuel Fogel Date: Wed, 17 May 2023 11:21:57 +0300 Subject: [PATCH 14/43] Update src/Database/Validator/Index.php Co-authored-by: Jake Barnby --- src/Database/Validator/Index.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Database/Validator/Index.php b/src/Database/Validator/Index.php index f713e33b7..7b15e85c9 100644 --- a/src/Database/Validator/Index.php +++ b/src/Database/Validator/Index.php @@ -9,7 +9,7 @@ class Index extends Validator { - protected string $message = 'Invalid Index'; + protected string $message = 'Invalid index'; public const MAX = 768; // 3072 bytes / mb4 /** From 318da08233e0188639bda5a6c82df559aeefaf82 Mon Sep 17 00:00:00 2001 From: fogelito Date: Wed, 17 May 2023 11:26:17 +0300 Subject: [PATCH 15/43] MAx varchar length --- src/Database/Adapter/MariaDB.php | 4 ++- src/Database/Adapter/MySQL.php | 46 +------------------------------- tests/Database/Base.php | 2 +- 3 files changed, 5 insertions(+), 47 deletions(-) diff --git a/src/Database/Adapter/MariaDB.php b/src/Database/Adapter/MariaDB.php index 043e5c1d3..0ff0dd7de 100644 --- a/src/Database/Adapter/MariaDB.php +++ b/src/Database/Adapter/MariaDB.php @@ -14,6 +14,8 @@ class MariaDB extends SQL { + protected int $maxVarcharLength = 16382; + /** * Create Database * @@ -1276,7 +1278,7 @@ protected function getSQLType(string $type, int $size, bool $signed = true): str return 'MEDIUMTEXT'; } - if ($size > 16381) { // 16381 mysql vs 16383 maria + if ($size > $this->maxVarcharLength) { return 'TEXT'; } diff --git a/src/Database/Adapter/MySQL.php b/src/Database/Adapter/MySQL.php index b8686f7b1..b5c15e3de 100644 --- a/src/Database/Adapter/MySQL.php +++ b/src/Database/Adapter/MySQL.php @@ -2,56 +2,12 @@ namespace Utopia\Database\Adapter; -use Exception; use PDOException; -use Swoole\Database\PDOProxy; -use Utopia\Database\Database; use Utopia\Database\Exception\Timeout; class MySQL extends MariaDB { - /** - * Get SQL Index - * - * @param string $collection - * @param string $id - * @param string $type - * @param array $attributes - * - * @return string - * @throws Exception - * @throws Exception - * @throws Exception - */ - protected function getSQLIndex(string $collection, string $id, string $type, array $attributes): string - { - switch ($type) { - case Database::INDEX_KEY: - $type = 'INDEX'; - break; - - case Database::INDEX_ARRAY: - $type = 'INDEX'; - - foreach ($attributes as $key => $value) { - $attributes[$key] = '(CAST(' . $value . ' AS char(255) ARRAY))'; - } - break; - - case Database::INDEX_UNIQUE: - $type = 'UNIQUE INDEX'; - break; - - case Database::INDEX_FULLTEXT: - $type = 'FULLTEXT INDEX'; - break; - - default: - throw new Exception('Unknown Index Type:' . $type); - } - - return 'CREATE '.$type.' `'.$id.'` ON `'.$this->getDefaultDatabase().'`.`'.$this->getNamespace().'_'.$collection.'` ( '.implode(', ', $attributes).' );'; - } + protected int $maxVarcharLength = 16381; /** * Returns Max Execution Time diff --git a/tests/Database/Base.php b/tests/Database/Base.php index 70782a442..1199fd59d 100644 --- a/tests/Database/Base.php +++ b/tests/Database/Base.php @@ -250,7 +250,7 @@ public function testCreateDeleteAttribute(): void static::getDatabase()->createCollection('attributes'); $this->assertEquals(true, static::getDatabase()->createAttribute('attributes', 'string1', Database::VAR_STRING, 128, true)); - $this->assertEquals(true, static::getDatabase()->createAttribute('attributes', 'string2', Database::VAR_STRING, 16381 + 1, true)); + $this->assertEquals(true, static::getDatabase()->createAttribute('attributes', 'string2', Database::VAR_STRING, 16382 + 1, true)); $this->assertEquals(true, static::getDatabase()->createAttribute('attributes', 'string3', Database::VAR_STRING, 65535 + 1, true)); $this->assertEquals(true, static::getDatabase()->createAttribute('attributes', 'string4', Database::VAR_STRING, 16777215 + 1, true)); $this->assertEquals(true, static::getDatabase()->createAttribute('attributes', 'integer', Database::VAR_INTEGER, 0, true)); From ba7df0d9fc92596cd1f9eac4f67d46e1cdad9fa9 Mon Sep 17 00:00:00 2001 From: fogelito Date: Wed, 17 May 2023 11:27:47 +0300 Subject: [PATCH 16/43] remove extra lines --- src/Database/Database.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Database/Database.php b/src/Database/Database.php index 774212e20..b035df4a8 100644 --- a/src/Database/Database.php +++ b/src/Database/Database.php @@ -655,8 +655,6 @@ public function createCollection(string $id, array $attributes = [], array $inde return new Document($this->collection); } - - // Check index limits, if given if ($indexes && $this->adapter->getCountOfIndexes($collection) > $this->adapter->getLimitForIndexes()) { throw new LimitException('Index limit of ' . $this->adapter->getLimitForIndexes() . ' exceeded. Cannot create collection.'); From 08cea03184cd41cc3abb1073e6ad3ed55948c4e0 Mon Sep 17 00:00:00 2001 From: fogelito Date: Wed, 17 May 2023 17:19:17 +0300 Subject: [PATCH 17/43] Set max index length from constructor --- src/Database/Adapter.php | 5 +++++ src/Database/Adapter/Postgres.php | 2 +- src/Database/Database.php | 4 ++-- src/Database/Validator/Index.php | 12 ++++++++---- tests/Database/Base.php | 2 +- tests/Database/Validator/IndexTest.php | 8 ++++---- 6 files changed, 21 insertions(+), 12 deletions(-) diff --git a/src/Database/Adapter.php b/src/Database/Adapter.php index 47ecad14c..50f4a2fd6 100644 --- a/src/Database/Adapter.php +++ b/src/Database/Adapter.php @@ -21,6 +21,11 @@ abstract class Adapter */ protected array $debug = []; + /** + * @var int + */ + public int $maxIndexLength = 768; + /** * @param string $key * @param mixed $value diff --git a/src/Database/Adapter/Postgres.php b/src/Database/Adapter/Postgres.php index f2ee376f9..7a9a20ebd 100644 --- a/src/Database/Adapter/Postgres.php +++ b/src/Database/Adapter/Postgres.php @@ -1269,7 +1269,7 @@ protected function getSQLType(string $type, int $size, bool $signed = true): str switch ($type) { case Database::VAR_STRING: // $size = $size * 4; // Convert utf8mb4 size to bytes - if ($size > 16381) { + if ($size > 16383) { return 'TEXT'; } diff --git a/src/Database/Database.php b/src/Database/Database.php index b035df4a8..0599185d2 100644 --- a/src/Database/Database.php +++ b/src/Database/Database.php @@ -644,7 +644,7 @@ public function createCollection(string $id, array $attributes = [], array $inde 'documentSecurity' => $documentSecurity ]); - $validator = new IndexValidator(); + $validator = new IndexValidator($this->adapter->maxIndexLength); if (!$validator->isValid($collection)) { throw new Exception($validator->getDescription()); } @@ -2017,7 +2017,7 @@ public function createIndex(string $collection, string $id, string $type, array 'orders' => $orders, ]), Document::SET_TYPE_APPEND); - $validator = new IndexValidator(); + $validator = new IndexValidator($this->adapter->maxIndexLength); if (!$validator->isValid($collection)) { throw new Exception($validator->getDescription()); } diff --git a/src/Database/Validator/Index.php b/src/Database/Validator/Index.php index 7b15e85c9..1772ddfbf 100644 --- a/src/Database/Validator/Index.php +++ b/src/Database/Validator/Index.php @@ -10,15 +10,19 @@ class Index extends Validator { protected string $message = 'Invalid index'; - public const MAX = 768; // 3072 bytes / mb4 + protected int $maxLength; /** * @var array $attributes */ protected array $attributes = []; - public function __construct() + /** + * @param int $maxLength + */ + public function __construct(int $maxLength) { + $this->maxLength = $maxLength; } /** @@ -111,8 +115,8 @@ public function checkIndexLength(Document $collection): bool $total += $indexLength; } - if ($total > self::MAX) { - $this->message = 'Index Length is longer than max ('.self::MAX.'))'; + if ($total > $this->maxLength) { + $this->message = 'Index Length is longer than max (' . $this->maxLength . '))'; return false; } } diff --git a/tests/Database/Base.php b/tests/Database/Base.php index 1199fd59d..57238c34c 100644 --- a/tests/Database/Base.php +++ b/tests/Database/Base.php @@ -124,7 +124,7 @@ public function testIndexValidation(): void 'indexes' => $indexes ]); - $validator = new Index(); + $validator = new Index(768); $errorMessage = 'Index length("301") is longer than the key part for "title1("300")"'; $this->assertFalse($validator->isValid($collection)); diff --git a/tests/Database/Validator/IndexTest.php b/tests/Database/Validator/IndexTest.php index 86878d2fe..85766e1e9 100644 --- a/tests/Database/Validator/IndexTest.php +++ b/tests/Database/Validator/IndexTest.php @@ -62,7 +62,7 @@ public function testAppwriteCollection(): void { // Todo: Move this test to Appwrite... - $validator = new Index(); + $validator = new Index(768); /** @var array $collections */ $collections = include __DIR__ . '/config/collections.php'; @@ -78,7 +78,7 @@ public function testAppwriteCollection(): void */ public function testFulltextWithNonString(): void { - $validator = new Index(); + $validator = new Index(768); $collection = new Document([ '$id' => ID::custom('test'), @@ -127,7 +127,7 @@ public function testFulltextWithNonString(): void */ public function testIndexLength(): void { - $validator = new Index(); + $validator = new Index(768); $collection = new Document([ '$id' => ID::custom('test'), @@ -165,7 +165,7 @@ public function testIndexLength(): void */ public function testEmptyAttributes(): void { - $validator = new Index(); + $validator = new Index(768); $collection = new Document([ '$id' => ID::custom('test'), From 955de1b265d8c3c72bcd9f3ec1d7403a4eadacff Mon Sep 17 00:00:00 2001 From: fogelito Date: Wed, 17 May 2023 17:27:18 +0300 Subject: [PATCH 18/43] Descriptive naming --- src/Database/Validator/Index.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Database/Validator/Index.php b/src/Database/Validator/Index.php index 1772ddfbf..56ed3ac27 100644 --- a/src/Database/Validator/Index.php +++ b/src/Database/Validator/Index.php @@ -60,8 +60,8 @@ public function checkFulltextIndexNonString(Document $collection): bool { foreach ($collection->getAttribute('indexes', []) as $index) { if ($index->getAttribute('type') === Database::INDEX_FULLTEXT) { - foreach ($index->getAttribute('attributes', []) as $ia) { - $attribute = $this->attributes[$ia] ?? new Document([]); + foreach ($index->getAttribute('attributes', []) as $attributeName) { + $attribute = $this->attributes[$attributeName] ?? new Document([]); if ($attribute->getAttribute('type', '') !== Database::VAR_STRING) { $this->message = 'Attribute "'.$attribute->getAttribute('key', $attribute->getAttribute('$id')).'" cannot be part of a FULLTEXT index'; return false; @@ -87,13 +87,13 @@ public function checkIndexLength(Document $collection): bool $total = 0; $lengths = $index->getAttribute('lengths', []); - foreach ($index->getAttribute('attributes', []) as $ik => $attributeKey) { - $attribute = $this->attributes[$attributeKey]; + foreach ($index->getAttribute('attributes', []) as $attributePosition => $attributeName) { + $attribute = $this->attributes[$attributeName]; switch ($attribute->getAttribute('type')) { case Database::VAR_STRING: $attributeSize = $attribute->getAttribute('size', 0); - $indexLength = $lengths[$ik] ?? $attributeSize; + $indexLength = $lengths[$attributePosition] ?? $attributeSize; break; case Database::VAR_FLOAT: From d10f3c94b6d3bd3d708c9e66bbdce62cdd196392 Mon Sep 17 00:00:00 2001 From: fogelito Date: Wed, 17 May 2023 18:07:10 +0300 Subject: [PATCH 19/43] getInternalAttributes --- src/Database/Database.php | 6 +++--- src/Database/Validator/Index.php | 21 +++++---------------- 2 files changed, 8 insertions(+), 19 deletions(-) diff --git a/src/Database/Database.php b/src/Database/Database.php index 0599185d2..c43f80823 100644 --- a/src/Database/Database.php +++ b/src/Database/Database.php @@ -143,7 +143,7 @@ class Database * List of Internal Ids * @var array> */ - protected array $attributes = [ + protected static array $attributes = [ [ '$id' => '$id', 'type' => self::VAR_STRING, @@ -3990,10 +3990,10 @@ public static function addFilter(string $name, callable $encode, callable $decod * @return array * @throws Exception */ - public function getInternalAttributes(): array + public static function getInternalAttributes(): array { $attributes = []; - foreach ($this->attributes as $internal) { + foreach (self::$attributes as $internal) { $attributes[] = new Document($internal); } return $attributes; diff --git a/src/Database/Validator/Index.php b/src/Database/Validator/Index.php index 56ed3ac27..5f6803c46 100644 --- a/src/Database/Validator/Index.php +++ b/src/Database/Validator/Index.php @@ -108,7 +108,7 @@ public function checkIndexLength(Document $collection): bool } if ($indexLength > $attributeSize) { - $this->message = 'Index length("'.$indexLength.'") is longer than the key part for "'.$attributeKey.'("'.$attributeSize.'")"'; + $this->message = 'Index length("'.$indexLength.'") is longer than the key part for "'.$attributeName.'("'.$attributeSize.'")"'; return false; } @@ -135,23 +135,12 @@ public function checkIndexLength(Document $collection): bool public function isValid($value): bool { foreach ($value->getAttribute('attributes', []) as $attribute) { - $this->attributes[$attribute->getAttribute('$id', $value->getAttribute('key'))] = $attribute; + $this->attributes[$attribute->getAttribute('key', $attribute->getAttribute('$id'))] = $attribute; } - $this->attributes['$id'] = new Document([ - 'type' => Database::VAR_STRING, - 'size' => Database::LENGTH_KEY, - ]); - - $this->attributes['$createdAt'] = new Document([ - 'type' => Database::VAR_DATETIME, - 'size' => Database::LENGTH_KEY, - ]); - - $this->attributes['$updatedAt'] = new Document([ - 'type' => Database::VAR_DATETIME, - 'size' => Database::LENGTH_KEY, - ]); + foreach (Database::getInternalAttributes() as $internalAttribute){ + $this->attributes[$internalAttribute->getAttribute('$id')] = $internalAttribute; + } if (!$this->checkEmptyIndexAttributes($value)) { return false; From 4e64390c3aa2ac8542dffbe82d420c014403ed28 Mon Sep 17 00:00:00 2001 From: fogelito Date: Wed, 17 May 2023 18:08:08 +0300 Subject: [PATCH 20/43] getInternalAttributes --- src/Database/Validator/Index.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Database/Validator/Index.php b/src/Database/Validator/Index.php index 5f6803c46..02f01c7f3 100644 --- a/src/Database/Validator/Index.php +++ b/src/Database/Validator/Index.php @@ -138,8 +138,8 @@ public function isValid($value): bool $this->attributes[$attribute->getAttribute('key', $attribute->getAttribute('$id'))] = $attribute; } - foreach (Database::getInternalAttributes() as $internalAttribute){ - $this->attributes[$internalAttribute->getAttribute('$id')] = $internalAttribute; + foreach (Database::getInternalAttributes() as $attribute){ + $this->attributes[$attribute->getAttribute('$id')] = $attribute; } if (!$this->checkEmptyIndexAttributes($value)) { From e7454bd982eac3f5c117362cc288020f57237c4e Mon Sep 17 00:00:00 2001 From: fogelito Date: Thu, 18 May 2023 10:30:43 +0300 Subject: [PATCH 21/43] lint + composer.lock --- composer.lock | 46 ++++++++++++++++---------------- src/Database/Validator/Index.php | 2 +- 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/composer.lock b/composer.lock index 877e99f97..3985fdb08 100644 --- a/composer.lock +++ b/composer.lock @@ -512,16 +512,16 @@ }, { "name": "fakerphp/faker", - "version": "v1.21.0", + "version": "v1.22.0", "source": { "type": "git", "url": "https://github.com/FakerPHP/Faker.git", - "reference": "92efad6a967f0b79c499705c69b662f738cc9e4d" + "reference": "f85772abd508bd04e20bb4b1bbe260a68d0066d2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/FakerPHP/Faker/zipball/92efad6a967f0b79c499705c69b662f738cc9e4d", - "reference": "92efad6a967f0b79c499705c69b662f738cc9e4d", + "url": "https://api.github.com/repos/FakerPHP/Faker/zipball/f85772abd508bd04e20bb4b1bbe260a68d0066d2", + "reference": "f85772abd508bd04e20bb4b1bbe260a68d0066d2", "shasum": "" }, "require": { @@ -574,9 +574,9 @@ ], "support": { "issues": "https://github.com/FakerPHP/Faker/issues", - "source": "https://github.com/FakerPHP/Faker/tree/v1.21.0" + "source": "https://github.com/FakerPHP/Faker/tree/v1.22.0" }, - "time": "2022-12-13T13:54:32+00:00" + "time": "2023-05-14T12:31:37+00:00" }, { "name": "laravel/pint", @@ -906,16 +906,16 @@ }, { "name": "phpstan/phpstan", - "version": "1.10.11", + "version": "1.10.15", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "8aa62e6ea8b58ffb650e02940e55a788cbc3fe21" + "reference": "762c4dac4da6f8756eebb80e528c3a47855da9bd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/8aa62e6ea8b58ffb650e02940e55a788cbc3fe21", - "reference": "8aa62e6ea8b58ffb650e02940e55a788cbc3fe21", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/762c4dac4da6f8756eebb80e528c3a47855da9bd", + "reference": "762c4dac4da6f8756eebb80e528c3a47855da9bd", "shasum": "" }, "require": { @@ -964,7 +964,7 @@ "type": "tidelift" } ], - "time": "2023-04-04T19:17:42+00:00" + "time": "2023-05-09T15:28:01+00:00" }, { "name": "phpunit/php-code-coverage", @@ -1286,16 +1286,16 @@ }, { "name": "phpunit/phpunit", - "version": "9.6.6", + "version": "9.6.8", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "b65d59a059d3004a040c16a82e07bbdf6cfdd115" + "reference": "17d621b3aff84d0c8b62539e269e87d8d5baa76e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/b65d59a059d3004a040c16a82e07bbdf6cfdd115", - "reference": "b65d59a059d3004a040c16a82e07bbdf6cfdd115", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/17d621b3aff84d0c8b62539e269e87d8d5baa76e", + "reference": "17d621b3aff84d0c8b62539e269e87d8d5baa76e", "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.8" }, "funding": [ { @@ -1385,7 +1385,7 @@ "type": "tidelift" } ], - "time": "2023-03-27T11:43:46+00:00" + "time": "2023-05-11T05:14:45+00:00" }, { "name": "psr/container", @@ -1786,16 +1786,16 @@ }, { "name": "sebastian/diff", - "version": "4.0.4", + "version": "4.0.5", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d" + "reference": "74be17022044ebaaecfdf0c5cd504fc9cd5a7131" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/3461e3fccc7cfdfc2720be910d3bd73c69be590d", - "reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/74be17022044ebaaecfdf0c5cd504fc9cd5a7131", + "reference": "74be17022044ebaaecfdf0c5cd504fc9cd5a7131", "shasum": "" }, "require": { @@ -1840,7 +1840,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/diff/issues", - "source": "https://github.com/sebastianbergmann/diff/tree/4.0.4" + "source": "https://github.com/sebastianbergmann/diff/tree/4.0.5" }, "funding": [ { @@ -1848,7 +1848,7 @@ "type": "github" } ], - "time": "2020-10-26T13:10:38+00:00" + "time": "2023-05-07T05:35:17+00:00" }, { "name": "sebastian/environment", diff --git a/src/Database/Validator/Index.php b/src/Database/Validator/Index.php index 02f01c7f3..4b02863e2 100644 --- a/src/Database/Validator/Index.php +++ b/src/Database/Validator/Index.php @@ -138,7 +138,7 @@ public function isValid($value): bool $this->attributes[$attribute->getAttribute('key', $attribute->getAttribute('$id'))] = $attribute; } - foreach (Database::getInternalAttributes() as $attribute){ + foreach (Database::getInternalAttributes() as $attribute) { $this->attributes[$attribute->getAttribute('$id')] = $attribute; } From 6793244da94367f1eccb10c2b9ffe4fc2b63f970 Mon Sep 17 00:00:00 2001 From: fogelito Date: Thu, 18 May 2023 10:42:36 +0300 Subject: [PATCH 22/43] Remove appwite collections validations --- tests/Database/Validator/IndexTest.php | 18 - .../Database/Validator/config/collections.php | 3580 ----------------- 2 files changed, 3598 deletions(-) delete mode 100644 tests/Database/Validator/config/collections.php diff --git a/tests/Database/Validator/IndexTest.php b/tests/Database/Validator/IndexTest.php index 85766e1e9..b0f6b43f7 100644 --- a/tests/Database/Validator/IndexTest.php +++ b/tests/Database/Validator/IndexTest.php @@ -55,24 +55,6 @@ public function collectionArrayToDocuments(array $collection): Document return $document; } - /** - * @throws Exception - */ - public function testAppwriteCollection(): void - { - // Todo: Move this test to Appwrite... - - $validator = new Index(768); - - /** @var array $collections */ - $collections = include __DIR__ . '/config/collections.php'; - - foreach ($collections as $collection) { - $collection = $this->collectionArrayToDocuments($collection); - $this->assertTrue($validator->isValid($collection)); - } - } - /** * @throws Exception */ diff --git a/tests/Database/Validator/config/collections.php b/tests/Database/Validator/config/collections.php deleted file mode 100644 index bb4954e1d..000000000 --- a/tests/Database/Validator/config/collections.php +++ /dev/null @@ -1,3580 +0,0 @@ - id of the parent collection where this will be inserted - * $id => id of this collection - * name => name of this collection - * project => whether this collection should be created per project - * attributes => list of attributes - * indexes => list of indexes - */ - -$collections = [ - 'databases' => [ - '$collection' => ID::custom(Database::METADATA), - '$id' => ID::custom('databases'), - 'name' => 'Databases', - 'attributes' => [ - [ - '$id' => ID::custom('name'), - 'type' => Database::VAR_STRING, - 'size' => 256, - 'required' => true, - 'signed' => true, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('search'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 16384, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - ], - 'indexes' => [ - [ - '$id' => ID::custom('_fulltext_search'), - 'type' => Database::INDEX_FULLTEXT, - 'attributes' => ['search'], - 'lengths' => [], - 'orders' => [], - ], - [ - '$id' => ID::custom('_key_name'), - 'type' => Database::INDEX_KEY, - 'attributes' => ['name'], - 'lengths' => [256], - 'orders' => [Database::ORDER_ASC], - ], - ], - ], - 'collections' => [ - '$collection' => ID::custom('databases'), - '$id' => ID::custom('collections'), - 'name' => 'Collections', - 'attributes' => [ - [ - '$id' => ID::custom('databaseInternalId'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => Database::LENGTH_KEY, - 'signed' => true, - 'required' => true, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('databaseId'), - 'type' => Database::VAR_STRING, - 'signed' => true, - 'size' => Database::LENGTH_KEY, - 'format' => '', - 'filters' => [], - 'required' => true, - 'default' => null, - 'array' => false, - ], - [ - '$id' => ID::custom('name'), - 'type' => Database::VAR_STRING, - 'size' => 256, - 'required' => true, - 'signed' => true, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('enabled'), - 'type' => Database::VAR_BOOLEAN, - 'signed' => true, - 'size' => 0, - 'format' => '', - 'filters' => [], - 'required' => true, - 'default' => null, - 'array' => false, - ], - [ - '$id' => ID::custom('documentSecurity'), - 'type' => Database::VAR_BOOLEAN, - 'signed' => true, - 'size' => 0, - 'format' => '', - 'filters' => [], - 'required' => true, - 'default' => null, - 'array' => false, - ], - [ - '$id' => ID::custom('attributes'), - 'type' => Database::VAR_STRING, - 'size' => 1000000, - 'required' => false, - 'signed' => true, - 'array' => false, - 'filters' => ['subQueryAttributes'], - ], - [ - '$id' => ID::custom('indexes'), - 'type' => Database::VAR_STRING, - 'size' => 1000000, - 'required' => false, - 'signed' => true, - 'array' => false, - 'filters' => ['subQueryIndexes'], - ], - [ - '$id' => ID::custom('search'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 16384, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - ], - 'indexes' => [ - [ - '$id' => ID::custom('_fulltext_search'), - 'type' => Database::INDEX_FULLTEXT, - 'attributes' => ['search'], - 'lengths' => [], - 'orders' => [], - ], - [ - '$id' => ID::custom('_key_name'), - 'type' => Database::INDEX_KEY, - 'attributes' => ['name'], - 'lengths' => [256], - 'orders' => [Database::ORDER_ASC], - ], - [ - '$id' => ID::custom('_key_enabled'), - 'type' => Database::INDEX_KEY, - 'attributes' => ['enabled'], - 'lengths' => [], - 'orders' => [Database::ORDER_ASC], - ], - [ - '$id' => ID::custom('_key_documentSecurity'), - 'type' => Database::INDEX_KEY, - 'attributes' => ['documentSecurity'], - 'lengths' => [], - 'orders' => [Database::ORDER_ASC], - ], - ], - ], - - 'attributes' => [ - '$collection' => ID::custom(Database::METADATA), - '$id' => ID::custom('attributes'), - 'name' => 'Attributes', - 'attributes' => [ - [ - '$id' => ID::custom('databaseInternalId'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => Database::LENGTH_KEY, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('databaseId'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => Database::LENGTH_KEY, - 'signed' => false, - 'required' => true, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('collectionInternalId'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => Database::LENGTH_KEY, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('collectionId'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => Database::LENGTH_KEY, - 'signed' => true, - 'required' => true, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('key'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => Database::LENGTH_KEY, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('type'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 256, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('status'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 16, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('size'), - 'type' => Database::VAR_INTEGER, - 'format' => '', - 'size' => 0, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('required'), - 'type' => Database::VAR_BOOLEAN, - 'format' => '', - 'size' => 0, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('default'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 16384, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => ['casting'], - ], - [ - '$id' => ID::custom('signed'), - 'type' => Database::VAR_BOOLEAN, - 'size' => 0, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('array'), - 'type' => Database::VAR_BOOLEAN, - 'size' => 0, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('format'), - 'type' => Database::VAR_STRING, - 'size' => 64, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('formatOptions'), - 'type' => Database::VAR_STRING, - 'size' => 16384, - 'signed' => true, - 'required' => false, - 'default' => new stdClass(), - 'array' => false, - 'filters' => ['json', 'range', 'enum'], - ], - [ - '$id' => ID::custom('filters'), - 'type' => Database::VAR_STRING, - 'size' => 64, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => true, - 'filters' => [], - ], - [ - '$id' => ID::custom('options'), - 'type' => Database::VAR_STRING, - 'size' => 16384, - 'signed' => false, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => ['json'], - ], - ], - 'indexes' => [ - [ - '$id' => ID::custom('_key_db_collection'), - 'type' => Database::INDEX_KEY, - 'attributes' => ['databaseInternalId', 'collectionInternalId'], - 'lengths' => [Database::LENGTH_KEY, Database::LENGTH_KEY], - 'orders' => [Database::ORDER_ASC, Database::ORDER_ASC], - ], - ], - ], - - 'indexes' => [ - '$collection' => ID::custom(Database::METADATA), - '$id' => ID::custom('indexes'), - 'name' => 'Indexes', - 'attributes' => [ - [ - '$id' => ID::custom('databaseInternalId'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => Database::LENGTH_KEY, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('databaseId'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => Database::LENGTH_KEY, - 'signed' => false, - 'required' => true, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('collectionInternalId'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => Database::LENGTH_KEY, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('collectionId'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => Database::LENGTH_KEY, - 'signed' => true, - 'required' => true, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('key'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => Database::LENGTH_KEY, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('type'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 16, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('status'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 16, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('attributes'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => Database::LENGTH_KEY, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => true, - 'filters' => [], - ], - [ - '$id' => ID::custom('lengths'), - 'type' => Database::VAR_INTEGER, - 'format' => '', - 'size' => 0, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => true, - 'filters' => [], - ], - [ - '$id' => ID::custom('orders'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 4, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => true, - 'filters' => [], - ], - ], - 'indexes' => [ - [ - '$id' => ID::custom('_key_db_collection'), - 'type' => Database::INDEX_KEY, - 'attributes' => ['databaseInternalId', 'collectionInternalId'], - 'lengths' => [Database::LENGTH_KEY, Database::LENGTH_KEY], - 'orders' => [Database::ORDER_ASC, Database::ORDER_ASC], - ], - ], - ], - - 'projects' => [ - '$collection' => ID::custom(Database::METADATA), - '$id' => ID::custom('projects'), - 'name' => 'Projects', - 'attributes' => [ - [ - '$id' => ID::custom('teamInternalId'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => Database::LENGTH_KEY, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('teamId'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => Database::LENGTH_KEY, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('name'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 128, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('region'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 128, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('description'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 256, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('logo'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => Database::LENGTH_KEY, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('url'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 16384, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('version'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 16, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('legalName'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 256, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('legalCountry'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 256, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('legalState'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 256, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('legalCity'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 256, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('legalAddress'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 256, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('legalTaxId'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 256, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('services'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 16384, - 'signed' => true, - 'required' => false, - 'default' => [], - 'array' => false, - 'filters' => ['json'], - ], - [ - '$id' => ID::custom('auths'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 16384, - 'signed' => true, - 'required' => false, - 'default' => [], - 'array' => false, - 'filters' => ['json'], - ], - [ - '$id' => ID::custom('authProviders'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 16384, - 'signed' => true, - 'required' => false, - 'default' => [], - 'array' => false, - 'filters' => ['json', 'encrypt'], - ], - [ - '$id' => ID::custom('platforms'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 16384, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => ['subQueryPlatforms'], - ], - [ - '$id' => ID::custom('webhooks'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 16384, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => ['subQueryWebhooks'], - ], - [ - '$id' => ID::custom('keys'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 16384, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => ['subQueryKeys'], - ], - [ - '$id' => ID::custom('domains'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 16384, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => ['subQueryDomains'], - ], - [ - '$id' => ID::custom('search'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 16384, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - ], - 'indexes' => [ - [ - '$id' => ID::custom('_key_search'), - 'type' => Database::INDEX_FULLTEXT, - 'attributes' => ['search'], - 'lengths' => [], - 'orders' => [], - ], - [ - '$id' => ID::custom('_key_name'), - 'type' => Database::INDEX_KEY, - 'attributes' => ['name'], - 'lengths' => [128], - 'orders' => [Database::ORDER_ASC], - ], - [ - '$id' => ID::custom('_key_team'), - 'type' => Database::INDEX_KEY, - 'attributes' => ['teamId'], - 'lengths' => [Database::LENGTH_KEY], - 'orders' => [Database::ORDER_ASC], - ], - ], - ], - - 'platforms' => [ - '$collection' => ID::custom(Database::METADATA), - '$id' => ID::custom('platforms'), - 'name' => 'platforms', - 'attributes' => [ - [ - '$id' => ID::custom('projectInternalId'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => Database::LENGTH_KEY, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('projectId'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => Database::LENGTH_KEY, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('type'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 16, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('name'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 256, - 'signed' => true, - 'required' => true, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('key'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => Database::LENGTH_KEY, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('store'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 256, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('hostname'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 256, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ] - ], - 'indexes' => [ - [ - '$id' => ID::custom('_key_project'), - 'type' => Database::INDEX_KEY, - 'attributes' => ['projectInternalId'], - 'lengths' => [Database::LENGTH_KEY], - 'orders' => [Database::ORDER_ASC], - ], - ], - ], - - 'domains' => [ - '$collection' => ID::custom(Database::METADATA), - '$id' => ID::custom('domains'), - 'name' => 'domains', - 'attributes' => [ - [ - '$id' => ID::custom('projectInternalId'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => Database::LENGTH_KEY, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('projectId'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => Database::LENGTH_KEY, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('updated'), - 'type' => Database::VAR_DATETIME, - 'format' => '', - 'size' => 0, - 'signed' => false, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => ['datetime'], - ], - [ - '$id' => ID::custom('domain'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => Database::LENGTH_KEY, - 'signed' => true, - 'required' => true, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('tld'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => Database::LENGTH_KEY, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('registerable'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => Database::LENGTH_KEY, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('verification'), - 'type' => Database::VAR_BOOLEAN, - 'format' => '', - 'size' => 0, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('certificateId'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => Database::LENGTH_KEY, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - ], - 'indexes' => [ - [ - '$id' => ID::custom('_key_project'), - 'type' => Database::INDEX_KEY, - 'attributes' => ['projectInternalId'], - 'lengths' => [Database::LENGTH_KEY], - 'orders' => [Database::ORDER_ASC], - ], - ], - ], - - 'keys' => [ - '$collection' => ID::custom(Database::METADATA), - '$id' => ID::custom('keys'), - 'name' => 'keys', - 'attributes' => [ - [ - '$id' => ID::custom('projectInternalId'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => Database::LENGTH_KEY, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('projectId'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => Database::LENGTH_KEY, - 'signed' => true, - 'required' => false, - 'default' => 0, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('name'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => Database::LENGTH_KEY, - 'signed' => true, - 'required' => true, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('scopes'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => Database::LENGTH_KEY, - 'signed' => true, - 'required' => true, - 'default' => null, - 'array' => true, - 'filters' => [], - ], - [ - '$id' => ID::custom('secret'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 512, // var_dump of \bin2hex(\random_bytes(128)) => string(256) doubling for encryption - 'signed' => true, - 'required' => true, - 'default' => null, - 'array' => false, - 'filters' => ['encrypt'], - ], - [ - '$id' => ID::custom('expire'), - 'type' => Database::VAR_DATETIME, - 'format' => '', - 'size' => 0, - 'signed' => false, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => ['datetime'], - ], - [ - '$id' => ID::custom('accessedAt'), - 'type' => Database::VAR_DATETIME, - 'format' => '', - 'size' => 0, - 'signed' => false, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => ['datetime'], - ], - [ - '$id' => ID::custom('sdks'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => Database::LENGTH_KEY, - 'signed' => true, - 'required' => true, - 'default' => null, - 'array' => true, - 'filters' => [], - ], - ], - 'indexes' => [ - [ - '$id' => ID::custom('_key_project'), - 'type' => Database::INDEX_KEY, - 'attributes' => ['projectInternalId'], - 'lengths' => [Database::LENGTH_KEY], - 'orders' => [Database::ORDER_ASC], - ], - [ - '$id' => '_key_accessedAt', - 'type' => Database::INDEX_KEY, - 'attributes' => ['accessedAt'], - 'lengths' => [], - 'orders' => [], - ], - ], - ], - - 'webhooks' => [ - '$collection' => ID::custom(Database::METADATA), - '$id' => ID::custom('webhooks'), - 'name' => 'webhooks', - 'attributes' => [ - [ - '$id' => ID::custom('projectInternalId'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => Database::LENGTH_KEY, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('projectId'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => Database::LENGTH_KEY, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('name'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => Database::LENGTH_KEY, - 'signed' => true, - 'required' => true, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('url'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => Database::LENGTH_KEY, - 'signed' => true, - 'required' => true, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('httpUser'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => Database::LENGTH_KEY, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('httpPass'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => Database::LENGTH_KEY, // TODO will the length suffice after encryption? - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => ['encrypt'], - ], - [ - '$id' => ID::custom('security'), - 'type' => Database::VAR_BOOLEAN, - 'format' => '', - 'size' => 0, - 'signed' => true, - 'required' => true, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('events'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => Database::LENGTH_KEY, - 'signed' => true, - 'required' => true, - 'default' => null, - 'array' => true, - 'filters' => [], - ], - [ - '$id' => ID::custom('signatureKey'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 2048, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - ], - 'indexes' => [ - [ - '$id' => ID::custom('_key_project'), - 'type' => Database::INDEX_KEY, - 'attributes' => ['projectInternalId'], - 'lengths' => [Database::LENGTH_KEY], - 'orders' => [Database::ORDER_ASC], - ] - ], - ], - - 'users' => [ - '$collection' => ID::custom(Database::METADATA), - '$id' => ID::custom('users'), - 'name' => 'Users', - 'attributes' => [ - [ - '$id' => ID::custom('name'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 256, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('email'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 320, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('phone'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 16, // leading '+' and 15 digitts maximum by E.164 format - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('status'), - 'type' => Database::VAR_BOOLEAN, - 'format' => '', - 'size' => 0, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('passwordHistory'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 16384, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => true, - 'filters' => [], - ], - [ - '$id' => ID::custom('password'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 16384, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => ['encrypt'], - ], - [ - '$id' => 'hash', // Hashing algorithm used to hash the password - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 256, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('hashOptions'), // Configuration of hashing algorithm - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 65535, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => ['json'], - ], - [ - '$id' => ID::custom('passwordUpdate'), - 'type' => Database::VAR_DATETIME, - 'format' => '', - 'size' => 0, - 'signed' => false, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => ['datetime'], - ], - [ - '$id' => ID::custom('prefs'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 65535, - 'signed' => true, - 'required' => false, - 'default' => new \stdClass(), - 'array' => false, - 'filters' => ['json'], - ], - [ - '$id' => ID::custom('registration'), - 'type' => Database::VAR_DATETIME, - 'format' => '', - 'size' => 0, - 'signed' => false, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => ['datetime'], - ], - [ - '$id' => ID::custom('emailVerification'), - 'type' => Database::VAR_BOOLEAN, - 'format' => '', - 'size' => 0, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('phoneVerification'), - 'type' => Database::VAR_BOOLEAN, - 'format' => '', - 'size' => 0, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('reset'), - 'type' => Database::VAR_BOOLEAN, - 'format' => '', - 'size' => 0, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('sessions'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 16384, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => ['subQuerySessions'], - ], - [ - '$id' => ID::custom('tokens'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 16384, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => ['subQueryTokens'], - ], - [ - '$id' => ID::custom('memberships'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 16384, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => ['subQueryMemberships'], - ], - [ - '$id' => ID::custom('search'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 16384, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ] - ], - 'indexes' => [ - [ - '$id' => ID::custom('_key_name'), - 'type' => Database::INDEX_KEY, - 'attributes' => ['name'], - 'lengths' => [256], - 'orders' => [Database::ORDER_ASC], - ], - [ - '$id' => ID::custom('_key_email'), - 'type' => Database::INDEX_UNIQUE, - 'attributes' => ['email'], - 'lengths' => [320], - 'orders' => [Database::ORDER_ASC], - ], - [ - '$id' => ID::custom('_key_phone'), - 'type' => Database::INDEX_UNIQUE, - 'attributes' => ['phone'], - 'lengths' => [16], - 'orders' => [Database::ORDER_ASC], - ], - [ - '$id' => ID::custom('_key_status'), - 'type' => Database::INDEX_KEY, - 'attributes' => ['status'], - 'lengths' => [], - 'orders' => [Database::ORDER_ASC], - ], - [ - '$id' => ID::custom('_key_passwordUpdate'), - 'type' => Database::INDEX_KEY, - 'attributes' => ['passwordUpdate'], - 'lengths' => [], - 'orders' => [Database::ORDER_ASC], - ], - [ - '$id' => ID::custom('_key_registration'), - 'type' => Database::INDEX_KEY, - 'attributes' => ['registration'], - 'lengths' => [], - 'orders' => [Database::ORDER_ASC], - ], - [ - '$id' => ID::custom('_key_emailVerification'), - 'type' => Database::INDEX_KEY, - 'attributes' => ['emailVerification'], - 'lengths' => [], - 'orders' => [Database::ORDER_ASC], - ], - [ - '$id' => ID::custom('_key_phoneVerification'), - 'type' => Database::INDEX_KEY, - 'attributes' => ['phoneVerification'], - 'lengths' => [], - 'orders' => [Database::ORDER_ASC], - ], - [ - '$id' => ID::custom('_key_search'), - 'type' => Database::INDEX_FULLTEXT, - 'attributes' => ['search'], - 'lengths' => [], - 'orders' => [], - ] - ], - ], - - 'tokens' => [ - '$collection' => ID::custom(Database::METADATA), - '$id' => ID::custom('tokens'), - 'name' => 'Tokens', - 'attributes' => [ - [ - '$id' => ID::custom('userInternalId'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => Database::LENGTH_KEY, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('userId'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => Database::LENGTH_KEY, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('type'), - 'type' => Database::VAR_INTEGER, - 'format' => '', - 'size' => 0, - 'signed' => true, - 'required' => true, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('secret'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 512, // https://www.tutorialspoint.com/how-long-is-the-sha256-hash-in-mysql (512 for encryption) - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => ['encrypt'], - ], - [ - '$id' => ID::custom('expire'), - 'type' => Database::VAR_DATETIME, - 'format' => '', - 'size' => 0, - 'signed' => false, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => ['datetime'], - ], - [ - '$id' => ID::custom('userAgent'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 16384, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('ip'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 45, // https://stackoverflow.com/a/166157/2299554 - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ] - ], - 'indexes' => [ - [ - '$id' => ID::custom('_key_user'), - 'type' => Database::INDEX_KEY, - 'attributes' => ['userInternalId'], - 'lengths' => [Database::LENGTH_KEY], - 'orders' => [Database::ORDER_ASC], - ], - ], - ], - - 'sessions' => [ - '$collection' => ID::custom(Database::METADATA), - '$id' => ID::custom('sessions'), - 'name' => 'Sessions', - 'attributes' => [ - [ - '$id' => ID::custom('userInternalId'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => Database::LENGTH_KEY, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('userId'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => Database::LENGTH_KEY, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('provider'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 128, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('providerUid'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 2048, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('providerAccessToken'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 16384, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => ['encrypt'], - ], - [ - '$id' => ID::custom('providerAccessTokenExpiry'), - 'type' => Database::VAR_DATETIME, - 'format' => '', - 'size' => 0, - 'signed' => false, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => ['datetime'], - ], - [ - '$id' => ID::custom('providerRefreshToken'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 16384, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => ['encrypt'], - ], - [ - '$id' => ID::custom('secret'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 512, // https://www.tutorialspoint.com/how-long-is-the-sha256-hash-in-mysql (512 for encryption) - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => ['encrypt'], - ], - [ - '$id' => ID::custom('userAgent'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 16384, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('ip'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 45, // https://stackoverflow.com/a/166157/2299554 - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('countryCode'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 2, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('osCode'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 256, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('osName'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 256, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('osVersion'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 256, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('clientType'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 256, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('clientCode'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 256, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('clientName'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 256, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('clientVersion'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 256, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('clientEngine'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 256, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('clientEngineVersion'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 256, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('deviceName'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 256, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('deviceBrand'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 256, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('deviceModel'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 256, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - ], - 'indexes' => [ - [ - '$id' => ID::custom('_key_provider_providerUid'), - 'type' => Database::INDEX_KEY, - 'attributes' => ['provider', 'providerUid'], - 'lengths' => [100, 100], - 'orders' => [Database::ORDER_ASC, Database::ORDER_ASC], - ], - [ - '$id' => ID::custom('_key_user'), - 'type' => Database::INDEX_KEY, - 'attributes' => ['userInternalId'], - 'lengths' => [Database::LENGTH_KEY], - 'orders' => [Database::ORDER_ASC], - ], - ], - ], - - 'teams' => [ - '$collection' => ID::custom(Database::METADATA), - '$id' => ID::custom('teams'), - 'name' => 'Teams', - 'attributes' => [ - [ - '$id' => ID::custom('name'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 128, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('total'), - 'type' => Database::VAR_INTEGER, - 'format' => '', - 'size' => 0, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('search'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 16384, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('prefs'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 65535, - 'signed' => true, - 'required' => false, - 'default' => new \stdClass(), - 'array' => false, - 'filters' => ['json'], - ], - ], - 'indexes' => [ - [ - '$id' => ID::custom('_key_search'), - 'type' => Database::INDEX_FULLTEXT, - 'attributes' => ['search'], - 'lengths' => [], - 'orders' => [], - ], - [ - '$id' => ID::custom('_key_name'), - 'type' => Database::INDEX_KEY, - 'attributes' => ['name'], - 'lengths' => [128], - 'orders' => [Database::ORDER_ASC], - ], - [ - '$id' => ID::custom('_key_total'), - 'type' => Database::INDEX_KEY, - 'attributes' => ['total'], - 'lengths' => [], - 'orders' => [Database::ORDER_ASC], - ], - ], - ], - - 'memberships' => [ - '$collection' => ID::custom(Database::METADATA), - '$id' => ID::custom('memberships'), - 'name' => 'Memberships', - 'attributes' => [ - [ - '$id' => ID::custom('userInternalId'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => Database::LENGTH_KEY, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('userId'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => Database::LENGTH_KEY, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('teamInternalId'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => Database::LENGTH_KEY, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('teamId'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => Database::LENGTH_KEY, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('roles'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 128, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => true, - 'filters' => [], - ], - [ - '$id' => ID::custom('invited'), - 'type' => Database::VAR_DATETIME, - 'format' => '', - 'size' => 0, - 'signed' => false, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => ['datetime'], - ], - [ - '$id' => ID::custom('joined'), - 'type' => Database::VAR_DATETIME, - 'format' => '', - 'size' => 0, - 'signed' => false, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => ['datetime'], - ], - [ - '$id' => ID::custom('confirm'), - 'type' => Database::VAR_BOOLEAN, - 'format' => '', - 'size' => 0, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('secret'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 256, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => ['encrypt'], - ], - [ - '$id' => ID::custom('search'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 16384, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - ], - 'indexes' => [ - [ - '$id' => ID::custom('_key_unique'), - 'type' => Database::INDEX_UNIQUE, - 'attributes' => ['teamInternalId', 'userInternalId'], - 'lengths' => [Database::LENGTH_KEY, Database::LENGTH_KEY], - 'orders' => [Database::ORDER_ASC, Database::ORDER_ASC], - ], - [ - '$id' => ID::custom('_key_user'), - 'type' => Database::INDEX_KEY, - 'attributes' => ['userInternalId'], - 'lengths' => [Database::LENGTH_KEY], - 'orders' => [Database::ORDER_ASC], - ], - [ - '$id' => ID::custom('_key_team'), - 'type' => Database::INDEX_KEY, - 'attributes' => ['teamInternalId'], - 'lengths' => [Database::LENGTH_KEY], - 'orders' => [Database::ORDER_ASC], - ], - [ - '$id' => ID::custom('_key_search'), - 'type' => Database::INDEX_FULLTEXT, - 'attributes' => ['search'], - 'lengths' => [], - 'orders' => [], - ], - [ - '$id' => ID::custom('_key_userId'), - 'type' => Database::INDEX_KEY, - 'attributes' => ['userId'], - 'lengths' => [Database::LENGTH_KEY], - 'orders' => [Database::ORDER_ASC], - ], - [ - '$id' => ID::custom('_key_teamId'), - 'type' => Database::INDEX_KEY, - 'attributes' => ['teamId'], - 'lengths' => [Database::LENGTH_KEY], - 'orders' => [Database::ORDER_ASC], - ], - [ - '$id' => ID::custom('_key_invited'), - 'type' => Database::INDEX_KEY, - 'attributes' => ['invited'], - 'lengths' => [], - 'orders' => [Database::ORDER_ASC], - ], - [ - '$id' => ID::custom('_key_joined'), - 'type' => Database::INDEX_KEY, - 'attributes' => ['joined'], - 'lengths' => [], - 'orders' => [Database::ORDER_ASC], - ], - [ - '$id' => ID::custom('_key_confirm'), - 'type' => Database::INDEX_KEY, - 'attributes' => ['confirm'], - 'lengths' => [], - 'orders' => [Database::ORDER_ASC], - ], - ], - ], - - 'functions' => [ - '$collection' => ID::custom(Database::METADATA), - '$id' => ID::custom('functions'), - 'name' => 'Functions', - 'attributes' => [ - [ - '$id' => ID::custom('execute'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 128, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => true, - 'filters' => [], - ], - [ - '$id' => ID::custom('name'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 2048, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('enabled'), - 'type' => Database::VAR_BOOLEAN, - 'signed' => true, - 'size' => 0, - 'format' => '', - 'filters' => [], - 'required' => true, - 'array' => false, - ], - [ - '$id' => ID::custom('runtime'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 2048, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('deployment'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => Database::LENGTH_KEY, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('vars'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 16384, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => ['subQueryVariables'], - ], - [ - '$id' => ID::custom('events'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 256, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => true, - 'filters' => [], - ], - [ - '$id' => ID::custom('schedule'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 128, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('scheduleUpdatedAt'), // Used to fix duplicate executions bug. Can be removed once new queue library is used - 'type' => Database::VAR_DATETIME, - 'format' => '', - 'size' => 0, - 'signed' => false, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => ['datetime'], - ], - [ - '$id' => ID::custom('schedulePrevious'), - 'type' => Database::VAR_DATETIME, - 'format' => '', - 'size' => 0, - 'signed' => false, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => ['datetime'], - ], - [ - '$id' => ID::custom('scheduleNext'), - 'type' => Database::VAR_DATETIME, - 'format' => '', - 'size' => 0, - 'signed' => false, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => ['datetime'], - ], - [ - '$id' => ID::custom('timeout'), - 'type' => Database::VAR_INTEGER, - 'format' => '', - 'size' => 0, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('search'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 16384, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - ], - 'indexes' => [ - [ - '$id' => ID::custom('_key_search'), - 'type' => Database::INDEX_FULLTEXT, - 'attributes' => ['search'], - 'lengths' => [], - 'orders' => [Database::ORDER_ASC], - ], - [ - '$id' => ID::custom('_key_name'), - 'type' => Database::INDEX_KEY, - 'attributes' => ['name'], - 'lengths' => [768], - 'orders' => [Database::ORDER_ASC], - ], - [ - '$id' => ID::custom('_key_enabled'), - 'type' => Database::INDEX_KEY, - 'attributes' => ['enabled'], - 'lengths' => [], - 'orders' => [Database::ORDER_ASC], - ], - [ - '$id' => ID::custom('_key_runtime'), - 'type' => Database::INDEX_KEY, - 'attributes' => ['runtime'], - 'lengths' => [768], - 'orders' => [Database::ORDER_ASC], - ], - [ - '$id' => ID::custom('_key_deployment'), - 'type' => Database::INDEX_KEY, - 'attributes' => ['deployment'], - 'lengths' => [], - 'orders' => [Database::ORDER_ASC], - ], - [ - '$id' => ID::custom('_key_schedule'), - 'type' => Database::INDEX_KEY, - 'attributes' => ['schedule'], - 'lengths' => [], - 'orders' => [Database::ORDER_ASC], - ], - [ - '$id' => ID::custom('_key_scheduleNext'), - 'type' => Database::INDEX_KEY, - 'attributes' => ['scheduleNext'], - 'lengths' => [], - 'orders' => [Database::ORDER_ASC], - ], - [ - '$id' => ID::custom('_key_schedulePrevious'), - 'type' => Database::INDEX_KEY, - 'attributes' => ['schedulePrevious'], - 'lengths' => [], - 'orders' => [Database::ORDER_ASC], - ], - [ - '$id' => ID::custom('_key_timeout'), - 'type' => Database::INDEX_KEY, - 'attributes' => ['timeout'], - 'lengths' => [], - 'orders' => [Database::ORDER_ASC], - ], - ], - ], - - 'deployments' => [ - '$collection' => ID::custom(Database::METADATA), - '$id' => ID::custom('deployments'), - 'name' => 'Deployments', - 'attributes' => [ - [ - '$id' => ID::custom('resourceId'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => Database::LENGTH_KEY, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('resourceType'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => Database::LENGTH_KEY, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('buildId'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => Database::LENGTH_KEY, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - 'array' => false, - '$id' => ID::custom('entrypoint'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 2048, - 'signed' => true, - 'required' => false, - 'default' => null, - 'filters' => [], - ], - [ - '$id' => ID::custom('path'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 2048, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('size'), - 'type' => Database::VAR_INTEGER, - 'format' => '', - 'size' => 0, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('metadata'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 16384, // https://tools.ietf.org/html/rfc4288#section-4.2 - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => ['json'], - ], - [ - '$id' => ID::custom('chunksTotal'), - 'type' => Database::VAR_INTEGER, - 'format' => '', - 'size' => 0, - 'signed' => false, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('chunksUploaded'), - 'type' => Database::VAR_INTEGER, - 'format' => '', - 'size' => 0, - 'signed' => false, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('search'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 16384, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('activate'), - 'type' => Database::VAR_BOOLEAN, - 'format' => '', - 'size' => 0, - 'signed' => true, - 'required' => false, - 'default' => false, - 'array' => false, - 'filters' => [], - ] - ], - 'indexes' => [ - [ - '$id' => ID::custom('_key_resource'), - 'type' => Database::INDEX_KEY, - 'attributes' => ['resourceId'], - 'lengths' => [], - 'orders' => [Database::ORDER_ASC], - ], - [ - '$id' => ID::custom('_key_resource_type'), - 'type' => Database::INDEX_KEY, - 'attributes' => ['resourceType'], - 'lengths' => [], - 'orders' => [Database::ORDER_ASC], - ], - [ - '$id' => ID::custom('_key_search'), - 'type' => Database::INDEX_FULLTEXT, - 'attributes' => ['search'], - 'lengths' => [], - 'orders' => [], - ], - [ - '$id' => ID::custom('_key_entrypoint'), - 'type' => Database::INDEX_KEY, - 'attributes' => ['entrypoint'], - 'lengths' => [768], - 'orders' => [Database::ORDER_ASC], - ], - [ - '$id' => ID::custom('_key_size'), - 'type' => Database::INDEX_KEY, - 'attributes' => ['size'], - 'lengths' => [], - 'orders' => [Database::ORDER_ASC], - ], - [ - '$id' => ID::custom('_key_buildId'), - 'type' => Database::INDEX_KEY, - 'attributes' => ['buildId'], - 'lengths' => [], - 'orders' => [Database::ORDER_ASC], - ], - [ - '$id' => ID::custom('_key_activate'), - 'type' => Database::INDEX_KEY, - 'attributes' => ['activate'], - 'lengths' => [], - 'orders' => [Database::ORDER_ASC], - ], - ], - ], - - 'builds' => [ - '$collection' => ID::custom(Database::METADATA), - '$id' => ID::custom('builds'), - 'name' => 'Builds', - 'attributes' => [ - [ - '$id' => ID::custom('startTime'), - 'type' => Database::VAR_DATETIME, - 'format' => '', - 'size' => 0, - 'signed' => false, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => ['datetime'], - ], - [ - '$id' => ID::custom('endTime'), - 'type' => Database::VAR_DATETIME, - 'format' => '', - 'size' => 0, - 'signed' => false, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => ['datetime'], - ], - [ - '$id' => ID::custom('duration'), - 'type' => Database::VAR_INTEGER, - 'format' => '', - 'size' => 0, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('deploymentId'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => Database::LENGTH_KEY, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('runtime'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 2048, - 'signed' => true, - 'required' => true, - 'default' => '', - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('status'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 256, - 'signed' => true, - 'required' => true, - 'default' => 'processing', - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('outputPath'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 2048, - 'signed' => true, - 'required' => false, - 'default' => '', - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('stderr'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 1000000, - 'signed' => true, - 'required' => false, - 'default' => '', - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('stdout'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 1000000, - 'signed' => true, - 'required' => false, - 'default' => '', - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('sourceType'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 2048, - 'signed' => true, - 'required' => true, - 'default' => 'local', - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('source'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 2048, - 'signed' => true, - 'required' => true, - 'default' => '', - 'array' => false, - 'filters' => [], - ] - ], - 'indexes' => [ - [ - '$id' => ID::custom('_key_deployment'), - 'type' => Database::INDEX_KEY, - 'attributes' => ['deploymentId'], - 'lengths' => [Database::LENGTH_KEY], - 'orders' => [Database::ORDER_ASC], - ] - ], - ], - - 'executions' => [ - '$collection' => ID::custom(Database::METADATA), - '$id' => ID::custom('executions'), - 'name' => 'Executions', - 'attributes' => [ - [ - '$id' => ID::custom('functionId'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => Database::LENGTH_KEY, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('deploymentId'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => Database::LENGTH_KEY, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - 'array' => false, - '$id' => ID::custom('trigger'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 128, - 'signed' => true, - 'required' => false, - 'default' => null, - 'filters' => [], - ], - [ - '$id' => ID::custom('status'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 128, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('response'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 1000000, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('stderr'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 1000000, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('stdout'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 1000000, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('statusCode'), - 'type' => Database::VAR_INTEGER, - 'format' => '', - 'size' => 0, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('duration'), - 'type' => Database::VAR_FLOAT, - 'format' => '', - 'size' => 0, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('search'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 16384, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - ], - 'indexes' => [ - [ - '$id' => ID::custom('_key_function'), - 'type' => Database::INDEX_KEY, - 'attributes' => ['functionId'], - 'lengths' => [Database::LENGTH_KEY], - 'orders' => [Database::ORDER_ASC], - ], - [ - '$id' => ID::custom('_fulltext_search'), - 'type' => Database::INDEX_FULLTEXT, - 'attributes' => ['search'], - 'lengths' => [], - 'orders' => [], - ], - [ - '$id' => ID::custom('_key_trigger'), - 'type' => Database::INDEX_KEY, - 'attributes' => ['trigger'], - 'lengths' => [128], - 'orders' => [Database::ORDER_ASC], - ], - [ - '$id' => ID::custom('_key_status'), - 'type' => Database::INDEX_KEY, - 'attributes' => ['status'], - 'lengths' => [128], - 'orders' => [Database::ORDER_ASC], - ], - [ - '$id' => ID::custom('_key_statusCode'), - 'type' => Database::INDEX_KEY, - 'attributes' => ['statusCode'], - 'lengths' => [], - 'orders' => [Database::ORDER_ASC], - ], - [ - '$id' => ID::custom('_key_duration'), - 'type' => Database::INDEX_KEY, - 'attributes' => ['duration'], - 'lengths' => [], - 'orders' => [Database::ORDER_ASC], - ], - ], - ], - - 'certificates' => [ - '$collection' => ID::custom(Database::METADATA), - '$id' => ID::custom('certificates'), - 'name' => 'Certificates', - 'attributes' => [ - [ - '$id' => ID::custom('domain'), - 'type' => Database::VAR_STRING, - 'format' => '', - // The maximum total length of a domain name or number is 255 characters. - // https://datatracker.ietf.org/doc/html/rfc2821#section-4.5.3.1 - // https://datatracker.ietf.org/doc/html/rfc5321#section-4.5.3.1.2 - 'size' => 255, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('issueDate'), - 'type' => Database::VAR_DATETIME, - 'format' => '', - 'size' => 0, - 'signed' => false, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => ['datetime'], - ], - [ - '$id' => ID::custom('renewDate'), - 'type' => Database::VAR_DATETIME, - 'format' => '', - 'size' => 0, - 'signed' => false, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => ['datetime'], - ], - [ - '$id' => ID::custom('attempts'), - 'type' => Database::VAR_INTEGER, - 'format' => '', - 'size' => 0, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('log'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 16384, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('updated'), - 'type' => Database::VAR_DATETIME, - 'format' => '', - 'size' => 0, - 'signed' => false, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => ['datetime'], - ], - ], - 'indexes' => [ - [ - '$id' => ID::custom('_key_domain'), - 'type' => Database::INDEX_KEY, - 'attributes' => ['domain'], - 'lengths' => [255], - 'orders' => [Database::ORDER_ASC], - ], - ], - ], - - 'buckets' => [ - '$collection' => ID::custom(Database::METADATA), - '$id' => ID::custom('buckets'), - 'name' => 'Buckets', - 'attributes' => [ - [ - '$id' => ID::custom('enabled'), - 'type' => Database::VAR_BOOLEAN, - 'signed' => true, - 'size' => 0, - 'format' => '', - 'filters' => [], - 'required' => true, - 'array' => false, - ], - [ - '$id' => ID::custom('name'), - 'type' => Database::VAR_STRING, - 'signed' => true, - 'size' => 128, - 'format' => '', - 'filters' => [], - 'required' => true, - 'array' => false, - ], - [ - '$id' => ID::custom('fileSecurity'), - 'type' => Database::VAR_BOOLEAN, - 'signed' => true, - 'size' => 1, - 'format' => '', - 'filters' => [], - 'required' => false, - 'array' => false, - ], - [ - '$id' => ID::custom('maximumFileSize'), - 'type' => Database::VAR_INTEGER, - 'signed' => false, - 'size' => 8, - 'format' => '', - 'filters' => [], - 'required' => true, - 'array' => false, - ], - [ - '$id' => ID::custom('allowedFileExtensions'), - 'type' => Database::VAR_STRING, - 'signed' => true, - 'size' => 64, - 'format' => '', - 'filters' => [], - 'required' => true, - 'array' => true, - ], - [ - '$id' => 'compression', - 'type' => Database::VAR_STRING, - 'signed' => true, - 'size' => 10, - 'format' => '', - 'filters' => [], - 'required' => true, - 'array' => false, - ], - [ - '$id' => ID::custom('encryption'), - 'type' => Database::VAR_BOOLEAN, - 'signed' => true, - 'size' => 0, - 'format' => '', - 'filters' => [], - 'required' => true, - 'array' => false, - ], - [ - '$id' => ID::custom('antivirus'), - 'type' => Database::VAR_BOOLEAN, - 'signed' => true, - 'size' => 0, - 'format' => '', - 'filters' => [], - 'required' => true, - 'array' => false, - ], - [ - '$id' => ID::custom('search'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 16384, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - ], - 'indexes' => [ - [ - '$id' => ID::custom('_fulltext_name'), - 'type' => Database::INDEX_FULLTEXT, - 'attributes' => ['name'], - 'lengths' => [], - 'orders' => [Database::ORDER_ASC], - ], - [ - '$id' => ID::custom('_key_search'), - 'type' => Database::INDEX_FULLTEXT, - 'attributes' => ['search'], - 'lengths' => [], - 'orders' => [Database::ORDER_ASC], - ], - [ - '$id' => ID::custom('_key_enabled'), - 'type' => Database::INDEX_KEY, - 'attributes' => ['enabled'], - 'lengths' => [], - 'orders' => [Database::ORDER_ASC], - ], - [ - '$id' => ID::custom('_key_name'), - 'type' => Database::INDEX_KEY, - 'attributes' => ['name'], - 'lengths' => [], - 'orders' => [Database::ORDER_ASC], - ], - [ - '$id' => ID::custom('_key_fileSecurity'), - 'type' => Database::INDEX_KEY, - 'attributes' => ['fileSecurity'], - 'lengths' => [], - 'orders' => [Database::ORDER_ASC], - ], - [ - '$id' => ID::custom('_key_maximumFileSize'), - 'type' => Database::INDEX_KEY, - 'attributes' => ['maximumFileSize'], - 'lengths' => [], - 'orders' => [Database::ORDER_ASC], - ], - [ - '$id' => ID::custom('_key_encryption'), - 'type' => Database::INDEX_KEY, - 'attributes' => ['encryption'], - 'lengths' => [], - 'orders' => [Database::ORDER_ASC], - ], - [ - '$id' => ID::custom('_key_antivirus'), - 'type' => Database::INDEX_KEY, - 'attributes' => ['antivirus'], - 'lengths' => [], - 'orders' => [Database::ORDER_ASC], - ], - ] - ], - - 'stats' => [ - '$collection' => ID::custom(Database::METADATA), - '$id' => ID::custom('stats'), - 'name' => 'Stats', - 'attributes' => [ - [ - '$id' => ID::custom('metric'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 255, - 'signed' => true, - 'required' => true, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('region'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 255, - 'signed' => true, - 'required' => true, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('value'), - 'type' => Database::VAR_INTEGER, - 'format' => '', - 'size' => 8, - 'signed' => false, - 'required' => true, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('time'), - 'type' => Database::VAR_DATETIME, - 'format' => '', - 'size' => 0, - 'signed' => false, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => ['datetime'], - ], - [ - '$id' => ID::custom('period'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 4, - 'signed' => true, - 'required' => true, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('type'), - 'type' => Database::VAR_INTEGER, - 'format' => '', - 'size' => 1, - 'signed' => false, - 'required' => true, - 'default' => 0, // 0 -> count, 1 -> sum - 'array' => false, - 'filters' => [], - ], - ], - 'indexes' => [ - [ - '$id' => ID::custom('_key_time'), - 'type' => Database::INDEX_KEY, - 'attributes' => ['time'], - 'lengths' => [], - 'orders' => [Database::ORDER_DESC], - ], - [ - '$id' => ID::custom('_key_period_time'), - 'type' => Database::INDEX_KEY, - 'attributes' => ['period', 'time'], - 'lengths' => [], - 'orders' => [Database::ORDER_ASC], - ], - [ - '$id' => ID::custom('_key_metric_period_time'), - 'type' => Database::INDEX_KEY, - 'attributes' => ['metric', 'period', 'time'], - 'lengths' => [], - 'orders' => [Database::ORDER_DESC], - ], - ], - ], - - 'realtime' => [ - '$collection' => ID::custom(Database::METADATA), - '$id' => ID::custom('realtime'), - 'name' => 'Realtime Connections', - 'attributes' => [ - [ - '$id' => ID::custom('container'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => Database::LENGTH_KEY, - 'signed' => true, - 'required' => true, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('timestamp'), - 'type' => Database::VAR_DATETIME, - 'format' => '', - 'size' => 0, - 'signed' => false, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => ['datetime'], - ], - [ - '$id' => ID::custom('value'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 16384, - 'signed' => true, - 'required' => true, - 'default' => null, - 'array' => false, - 'filters' => [], //TODO: use json filter - ] - ], - 'indexes' => [ - [ - '$id' => ID::custom('_key_timestamp'), - 'type' => Database::INDEX_KEY, - 'attributes' => ['timestamp'], - 'lengths' => [], - 'orders' => [Database::ORDER_DESC], - ], - ] - ], - 'cache' => [ - '$collection' => Database::METADATA, - '$id' => 'cache', - 'name' => 'Cache', - 'attributes' => [ - [ - '$id' => 'resource', - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 255, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => 'accessedAt', - 'type' => Database::VAR_DATETIME, - 'format' => '', - 'size' => 0, - 'signed' => false, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => ['datetime'], - ], - [ - '$id' => 'signature', - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 255, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - ], - 'indexes' => [ - [ - '$id' => '_key_accessedAt', - 'type' => Database::INDEX_KEY, - 'attributes' => ['accessedAt'], - 'lengths' => [], - 'orders' => [], - ], - [ - '$id' => '_key_resource', - 'type' => Database::INDEX_KEY, - 'attributes' => ['resource'], - 'lengths' => [], - 'orders' => [], - ], - ], - ], - 'files' => [ - '$collection' => ID::custom('buckets'), - '$id' => ID::custom('files'), - '$name' => 'Files', - 'attributes' => [ - [ - 'array' => false, - '$id' => ID::custom('bucketId'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => Database::LENGTH_KEY, - 'signed' => true, - 'required' => false, - 'default' => null, - 'filters' => [], - ], - [ - '$id' => ID::custom('name'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 2048, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('path'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 2048, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('signature'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 2048, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('mimeType'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 255, // https://tools.ietf.org/html/rfc4288#section-4.2 - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('metadata'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 16384, // https://tools.ietf.org/html/rfc4288#section-4.2 - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => ['json'], - ], - [ - '$id' => ID::custom('sizeOriginal'), - 'type' => Database::VAR_INTEGER, - 'format' => '', - 'size' => 8, - 'signed' => false, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('sizeActual'), - 'type' => Database::VAR_INTEGER, - 'format' => '', - 'size' => 8, - 'signed' => false, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('algorithm'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 255, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('comment'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 2048, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('openSSLVersion'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 64, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('openSSLCipher'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 64, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('openSSLTag'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 2048, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('openSSLIV'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 2048, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('chunksTotal'), - 'type' => Database::VAR_INTEGER, - 'format' => '', - 'size' => 0, - 'signed' => false, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('chunksUploaded'), - 'type' => Database::VAR_INTEGER, - 'format' => '', - 'size' => 0, - 'signed' => false, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('search'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 16384, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - ], - 'indexes' => [ - [ - '$id' => ID::custom('_key_search'), - 'type' => Database::INDEX_FULLTEXT, - 'attributes' => ['search'], - 'lengths' => [], - 'orders' => [Database::ORDER_ASC], - ], - [ - '$id' => ID::custom('_key_bucket'), - 'type' => Database::INDEX_KEY, - 'attributes' => ['bucketId'], - 'lengths' => [Database::LENGTH_KEY], - 'orders' => [Database::ORDER_ASC], - ], - [ - '$id' => ID::custom('_key_name'), - 'type' => Database::INDEX_KEY, - 'attributes' => ['name'], - 'lengths' => [768], - 'orders' => [Database::ORDER_ASC], - ], - [ - '$id' => ID::custom('_key_signature'), - 'type' => Database::INDEX_KEY, - 'attributes' => ['signature'], - 'lengths' => [768], - 'orders' => [Database::ORDER_ASC], - ], - [ - '$id' => ID::custom('_key_mimeType'), - 'type' => Database::INDEX_KEY, - 'attributes' => ['mimeType'], - 'lengths' => [], - 'orders' => [Database::ORDER_ASC], - ], - [ - '$id' => ID::custom('_key_sizeOriginal'), - 'type' => Database::INDEX_KEY, - 'attributes' => ['sizeOriginal'], - 'lengths' => [], - 'orders' => [Database::ORDER_ASC], - ], - [ - '$id' => ID::custom('_key_chunksTotal'), - 'type' => Database::INDEX_KEY, - 'attributes' => ['chunksTotal'], - 'lengths' => [], - 'orders' => [Database::ORDER_ASC], - ], - [ - '$id' => ID::custom('_key_chunksUploaded'), - 'type' => Database::INDEX_KEY, - 'attributes' => ['chunksUploaded'], - 'lengths' => [], - 'orders' => [Database::ORDER_ASC], - ], - ] - ], - - 'variables' => [ - '$collection' => Database::METADATA, - '$id' => 'variables', - 'name' => 'variables', - 'attributes' => [ - [ - '$id' => 'functionInternalId', - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => Database::LENGTH_KEY, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => 'functionId', - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => Database::LENGTH_KEY, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => 'key', - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => Database::LENGTH_KEY, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => 'value', - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 8192, - 'signed' => true, - 'required' => true, - 'default' => null, - 'array' => false, - 'filters' => [ 'encrypt' ] - ], - [ - '$id' => ID::custom('search'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 16384, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - ], - 'indexes' => [ - [ - '$id' => '_key_function', - 'type' => Database::INDEX_KEY, - 'attributes' => ['functionInternalId'], - 'lengths' => [Database::LENGTH_KEY], - 'orders' => [Database::ORDER_ASC], - ], - [ - '$id' => '_key_uniqueKey', - 'type' => Database::INDEX_UNIQUE, - 'attributes' => ['functionInternalId', 'key'], - 'lengths' => [Database::LENGTH_KEY, Database::LENGTH_KEY], - 'orders' => [Database::ORDER_ASC, Database::ORDER_ASC], - ], - [ - '$id' => '_key_key', - 'type' => Database::INDEX_KEY, - 'attributes' => ['key'], - 'lengths' => [Database::LENGTH_KEY], - 'orders' => [Database::ORDER_ASC], - ], - [ - '$id' => ID::custom('_fulltext_search'), - 'type' => Database::INDEX_FULLTEXT, - 'attributes' => ['search'], - 'lengths' => [], - 'orders' => [], - ], - ], - ], -]; - -return $collections; From c0815d4b3b0007ed58866db6beab80e888957bc6 Mon Sep 17 00:00:00 2001 From: fogelito Date: Thu, 18 May 2023 10:46:24 +0300 Subject: [PATCH 23/43] Remove array to document function --- tests/Database/Validator/IndexTest.php | 36 -------------------------- 1 file changed, 36 deletions(-) diff --git a/tests/Database/Validator/IndexTest.php b/tests/Database/Validator/IndexTest.php index b0f6b43f7..963bcd3f3 100644 --- a/tests/Database/Validator/IndexTest.php +++ b/tests/Database/Validator/IndexTest.php @@ -19,42 +19,6 @@ public function tearDown(): void { } - /** - * @param array $collection - * @return Document - * @throws Exception - */ - public function collectionArrayToDocuments(array $collection): Document - { - $document = new Document(); - - foreach ($collection['attributes'] as $attribute) { - $document->setAttribute('attributes', new Document([ - '$id' => ID::custom($attribute['$id']), - 'type' => $attribute['type'], - 'size' => $attribute['size'], - 'required' => $attribute['required'], - 'signed' => $attribute['signed'], - 'array' => $attribute['array'], - 'filters' => $attribute['filters'], - 'default' => $attribute['default'] ?? null, - 'format' => $attribute['format'] ?? '' - ]), Document::SET_TYPE_APPEND); - } - - foreach ($collection['indexes'] as $index) { - $document->setAttribute('indexes', new Document([ - '$id' => ID::custom($index['$id']), - 'type' => $index['type'], - 'attributes' => $index['attributes'], - 'lengths' => $index['lengths'], - 'orders' => $index['orders'], - ]), Document::SET_TYPE_APPEND); - } - - return $document; - } - /** * @throws Exception */ From 1502b9353fba6a2c3e06dabc0155ce5445303045 Mon Sep 17 00:00:00 2001 From: fogelito Date: Thu, 18 May 2023 10:51:41 +0300 Subject: [PATCH 24/43] Defualt number for index length --- src/Database/Validator/Index.php | 2 +- tests/Database/Base.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Database/Validator/Index.php b/src/Database/Validator/Index.php index 4b02863e2..f7928ee7a 100644 --- a/src/Database/Validator/Index.php +++ b/src/Database/Validator/Index.php @@ -20,7 +20,7 @@ class Index extends Validator /** * @param int $maxLength */ - public function __construct(int $maxLength) + public function __construct(int $maxLength = 768) { $this->maxLength = $maxLength; } diff --git a/tests/Database/Base.php b/tests/Database/Base.php index 4e528519f..4a6d8eab6 100644 --- a/tests/Database/Base.php +++ b/tests/Database/Base.php @@ -124,7 +124,7 @@ public function testIndexValidation(): void 'indexes' => $indexes ]); - $validator = new Index(768); + $validator = new Index(); $errorMessage = 'Index length("301") is longer than the key part for "title1("300")"'; $this->assertFalse($validator->isValid($collection)); From 34e33d40cab07a3cbe97ef8ac50f401699cc4b84 Mon Sep 17 00:00:00 2001 From: fogelito Date: Thu, 18 May 2023 11:14:56 +0300 Subject: [PATCH 25/43] stopOnFailure revert --- phpunit.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/phpunit.xml b/phpunit.xml index 3833748e0..31b947dd6 100755 --- a/phpunit.xml +++ b/phpunit.xml @@ -7,7 +7,7 @@ convertNoticesToExceptions="true" convertWarningsToExceptions="true" processIsolation="false" - stopOnFailure="true"> + stopOnFailure="false"> ./tests/ From d9b82e8b2b64430932c7c89123b7720e1185ac13 Mon Sep 17 00:00:00 2001 From: fogelito Date: Wed, 24 May 2023 13:08:50 +0300 Subject: [PATCH 26/43] composer.lock --- composer.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/composer.lock b/composer.lock index 3985fdb08..bea5328f7 100644 --- a/composer.lock +++ b/composer.lock @@ -705,16 +705,16 @@ }, { "name": "nikic/php-parser", - "version": "v4.15.4", + "version": "v4.15.5", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "6bb5176bc4af8bcb7d926f88718db9b96a2d4290" + "reference": "11e2663a5bc9db5d714eedb4277ee300403b4a9e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/6bb5176bc4af8bcb7d926f88718db9b96a2d4290", - "reference": "6bb5176bc4af8bcb7d926f88718db9b96a2d4290", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/11e2663a5bc9db5d714eedb4277ee300403b4a9e", + "reference": "11e2663a5bc9db5d714eedb4277ee300403b4a9e", "shasum": "" }, "require": { @@ -755,9 +755,9 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v4.15.4" + "source": "https://github.com/nikic/PHP-Parser/tree/v4.15.5" }, - "time": "2023-03-05T19:49:14+00:00" + "time": "2023-05-19T20:20:00+00:00" }, { "name": "pcov/clobber", From 54feb3a38bf63a0048d79cf812eb54bda2939a30 Mon Sep 17 00:00:00 2001 From: fogelito Date: Wed, 24 May 2023 13:45:58 +0300 Subject: [PATCH 27/43] Merge main in --- composer.lock | 7 ++----- src/Database/Database.php | 7 ++++--- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/composer.lock b/composer.lock index bea5328f7..ff525b0a8 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", @@ -2672,9 +2672,6 @@ "ext-pdo": "*", "php": ">=8.0" }, - "platform-dev": { - "ext-redis": "*", - "ext-mongodb": "*" - }, + "platform-dev": [], "plugin-api-version": "2.2.0" } diff --git a/src/Database/Database.php b/src/Database/Database.php index a86858a22..2bfec2719 100644 --- a/src/Database/Database.php +++ b/src/Database/Database.php @@ -1974,6 +1974,7 @@ public function renameIndex(string $collection, string $old, string $new): bool * @throws DuplicateException * @throws LimitException * @throws StructureException + * @throws Exception */ public function createIndex(string $collection, string $id, string $type, array $attributes, array $lengths = [], array $orders = []): bool { @@ -1983,8 +1984,8 @@ public function createIndex(string $collection, string $id, string $type, array $collection = $this->silent(fn () => $this->getCollection($collection)); - $validator = new IndexValidator($collection); - if (!$validator->isValid(['type' => $type, 'attributes' => $attributes])) { + $validator = new IndexValidator($this->adapter->maxIndexLength); + if (!$validator->isValid($collection)) { throw new DatabaseException($validator->getDescription()); } @@ -2036,7 +2037,7 @@ public function createIndex(string $collection, string $id, string $type, array $validator = new IndexValidator($this->adapter->maxIndexLength); if (!$validator->isValid($collection)) { - throw new Exception($validator->getDescription()); + throw new DatabaseException($validator->getDescription()); } $index = $this->adapter->createIndex($collection->getId(), $id, $type, $attributes, $lengths, $orders); From 49afc6759a733219df50fec768b30bd93142ffa6 Mon Sep 17 00:00:00 2001 From: fogelito Date: Wed, 24 May 2023 13:56:44 +0300 Subject: [PATCH 28/43] DatabaseException --- src/Database/Adapter/MySQL.php | 4 +--- src/Database/Database.php | 5 ++--- src/Database/Validator/Index.php | 8 ++++---- 3 files changed, 7 insertions(+), 10 deletions(-) diff --git a/src/Database/Adapter/MySQL.php b/src/Database/Adapter/MySQL.php index 1c238a3de..b685b1eb1 100644 --- a/src/Database/Adapter/MySQL.php +++ b/src/Database/Adapter/MySQL.php @@ -20,9 +20,7 @@ class MySQL extends MariaDB * @param array $attributes * * @return string - * @throws Exception - * @throws Exception - * @throws Exception + * @throws DatabaseException */ protected function getSQLIndex(string $collection, string $id, string $type, array $attributes): string { diff --git a/src/Database/Database.php b/src/Database/Database.php index 2bfec2719..d60f8cd49 100644 --- a/src/Database/Database.php +++ b/src/Database/Database.php @@ -620,8 +620,7 @@ public function delete(string $name): bool * @param bool $documentSecurity * * @return Document - * @throws DuplicateException - * @throws Exception|Throwable + * @throws DatabaseException */ public function createCollection(string $id, array $attributes = [], array $indexes = [], array $permissions = null, bool $documentSecurity = true): Document { @@ -651,7 +650,7 @@ public function createCollection(string $id, array $attributes = [], array $inde $validator = new IndexValidator($this->adapter->maxIndexLength); if (!$validator->isValid($collection)) { - throw new Exception($validator->getDescription()); + throw new DatabaseException($validator->getDescription()); } $this->adapter->createCollection($id, $attributes, $indexes); diff --git a/src/Database/Validator/Index.php b/src/Database/Validator/Index.php index f7928ee7a..c1dd3c094 100644 --- a/src/Database/Validator/Index.php +++ b/src/Database/Validator/Index.php @@ -2,8 +2,8 @@ namespace Utopia\Database\Validator; -use Exception; use Utopia\Database\Database; +use Utopia\Database\Exception as DatabaseException; use Utopia\Validator; use Utopia\Database\Document; @@ -37,7 +37,6 @@ public function getDescription(): string /** * @param Document $collection * @return bool - * @throws Exception */ public function checkEmptyIndexAttributes(Document $collection): bool { @@ -54,7 +53,7 @@ public function checkEmptyIndexAttributes(Document $collection): bool /** * @param Document $collection * @return bool - * @throws Exception + * @throws DatabaseException */ public function checkFulltextIndexNonString(Document $collection): bool { @@ -130,7 +129,7 @@ public function checkIndexLength(Document $collection): bool * Returns true index if valid. * @param Document $value * @return bool - * @throws Exception + * @throws DatabaseException */ public function isValid($value): bool { @@ -156,6 +155,7 @@ public function isValid($value): bool return true; } + /** * Is array * From 66ae30abbcd06181f38e51a5e0e4bb27eb274d1d Mon Sep 17 00:00:00 2001 From: fogelito Date: Sun, 28 May 2023 14:59:17 +0300 Subject: [PATCH 29/43] getMaxVarcharLength --- src/Database/Adapter/MariaDB.php | 10 +++++++++- src/Database/Adapter/MySQL.php | 10 ++++++++-- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/Database/Adapter/MariaDB.php b/src/Database/Adapter/MariaDB.php index 3bd4843e6..d1ce38587 100644 --- a/src/Database/Adapter/MariaDB.php +++ b/src/Database/Adapter/MariaDB.php @@ -1279,7 +1279,7 @@ protected function getSQLType(string $type, int $size, bool $signed = true): str return 'MEDIUMTEXT'; } - if ($size > $this->maxVarcharLength) { + if ($size > $this->getMaxVarcharLength()) { return 'TEXT'; } @@ -1406,4 +1406,12 @@ protected function processException(PDOException $e): void throw $e; } + + /** + * @return int + */ + protected function getMaxVarcharLength(): int + { + return 16382; + } } diff --git a/src/Database/Adapter/MySQL.php b/src/Database/Adapter/MySQL.php index b685b1eb1..8d3746dd3 100644 --- a/src/Database/Adapter/MySQL.php +++ b/src/Database/Adapter/MySQL.php @@ -9,8 +9,6 @@ class MySQL extends MariaDB { - protected int $maxVarcharLength = 16381; - /** * Get SQL Index * @@ -80,4 +78,12 @@ protected function processException(PDOException $e): void throw $e; } + + /** + * @return int + */ + protected function getMaxVarcharLength(): int + { + return 16382; + } } From e3951754763109590d1746c09c2a59f5c2e54ca9 Mon Sep 17 00:00:00 2001 From: fogelito Date: Sun, 28 May 2023 15:17:25 +0300 Subject: [PATCH 30/43] getMaxIndexLength --- src/Database/Adapter.php | 13 ++++++++----- src/Database/Database.php | 15 ++++++++++----- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/src/Database/Adapter.php b/src/Database/Adapter.php index 5648ab095..3f115bee4 100644 --- a/src/Database/Adapter.php +++ b/src/Database/Adapter.php @@ -22,11 +22,6 @@ abstract class Adapter */ protected array $debug = []; - /** - * @var int - */ - public int $maxIndexLength = 768; - /** * @param string $key * @param mixed $value @@ -641,4 +636,12 @@ public function escapeWildcards(string $value): string * @throws Exception */ abstract public function increaseDocumentAttribute(string $collection, string $id, string $attribute, int|float $value, int|float|null $min = null, int|float|null $max = null): bool; + + /** + * @return int + */ + public function getMaxIndexLength(): int + { + return 768; + } } diff --git a/src/Database/Database.php b/src/Database/Database.php index b95eae0c5..af3190f9b 100644 --- a/src/Database/Database.php +++ b/src/Database/Database.php @@ -618,9 +618,11 @@ public function delete(string $name): bool * @param array $indexes * @param array $permissions * @param bool $documentSecurity - * * @return Document * @throws DatabaseException + * @throws DuplicateException + * @throws InvalidArgumentException + * @throws LimitException */ public function createCollection(string $id, array $attributes = [], array $indexes = [], array $permissions = null, bool $documentSecurity = true): Document { @@ -648,7 +650,7 @@ public function createCollection(string $id, array $attributes = [], array $inde 'documentSecurity' => $documentSecurity ]); - $validator = new IndexValidator($this->adapter->maxIndexLength); + $validator = new IndexValidator($this->adapter->getMaxIndexLength()); if (!$validator->isValid($collection)) { throw new DatabaseException($validator->getDescription()); } @@ -696,7 +698,10 @@ public function createCollection(string $id, array $attributes = [], array $inde * @param bool $documentSecurity * * @return Document - * @throws DuplicateException + * @throws InvalidArgumentException + * @throws ConflictException + * @throws DatabaseException + * @throws InvalidArgumentException */ public function updateCollection(string $id, array $permissions, bool $documentSecurity): Document { @@ -1990,7 +1995,7 @@ public function createIndex(string $collection, string $id, string $type, array $collection = $this->silent(fn () => $this->getCollection($collection)); - $validator = new IndexValidator($this->adapter->maxIndexLength); + $validator = new IndexValidator($this->adapter->getMaxIndexLength()); if (!$validator->isValid($collection)) { throw new DatabaseException($validator->getDescription()); } @@ -2041,7 +2046,7 @@ public function createIndex(string $collection, string $id, string $type, array 'orders' => $orders, ]), Document::SET_TYPE_APPEND); - $validator = new IndexValidator($this->adapter->maxIndexLength); + $validator = new IndexValidator($this->adapter->getMaxIndexLength()); if (!$validator->isValid($collection)) { throw new DatabaseException($validator->getDescription()); } From e8aa076d1a4a7b31eb90ecdf30212f7166a6d723 Mon Sep 17 00:00:00 2001 From: Shmuel Fogel Date: Sun, 28 May 2023 15:18:28 +0300 Subject: [PATCH 31/43] Update src/Database/Validator/Index.php Co-authored-by: Jake Barnby --- src/Database/Validator/Index.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Database/Validator/Index.php b/src/Database/Validator/Index.php index c1dd3c094..ba260161f 100644 --- a/src/Database/Validator/Index.php +++ b/src/Database/Validator/Index.php @@ -62,7 +62,7 @@ public function checkFulltextIndexNonString(Document $collection): bool foreach ($index->getAttribute('attributes', []) as $attributeName) { $attribute = $this->attributes[$attributeName] ?? new Document([]); if ($attribute->getAttribute('type', '') !== Database::VAR_STRING) { - $this->message = 'Attribute "'.$attribute->getAttribute('key', $attribute->getAttribute('$id')).'" cannot be part of a FULLTEXT index'; + $this->message = 'Attribute "'.$attribute->getAttribute('key', $attribute->getAttribute('$id')).'" cannot be part of a FULLTEXT index, must be of type string'; return false; } } From 1a28e0eebfe25d3319ed01aa2e7d333389310b16 Mon Sep 17 00:00:00 2001 From: fogelito Date: Sun, 28 May 2023 16:15:04 +0300 Subject: [PATCH 32/43] getMaxVarcharLength Sql.php --- src/Database/Adapter/MariaDB.php | 10 ---------- src/Database/Adapter/MySQL.php | 8 -------- src/Database/Adapter/Postgres.php | 2 +- src/Database/Adapter/SQL.php | 10 +++++++++- 4 files changed, 10 insertions(+), 20 deletions(-) diff --git a/src/Database/Adapter/MariaDB.php b/src/Database/Adapter/MariaDB.php index d1ce38587..113197c31 100644 --- a/src/Database/Adapter/MariaDB.php +++ b/src/Database/Adapter/MariaDB.php @@ -15,8 +15,6 @@ class MariaDB extends SQL { - protected int $maxVarcharLength = 16382; - /** * Create Database * @@ -1406,12 +1404,4 @@ protected function processException(PDOException $e): void throw $e; } - - /** - * @return int - */ - protected function getMaxVarcharLength(): int - { - return 16382; - } } diff --git a/src/Database/Adapter/MySQL.php b/src/Database/Adapter/MySQL.php index 8d3746dd3..6bc4e9b0f 100644 --- a/src/Database/Adapter/MySQL.php +++ b/src/Database/Adapter/MySQL.php @@ -78,12 +78,4 @@ protected function processException(PDOException $e): void throw $e; } - - /** - * @return int - */ - protected function getMaxVarcharLength(): int - { - return 16382; - } } diff --git a/src/Database/Adapter/Postgres.php b/src/Database/Adapter/Postgres.php index fb569c85f..1ab754ee2 100644 --- a/src/Database/Adapter/Postgres.php +++ b/src/Database/Adapter/Postgres.php @@ -1270,7 +1270,7 @@ protected function getSQLType(string $type, int $size, bool $signed = true): str switch ($type) { case Database::VAR_STRING: // $size = $size * 4; // Convert utf8mb4 size to bytes - if ($size > 16383) { + if ($size > $this->getMaxVarcharLength()) { return 'TEXT'; } diff --git a/src/Database/Adapter/SQL.php b/src/Database/Adapter/SQL.php index a5d877743..2a247129e 100644 --- a/src/Database/Adapter/SQL.php +++ b/src/Database/Adapter/SQL.php @@ -314,7 +314,7 @@ public function getAttributeWidth(Document $collection): int $total += 11; break; - case ($attribute['size'] > 16381): + case ($attribute['size'] > $this->getMaxVarcharLength()): // 8 bytes length + 2 bytes for TEXT $total += 10; break; @@ -865,4 +865,12 @@ public static function getPDOAttributes(): array PDO::ATTR_STRINGIFY_FETCHES => true // Returns all fetched data as Strings ]; } + + /** + * @return int + */ + public function getMaxVarcharLength(): int + { + return 16381; // Floor value for Postgres:16383 | MySQL:16381 | MariaDB:16382 + } } From 16dad2700b2cd22e7b1236e49a80503e4345cb3d Mon Sep 17 00:00:00 2001 From: Shmuel Fogel Date: Sun, 28 May 2023 16:18:05 +0300 Subject: [PATCH 33/43] Update src/Database/Validator/Index.php Co-authored-by: Jake Barnby --- src/Database/Validator/Index.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Database/Validator/Index.php b/src/Database/Validator/Index.php index ba260161f..91e6db874 100644 --- a/src/Database/Validator/Index.php +++ b/src/Database/Validator/Index.php @@ -107,7 +107,7 @@ public function checkIndexLength(Document $collection): bool } if ($indexLength > $attributeSize) { - $this->message = 'Index length("'.$indexLength.'") is longer than the key part for "'.$attributeName.'("'.$attributeSize.'")"'; + $this->message = 'Index length '.$indexLength.' is larger than the size for '.$attributeName.': '.$attributeSize.'"'; return false; } From e9f0dbe13be476f2a9cc571a70696ba0bb776403 Mon Sep 17 00:00:00 2001 From: Shmuel Fogel Date: Sun, 28 May 2023 16:18:28 +0300 Subject: [PATCH 34/43] Update src/Database/Validator/Index.php Co-authored-by: Jake Barnby --- src/Database/Validator/Index.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Database/Validator/Index.php b/src/Database/Validator/Index.php index 91e6db874..befb8c7c3 100644 --- a/src/Database/Validator/Index.php +++ b/src/Database/Validator/Index.php @@ -115,7 +115,7 @@ public function checkIndexLength(Document $collection): bool } if ($total > $this->maxLength) { - $this->message = 'Index Length is longer than max (' . $this->maxLength . '))'; + $this->message = 'Index length is longer than the maximum: ' . $this->maxLength; return false; } } From a23a841e4e51b42dbdd68668af458ea8bc590097 Mon Sep 17 00:00:00 2001 From: fogelito Date: Sun, 28 May 2023 16:27:48 +0300 Subject: [PATCH 35/43] Message change --- tests/Database/Base.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Database/Base.php b/tests/Database/Base.php index aa18ff48a..3743b2003 100644 --- a/tests/Database/Base.php +++ b/tests/Database/Base.php @@ -189,7 +189,7 @@ public function testIndexValidation(): void 'indexes' => $indexes ]); - $errorMessage = 'Attribute "integer" cannot be part of a FULLTEXT index'; + $errorMessage = 'Attribute "integer" cannot be part of a FULLTEXT index, must be of type string'; $this->assertFalse($validator->isValid($collection)); $this->assertEquals($errorMessage, $validator->getDescription()); From 5a04112ded59b5db03bb6eeb0fb5224f2f59bf51 Mon Sep 17 00:00:00 2001 From: fogelito Date: Sun, 28 May 2023 16:41:38 +0300 Subject: [PATCH 36/43] Text changes --- tests/Database/Base.php | 6 +++--- tests/Database/Validator/IndexTest.php | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/Database/Base.php b/tests/Database/Base.php index 3743b2003..ee33cb7eb 100644 --- a/tests/Database/Base.php +++ b/tests/Database/Base.php @@ -126,7 +126,7 @@ public function testIndexValidation(): void $validator = new Index(); - $errorMessage = 'Index length("301") is longer than the key part for "title1("300")"'; + $errorMessage = 'Index length 301 is larger than the size for title1: 300"'; $this->assertFalse($validator->isValid($collection)); $this->assertEquals($errorMessage, $validator->getDescription()); @@ -149,7 +149,7 @@ public function testIndexValidation(): void $collection->setAttribute('indexes', $indexes); - $errorMessage = 'Index Length is longer than max (768))'; + $errorMessage = 'Index length is longer than the maximum: 768'; $this->assertFalse($validator->isValid($collection)); $this->assertEquals($errorMessage, $validator->getDescription()); @@ -971,7 +971,7 @@ public function testFulltextIndexWithInteger(): void if (!$this->getDatabase()->getAdapter()->getSupportForFulltextIndex()) { $this->expectExceptionMessage('Fulltext index is not supported'); } else { - $this->expectExceptionMessage('Attribute "integer" cannot be part of a FULLTEXT index'); + $this->expectExceptionMessage('Attribute "integer" cannot be part of a FULLTEXT index, must be of type string'); } static::getDatabase()->createIndex('documents', 'fulltext_integer', Database::INDEX_FULLTEXT, ['string','integer']); diff --git a/tests/Database/Validator/IndexTest.php b/tests/Database/Validator/IndexTest.php index 963bcd3f3..eb469a465 100644 --- a/tests/Database/Validator/IndexTest.php +++ b/tests/Database/Validator/IndexTest.php @@ -65,7 +65,7 @@ public function testFulltextWithNonString(): void ]); $this->assertFalse($validator->isValid($collection)); - $this->assertEquals('Attribute "date" cannot be part of a FULLTEXT index', $validator->getDescription()); + $this->assertEquals('Attribute "date" cannot be part of a FULLTEXT index, must be of type string', $validator->getDescription()); } /** @@ -103,7 +103,7 @@ public function testIndexLength(): void ]); $this->assertFalse($validator->isValid($collection)); - $this->assertEquals('Index Length is longer than max (768))', $validator->getDescription()); + $this->assertEquals('Index length is longer than the maximum: 768', $validator->getDescription()); } /** From c2eba1d0f2e124ff8b83e2f877ff92f51239b0c7 Mon Sep 17 00:00:00 2001 From: fogelito Date: Mon, 29 May 2023 18:34:28 +0300 Subject: [PATCH 37/43] Duplicated attributes --- src/Database/Validator/Index.php | 21 ++++++++++++++ tests/Database/Validator/IndexTest.php | 38 ++++++++++++++++++++++++++ 2 files changed, 59 insertions(+) diff --git a/src/Database/Validator/Index.php b/src/Database/Validator/Index.php index befb8c7c3..78cf99a3e 100644 --- a/src/Database/Validator/Index.php +++ b/src/Database/Validator/Index.php @@ -50,6 +50,23 @@ public function checkEmptyIndexAttributes(Document $collection): bool return true; } + /** + * @param Document $collection + * @return bool + */ + public function checkDuplicatedAttributes(Document $collection): bool + { + foreach ($collection->getAttribute('indexes', []) as $index) { + $attributes = $index->getAttribute('attributes', []); + if(count($attributes) !== count(array_unique($attributes))){ + $this->message = 'Duplicated attributes provided'; + return false; + } + } + + return true; + } + /** * @param Document $collection * @return bool @@ -145,6 +162,10 @@ public function isValid($value): bool return false; } + if (!$this->checkDuplicatedAttributes($value)) { + return false; + } + if (!$this->checkFulltextIndexNonString($value)) { return false; } diff --git a/tests/Database/Validator/IndexTest.php b/tests/Database/Validator/IndexTest.php index eb469a465..c20244eff 100644 --- a/tests/Database/Validator/IndexTest.php +++ b/tests/Database/Validator/IndexTest.php @@ -143,4 +143,42 @@ public function testEmptyAttributes(): void $this->assertFalse($validator->isValid($collection)); $this->assertEquals('No attributes provided for index', $validator->getDescription()); } + + /** + * @throws Exception + */ + public function testDuplicatedAttributes(): void + { + $validator = new Index(768); + + $collection = new Document([ + '$id' => ID::custom('test'), + 'name' => 'test', + 'attributes' => [ + new Document([ + '$id' => ID::custom('title'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 255, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ]) + ], + 'indexes' => [ + new Document([ + '$id' => ID::custom('index1'), + 'type' => Database::INDEX_FULLTEXT, + 'attributes' => ['title', 'title'], + 'lengths' => [], + 'orders' => [], + ]), + ], + ]); + + $this->assertFalse($validator->isValid($collection)); + $this->assertEquals('Duplicated attributes provided', $validator->getDescription()); + } } From ecbd4d0eced0048f41e06c1ac433431b8c13a2e1 Mon Sep 17 00:00:00 2001 From: fogelito Date: Mon, 29 May 2023 18:36:22 +0300 Subject: [PATCH 38/43] lint --- src/Database/Validator/Index.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Database/Validator/Index.php b/src/Database/Validator/Index.php index 78cf99a3e..8da801527 100644 --- a/src/Database/Validator/Index.php +++ b/src/Database/Validator/Index.php @@ -58,7 +58,7 @@ public function checkDuplicatedAttributes(Document $collection): bool { foreach ($collection->getAttribute('indexes', []) as $index) { $attributes = $index->getAttribute('attributes', []); - if(count($attributes) !== count(array_unique($attributes))){ + if (count($attributes) !== count(array_unique($attributes))) { $this->message = 'Duplicated attributes provided'; return false; } From ab1f7f99bea4fbadd6782afbb9994e2e92d03cc1 Mon Sep 17 00:00:00 2001 From: Shmuel Fogel Date: Sun, 4 Jun 2023 11:31:47 +0300 Subject: [PATCH 39/43] Update src/Database/Validator/Index.php Co-authored-by: Jake Barnby --- src/Database/Validator/Index.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Database/Validator/Index.php b/src/Database/Validator/Index.php index 8da801527..61ce5789d 100644 --- a/src/Database/Validator/Index.php +++ b/src/Database/Validator/Index.php @@ -59,7 +59,7 @@ public function checkDuplicatedAttributes(Document $collection): bool foreach ($collection->getAttribute('indexes', []) as $index) { $attributes = $index->getAttribute('attributes', []); if (count($attributes) !== count(array_unique($attributes))) { - $this->message = 'Duplicated attributes provided'; + $this->message = 'Duplicate attributes provided'; return false; } } From 8a1173641f9c4eb7a9f2601e4b7503540998f36a Mon Sep 17 00:00:00 2001 From: fogelito Date: Sun, 4 Jun 2023 13:30:31 +0300 Subject: [PATCH 40/43] getMaxIndexLength Mongo.php fix --- composer.lock | 29 +++++++++++++------------- src/Database/Adapter.php | 5 +---- src/Database/Adapter/Mongo.php | 8 +++++++ src/Database/Adapter/SQL.php | 8 +++++++ src/Database/Validator/Index.php | 4 ++-- tests/Database/Base.php | 28 +++++++++++++------------ tests/Database/Validator/IndexTest.php | 2 +- 7 files changed, 50 insertions(+), 34 deletions(-) diff --git a/composer.lock b/composer.lock index ff525b0a8..c385be719 100644 --- a/composer.lock +++ b/composer.lock @@ -336,23 +336,24 @@ }, { "name": "utopia-php/framework", - "version": "0.28.1", + "version": "0.28.4", "source": { "type": "git", "url": "https://github.com/utopia-php/framework.git", - "reference": "7f22c556fc5991e54e5811a68fb39809b21bda55" + "reference": "98c5469efe195aeecc63745dbf8e2f357f8cedac" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/framework/zipball/7f22c556fc5991e54e5811a68fb39809b21bda55", - "reference": "7f22c556fc5991e54e5811a68fb39809b21bda55", + "url": "https://api.github.com/repos/utopia-php/framework/zipball/98c5469efe195aeecc63745dbf8e2f357f8cedac", + "reference": "98c5469efe195aeecc63745dbf8e2f357f8cedac", "shasum": "" }, "require": { - "php": ">=8.0.0" + "php": ">=8.0" }, "require-dev": { "laravel/pint": "^1.2", + "phpstan/phpstan": "1.9.x-dev", "phpunit/phpunit": "^9.5.25", "vimeo/psalm": "4.27.0" }, @@ -374,9 +375,9 @@ ], "support": { "issues": "https://github.com/utopia-php/framework/issues", - "source": "https://github.com/utopia-php/framework/tree/0.28.1" + "source": "https://github.com/utopia-php/framework/tree/0.28.4" }, - "time": "2023-03-02T08:16:01+00:00" + "time": "2023-06-03T14:09:22+00:00" }, { "name": "utopia-php/mongo", @@ -2494,16 +2495,16 @@ }, { "name": "symfony/deprecation-contracts", - "version": "v3.2.1", + "version": "v3.3.0", "source": { "type": "git", "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "e2d1534420bd723d0ef5aec58a22c5fe60ce6f5e" + "reference": "7c3aff79d10325257a001fcf92d991f24fc967cf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/e2d1534420bd723d0ef5aec58a22c5fe60ce6f5e", - "reference": "e2d1534420bd723d0ef5aec58a22c5fe60ce6f5e", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/7c3aff79d10325257a001fcf92d991f24fc967cf", + "reference": "7c3aff79d10325257a001fcf92d991f24fc967cf", "shasum": "" }, "require": { @@ -2512,7 +2513,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "3.3-dev" + "dev-main": "3.4-dev" }, "thanks": { "name": "symfony/contracts", @@ -2541,7 +2542,7 @@ "description": "A generic function and convention to trigger deprecation notices", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v3.2.1" + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.3.0" }, "funding": [ { @@ -2557,7 +2558,7 @@ "type": "tidelift" } ], - "time": "2023-03-01T10:25:55+00:00" + "time": "2023-05-23T14:45:45+00:00" }, { "name": "theseer/tokenizer", diff --git a/src/Database/Adapter.php b/src/Database/Adapter.php index 3f115bee4..cead857ca 100644 --- a/src/Database/Adapter.php +++ b/src/Database/Adapter.php @@ -640,8 +640,5 @@ abstract public function increaseDocumentAttribute(string $collection, string $i /** * @return int */ - public function getMaxIndexLength(): int - { - return 768; - } + abstract public function getMaxIndexLength(): int; } diff --git a/src/Database/Adapter/Mongo.php b/src/Database/Adapter/Mongo.php index d44102edf..a68440e16 100644 --- a/src/Database/Adapter/Mongo.php +++ b/src/Database/Adapter/Mongo.php @@ -1650,4 +1650,12 @@ protected function processException(Exception $e): void throw $e; } + + /** + * @return int + */ + public function getMaxIndexLength(): int + { + return 0; + } } diff --git a/src/Database/Adapter/SQL.php b/src/Database/Adapter/SQL.php index 2a247129e..97f364653 100644 --- a/src/Database/Adapter/SQL.php +++ b/src/Database/Adapter/SQL.php @@ -873,4 +873,12 @@ public function getMaxVarcharLength(): int { return 16381; // Floor value for Postgres:16383 | MySQL:16381 | MariaDB:16382 } + + /** + * @return int + */ + public function getMaxIndexLength(): int + { + return 768; + } } diff --git a/src/Database/Validator/Index.php b/src/Database/Validator/Index.php index 61ce5789d..f5754f736 100644 --- a/src/Database/Validator/Index.php +++ b/src/Database/Validator/Index.php @@ -20,7 +20,7 @@ class Index extends Validator /** * @param int $maxLength */ - public function __construct(int $maxLength = 768) + public function __construct(int $maxLength) { $this->maxLength = $maxLength; } @@ -131,7 +131,7 @@ public function checkIndexLength(Document $collection): bool $total += $indexLength; } - if ($total > $this->maxLength) { + if ($total > $this->maxLength && $this->maxLength > 0) { $this->message = 'Index length is longer than the maximum: ' . $this->maxLength; return false; } diff --git a/tests/Database/Base.php b/tests/Database/Base.php index ee33cb7eb..829509838 100644 --- a/tests/Database/Base.php +++ b/tests/Database/Base.php @@ -87,7 +87,7 @@ public function testIndexValidation(): void '$id' => ID::custom('title1'), 'type' => Database::VAR_STRING, 'format' => '', - 'size' => 300, + 'size' => 700, 'signed' => true, 'required' => false, 'default' => null, @@ -112,7 +112,7 @@ public function testIndexValidation(): void '$id' => ID::custom('index1'), 'type' => Database::INDEX_KEY, 'attributes' => ['title1', 'title2'], - 'lengths' => [301,100], + 'lengths' => [701,50], 'orders' => [], ]), ]; @@ -124,9 +124,9 @@ public function testIndexValidation(): void 'indexes' => $indexes ]); - $validator = new Index(); + $validator = new Index(static::getDatabase()->getAdapter()->getMaxIndexLength()); - $errorMessage = 'Index length 301 is larger than the size for title1: 300"'; + $errorMessage = 'Index length 701 is larger than the size for title1: 700"'; $this->assertFalse($validator->isValid($collection)); $this->assertEquals($errorMessage, $validator->getDescription()); @@ -142,22 +142,24 @@ public function testIndexValidation(): void '$id' => ID::custom('index1'), 'type' => Database::INDEX_KEY, 'attributes' => ['title1', 'title2'], - 'lengths' => [300], + 'lengths' => [700], // 700, 500 (length(title2)) 'orders' => [], ]), ]; $collection->setAttribute('indexes', $indexes); - $errorMessage = 'Index length is longer than the maximum: 768'; - $this->assertFalse($validator->isValid($collection)); - $this->assertEquals($errorMessage, $validator->getDescription()); + if(static::getDatabase()->getAdapter()->getMaxIndexLength() > 0){ + $errorMessage = 'Index length is longer than the maximum: ' . static::getDatabase()->getAdapter()->getMaxIndexLength(); + $this->assertFalse($validator->isValid($collection)); + $this->assertEquals($errorMessage, $validator->getDescription()); - try { - static::getDatabase()->createCollection($collection->getId(), $attributes, $indexes); - $this->fail('Failed to throw exception'); - } catch (Exception $e) { - $this->assertEquals($errorMessage, $e->getMessage()); + try { + static::getDatabase()->createCollection($collection->getId(), $attributes, $indexes); + $this->fail('Failed to throw exception'); + } catch (Exception $e) { + $this->assertEquals($errorMessage, $e->getMessage()); + } } $attributes[] = new Document([ diff --git a/tests/Database/Validator/IndexTest.php b/tests/Database/Validator/IndexTest.php index c20244eff..bf9a9859a 100644 --- a/tests/Database/Validator/IndexTest.php +++ b/tests/Database/Validator/IndexTest.php @@ -179,6 +179,6 @@ public function testDuplicatedAttributes(): void ]); $this->assertFalse($validator->isValid($collection)); - $this->assertEquals('Duplicated attributes provided', $validator->getDescription()); + $this->assertEquals('Duplicate attributes provided', $validator->getDescription()); } } From bd1cb05bb6a25d2596516adfee9c7d7669a1f2ab Mon Sep 17 00:00:00 2001 From: fogelito Date: Sun, 4 Jun 2023 13:35:00 +0300 Subject: [PATCH 41/43] Merge in main --- composer.lock | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/composer.lock b/composer.lock index 05ea6441e..c385be719 100644 --- a/composer.lock +++ b/composer.lock @@ -336,16 +336,16 @@ }, { "name": "utopia-php/framework", - "version": "0.28.2", + "version": "0.28.4", "source": { "type": "git", "url": "https://github.com/utopia-php/framework.git", - "reference": "bc0144ff3983afa6724c43f2ce578fdbceec21f9" + "reference": "98c5469efe195aeecc63745dbf8e2f357f8cedac" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/framework/zipball/bc0144ff3983afa6724c43f2ce578fdbceec21f9", - "reference": "bc0144ff3983afa6724c43f2ce578fdbceec21f9", + "url": "https://api.github.com/repos/utopia-php/framework/zipball/98c5469efe195aeecc63745dbf8e2f357f8cedac", + "reference": "98c5469efe195aeecc63745dbf8e2f357f8cedac", "shasum": "" }, "require": { @@ -375,9 +375,9 @@ ], "support": { "issues": "https://github.com/utopia-php/framework/issues", - "source": "https://github.com/utopia-php/framework/tree/0.28.2" + "source": "https://github.com/utopia-php/framework/tree/0.28.4" }, - "time": "2023-05-30T06:47:57+00:00" + "time": "2023-06-03T14:09:22+00:00" }, { "name": "utopia-php/mongo", @@ -2495,16 +2495,16 @@ }, { "name": "symfony/deprecation-contracts", - "version": "v3.2.1", + "version": "v3.3.0", "source": { "type": "git", "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "e2d1534420bd723d0ef5aec58a22c5fe60ce6f5e" + "reference": "7c3aff79d10325257a001fcf92d991f24fc967cf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/e2d1534420bd723d0ef5aec58a22c5fe60ce6f5e", - "reference": "e2d1534420bd723d0ef5aec58a22c5fe60ce6f5e", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/7c3aff79d10325257a001fcf92d991f24fc967cf", + "reference": "7c3aff79d10325257a001fcf92d991f24fc967cf", "shasum": "" }, "require": { @@ -2513,7 +2513,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "3.3-dev" + "dev-main": "3.4-dev" }, "thanks": { "name": "symfony/contracts", @@ -2542,7 +2542,7 @@ "description": "A generic function and convention to trigger deprecation notices", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v3.2.1" + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.3.0" }, "funding": [ { @@ -2558,7 +2558,7 @@ "type": "tidelift" } ], - "time": "2023-03-01T10:25:55+00:00" + "time": "2023-05-23T14:45:45+00:00" }, { "name": "theseer/tokenizer", From 39143b30cf302e91ce4dedda903845d30306268a Mon Sep 17 00:00:00 2001 From: fogelito Date: Sun, 4 Jun 2023 13:37:22 +0300 Subject: [PATCH 42/43] lint --- tests/Database/Base.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Database/Base.php b/tests/Database/Base.php index ed614a25f..648438a0a 100644 --- a/tests/Database/Base.php +++ b/tests/Database/Base.php @@ -150,7 +150,7 @@ public function testIndexValidation(): void $collection->setAttribute('indexes', $indexes); - if(static::getDatabase()->getAdapter()->getMaxIndexLength() > 0){ + if (static::getDatabase()->getAdapter()->getMaxIndexLength() > 0) { $errorMessage = 'Index length is longer than the maximum: ' . static::getDatabase()->getAdapter()->getMaxIndexLength(); $this->assertFalse($validator->isValid($collection)); $this->assertEquals($errorMessage, $validator->getDescription()); From e10a336a018bb9341fb871c5cc5e135089685ee9 Mon Sep 17 00:00:00 2001 From: fogelito Date: Thu, 8 Jun 2023 15:16:29 +0300 Subject: [PATCH 43/43] Update Index Duplicate check to work with ordering --- composer.lock | 10 +++---- src/Database/Validator/Index.php | 13 ++++++--- tests/Database/Validator/IndexTest.php | 37 ++++++++++++++++++++++++++ 3 files changed, 52 insertions(+), 8 deletions(-) diff --git a/composer.lock b/composer.lock index c385be719..894e7c790 100644 --- a/composer.lock +++ b/composer.lock @@ -907,16 +907,16 @@ }, { "name": "phpstan/phpstan", - "version": "1.10.15", + "version": "1.10.18", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "762c4dac4da6f8756eebb80e528c3a47855da9bd" + "reference": "52b6416c579663eebdd2f1d97df21971daf3b43f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/762c4dac4da6f8756eebb80e528c3a47855da9bd", - "reference": "762c4dac4da6f8756eebb80e528c3a47855da9bd", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/52b6416c579663eebdd2f1d97df21971daf3b43f", + "reference": "52b6416c579663eebdd2f1d97df21971daf3b43f", "shasum": "" }, "require": { @@ -965,7 +965,7 @@ "type": "tidelift" } ], - "time": "2023-05-09T15:28:01+00:00" + "time": "2023-06-07T22:00:43+00:00" }, { "name": "phpunit/php-code-coverage", diff --git a/src/Database/Validator/Index.php b/src/Database/Validator/Index.php index f5754f736..65a7c9de5 100644 --- a/src/Database/Validator/Index.php +++ b/src/Database/Validator/Index.php @@ -58,9 +58,16 @@ public function checkDuplicatedAttributes(Document $collection): bool { foreach ($collection->getAttribute('indexes', []) as $index) { $attributes = $index->getAttribute('attributes', []); - if (count($attributes) !== count(array_unique($attributes))) { - $this->message = 'Duplicate attributes provided'; - return false; + $orders = $index->getAttribute('orders', []); + $stack = []; + foreach ($attributes as $key => $attribute) { + $direction = $orders[$key] ?? 'asc'; + $value = strtolower($attribute . '|' . $direction); + if (in_array($value, $stack)) { + $this->message = 'Duplicate attributes provided'; + return false; + } + $stack[] = $value; } } diff --git a/tests/Database/Validator/IndexTest.php b/tests/Database/Validator/IndexTest.php index bf9a9859a..ea6a349f4 100644 --- a/tests/Database/Validator/IndexTest.php +++ b/tests/Database/Validator/IndexTest.php @@ -181,4 +181,41 @@ public function testDuplicatedAttributes(): void $this->assertFalse($validator->isValid($collection)); $this->assertEquals('Duplicate attributes provided', $validator->getDescription()); } + + /** + * @throws Exception + */ + public function testDuplicatedAttributesDifferentOrder(): void + { + $validator = new Index(768); + + $collection = new Document([ + '$id' => ID::custom('test'), + 'name' => 'test', + 'attributes' => [ + new Document([ + '$id' => ID::custom('title'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 255, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ]) + ], + 'indexes' => [ + new Document([ + '$id' => ID::custom('index1'), + 'type' => Database::INDEX_FULLTEXT, + 'attributes' => ['title', 'title'], + 'lengths' => [], + 'orders' => ['asc', 'desc'], + ]), + ], + ]); + + $this->assertTrue($validator->isValid($collection)); + } }