diff --git a/app/controllers/api/database.php b/app/controllers/api/database.php index 0b288a9ab8..255293c040 100644 --- a/app/controllers/api/database.php +++ b/app/controllers/api/database.php @@ -77,7 +77,7 @@ function createAttribute($collectionId, $attribute, $response, $dbForInternal, $ } try { - $attribute = $dbForInternal->createDocument('attributes', new Document([ + $attribute = new Document([ '$id' => $collectionId.'_'.$attributeId, 'key' => $attributeId, 'collectionId' => $collectionId, @@ -90,11 +90,17 @@ function createAttribute($collectionId, $attribute, $response, $dbForInternal, $ 'array' => $array, 'format' => $format, 'formatOptions' => $formatOptions, - 'filters' => $filters, - ])); - } catch (DuplicateException $th) { + ]); + + $dbForInternal->checkAttribute($collection, $attribute); + $attribute = $dbForInternal->createDocument('attributes', $attribute); + } + catch (DuplicateException $exception) { throw new Exception('Attribute already exists', 409); } + catch (LimitException $exception) { + throw new Exception('Attribute limit exceeded', 400); + } $dbForInternal->deleteCachedDocument('collections', $collectionId); diff --git a/app/init.php b/app/init.php index 2d0bb906c3..bcd2232cc1 100644 --- a/app/init.php +++ b/app/init.php @@ -228,7 +228,7 @@ Database::addFilter('subQueryAttributes', return $database ->find('attributes', [ new Query('collectionId', Query::TYPE_EQUAL, [$document->getId()]) - ], 100, 0, []); + ], $database->getAttributeLimit(), 0, []); } ); @@ -240,7 +240,7 @@ Database::addFilter('subQueryIndexes', return $database ->find('indexes', [ new Query('collectionId', Query::TYPE_EQUAL, [$document->getId()]) - ], 100, 0, []); + ], $database->getIndexLimit(), 0, []); } ); diff --git a/tests/e2e/Services/Database/DatabaseCustomServerTest.php b/tests/e2e/Services/Database/DatabaseCustomServerTest.php index 79750318b2..4ae81cb03d 100644 --- a/tests/e2e/Services/Database/DatabaseCustomServerTest.php +++ b/tests/e2e/Services/Database/DatabaseCustomServerTest.php @@ -307,6 +307,101 @@ class DatabaseCustomServerTest extends Scope $this->assertEquals($response['headers']['status-code'], 404); } + public function testAttributeCountLimit() + { + $collection = $this->client->call(Client::METHOD_POST, '/database/collections', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'collectionId' => 'unique()', + 'name' => 'attributeCountLimit', + 'read' => ['role:all'], + 'write' => ['role:all'], + 'permission' => 'document', + ]); + + $collectionId = $collection['body']['$id']; + + // load the collection up to the limit + for ($i=0; $i < 1012; $i++) { + $attribute = $this->client->call(Client::METHOD_POST, '/database/collections/' . $collectionId . '/attributes/integer', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'attributeId' => "attribute{$i}", + 'required' => false, + ]); + + $this->assertEquals(201, $attribute['headers']['status-code']); + } + + sleep(5); + + $tooMany = $this->client->call(Client::METHOD_POST, '/database/collections/' . $collectionId . '/attributes/integer', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'attributeId' => "tooMany", + 'required' => false, + ]); + + $this->assertEquals(400, $tooMany['headers']['status-code']); + $this->assertEquals('Attribute limit exceeded', $tooMany['body']['message']); + } + + public function testAttributeRowWidthLimit() + { + $collection = $this->client->call(Client::METHOD_POST, '/database/collections', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'collectionId' => 'attributeRowWidthLimit', + 'name' => 'attributeRowWidthLimit', + 'read' => ['role:all'], + 'write' => ['role:all'], + 'permission' => 'document', + ]); + + $this->assertEquals($collection['headers']['status-code'], 201); + $this->assertEquals($collection['body']['name'], 'attributeRowWidthLimit'); + + $collectionId = $collection['body']['$id']; + + // Add wide string attributes to approach row width limit + for ($i=0; $i < 15; $i++) { + $attribute = $this->client->call(Client::METHOD_POST, '/database/collections/' . $collectionId . '/attributes/string', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'attributeId' => "attribute{$i}", + 'size' => 1024, + 'required' => true, + ]); + + $this->assertEquals($attribute['headers']['status-code'], 201); + } + + sleep(5); + + $tooWide = $this->client->call(Client::METHOD_POST, '/database/collections/' . $collectionId . '/attributes/string', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'attributeId' => 'tooWide', + 'size' => 1024, + 'required' => true, + ]); + + $this->assertEquals(400, $tooWide['headers']['status-code']); + $this->assertEquals('Attribute limit exceeded', $tooWide['body']['message']); + } + public function testIndexLimitException() { $collection = $this->client->call(Client::METHOD_POST, '/database/collections', array_merge([