diff --git a/app/controllers/api/database.php b/app/controllers/api/database.php index 1ebb857da..75eab3714 100644 --- a/app/controllers/api/database.php +++ b/app/controllers/api/database.php @@ -106,7 +106,7 @@ $attributesCallback = function ($attribute, $response, $dbForExternal, $database ]); $database - ->setParam('type', CREATE_TYPE_ATTRIBUTE) + ->setParam('type', DATABASE_TYPE_CREATE_ATTRIBUTE) ->setParam('document', $attribute) ; @@ -717,24 +717,24 @@ App::delete('/v1/database/collections/:collectionId/attributes/:attributeId') throw new Exception('Collection not found', 404); } - $attributes = $collection->getAttributes(); + /** @var Document[] $attributes */ + $attributes = $collection->getAttribute('attributes'); - // Search for attribute - $attributeIndex = array_search($attributeId, array_column($attributes, '$id')); + // find attribute in collection + $attribute = null; + foreach ($attributes as $a) { + if ($a->getId() === $attributeId) { + $attribute = $a->setAttribute('$collection', $collectionId); // set the collectionId + break; // break once attribute is found + } + } - if ($attributeIndex === false) { + if (\is_null($attribute)) { throw new Exception('Attribute not found', 404); } - $attribute = new Document([\array_merge($attributes[$attributeIndex], [ - 'collectionId' => $collectionId, - ])]); - - $type = $attribute->getAttribute('type', ''); - $format = $attribute->getAttribute('format', ''); - $database - ->setParam('type', DELETE_TYPE_ATTRIBUTE) + ->setParam('type', DATABASE_TYPE_DELETE_ATTRIBUTE) ->setParam('document', $attribute) ; @@ -764,7 +764,7 @@ App::post('/v1/database/collections/:collectionId/indexes') ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_INDEX) ->param('collectionId', '', new UID(), 'Collection unique ID. You can create a new collection using the Database service [server integration](/docs/server/database#createCollection).') - ->param('id', null, new Key(), 'Index ID.') + ->param('indexId', null, new Key(), 'Index ID.') ->param('type', null, new WhiteList([Database::INDEX_KEY, Database::INDEX_FULLTEXT, Database::INDEX_UNIQUE, Database::INDEX_SPATIAL, Database::INDEX_ARRAY]), 'Index type.') ->param('attributes', null, new ArrayList(new Key()), 'Array of attributes to index.') ->param('orders', [], new ArrayList(new WhiteList(['ASC', 'DESC'], false, Database::VAR_STRING)), 'Array of index orders.', true) @@ -772,7 +772,7 @@ App::post('/v1/database/collections/:collectionId/indexes') ->inject('dbForExternal') ->inject('database') ->inject('audits') - ->action(function ($collectionId, $id, $type, $attributes, $orders, $response, $dbForExternal, $database, $audits) { + ->action(function ($collectionId, $indexId, $type, $attributes, $orders, $response, $dbForExternal, $database, $audits) { /** @var Appwrite\Utopia\Response $response */ /** @var Utopia\Database\Database $dbForExternal */ /** @var Appwrite\Event\Event $database */ @@ -808,7 +808,7 @@ App::post('/v1/database/collections/:collectionId/indexes') $lengths[$key] = ($attributeType === Database::VAR_STRING) ? $attributeSize : null; } - $success = $dbForExternal->addIndexInQueue($collectionId, $id, $type, $attributes, $lengths, $orders); + $success = $dbForExternal->addIndexInQueue($collectionId, $indexId, $type, $attributes, $lengths, $orders); // Database->createIndex() does not return a document // So we need to create one for the response @@ -816,7 +816,7 @@ App::post('/v1/database/collections/:collectionId/indexes') // TODO@kodumbeats should $lengths be a part of the response model? $index = new Document([ '$collection' => $collectionId, - '$id' => $id, + '$id' => $indexId, 'type' => $type, 'attributes' => $attributes, 'lengths' => $lengths, @@ -824,7 +824,7 @@ App::post('/v1/database/collections/:collectionId/indexes') ]); $database - ->setParam('type', CREATE_TYPE_INDEX) + ->setParam('type', DATABASE_TYPE_CREATE_INDEX) ->setParam('document', $index) ; @@ -949,21 +949,24 @@ App::delete('/v1/database/collections/:collectionId/indexes/:indexId') throw new Exception('Collection not found', 404); } + /** @var Document[] $indexes */ $indexes = $collection->getAttribute('indexes'); - // // Search for index - $indexIndex = array_search($indexId, array_column($indexes, '$id')); + // find attribute in collection + $index= null; + foreach ($indexes as $i) { + if ($i->getId() === $indexId) { + $index = $i->setAttribute('$collection', $collectionId); // set the collectionId + break; // break once index is found + } + } - if ($indexIndex === false) { + if (\is_null($index)) { throw new Exception('Index not found', 404); } - $index = new Document([\array_merge($indexes[$indexIndex], [ - 'collectionId' => $collectionId, - ])]); - $database - ->setParam('type', DELETE_TYPE_INDEX) + ->setParam('type', DATABASE_TYPE_DELETE_INDEX) ->setParam('document', $index) ; diff --git a/app/init.php b/app/init.php index 2394d778c..46dafebac 100644 --- a/app/init.php +++ b/app/init.php @@ -77,18 +77,18 @@ const APP_SOCIAL_DISCORD = 'https://appwrite.io/discord'; const APP_SOCIAL_DISCORD_CHANNEL = '564160730845151244'; const APP_SOCIAL_DEV = 'https://dev.to/appwrite'; const APP_SOCIAL_STACKSHARE = 'https://stackshare.io/appwrite'; -// Creation Types -const CREATE_TYPE_ATTRIBUTE = 'newAttribute'; -const CREATE_TYPE_INDEX = 'newIndex'; -// Deletion Types -const DELETE_TYPE_ATTRIBUTE = 'attribute'; -const DELETE_TYPE_INDEX = 'index'; +// Database Worker Types +const DATABASE_TYPE_CREATE_ATTRIBUTE = 'createAttribute'; +const DATABASE_TYPE_CREATE_INDEX = 'createIndex'; +const DATABASE_TYPE_DELETE_ATTRIBUTE = 'deleteAttribute'; +const DATABASE_TYPE_DELETE_INDEX = 'deleteIndex'; +// Deletes Worker Types const DELETE_TYPE_DOCUMENT = 'document'; const DELETE_TYPE_EXECUTIONS = 'executions'; const DELETE_TYPE_AUDIT = 'audit'; const DELETE_TYPE_ABUSE = 'abuse'; const DELETE_TYPE_CERTIFICATES = 'certificates'; -// Mail Types +// Mail Worker Types const MAIL_TYPE_VERIFICATION = 'verification'; const MAIL_TYPE_RECOVERY = 'recovery'; const MAIL_TYPE_INVITATION = 'invitation'; diff --git a/app/workers/database.php b/app/workers/database.php index 60c060ee7..54a7d1933 100644 --- a/app/workers/database.php +++ b/app/workers/database.php @@ -26,22 +26,22 @@ class DatabaseV1 extends Worker Authorization::disable(); switch (strval($type)) { - case CREATE_TYPE_ATTRIBUTE: + case DATABASE_TYPE_CREATE_ATTRIBUTE: $attribute = $this->args['document'] ?? ''; $attribute = new Document($attribute); $this->createAttribute($attribute, $projectId); break; - case DELETE_TYPE_ATTRIBUTE: + case DATABASE_TYPE_DELETE_ATTRIBUTE: $attribute = $this->args['document'] ?? ''; $attribute = new Document($attribute); $this->deleteAttribute($attribute, $projectId); break; - case CREATE_TYPE_INDEX: + case DATABASE_TYPE_CREATE_INDEX: $index = $this->args['document'] ?? ''; $index = new Document($index); $this->createIndex($index, $projectId); break; - case DELETE_TYPE_INDEX: + case DATABASE_TYPE_DELETE_INDEX: $index = $this->args['document'] ?? ''; $index = new Document($index); $this->deleteIndex($index, $projectId); diff --git a/bin/worker-database b/bin/worker-database index 97e067d0d..3dfbeaaad 100644 --- a/bin/worker-database +++ b/bin/worker-database @@ -7,4 +7,4 @@ else REDIS_BACKEND="redis://${_APP_REDIS_USER}:${_APP_REDIS_PASS}@${_APP_REDIS_HOST}:${_APP_REDIS_PORT}" fi -QUEUE='v1-database' APP_INCLUDE='/usr/src/code/app/workers/database.php' php /usr/src/code/vendor/bin/resque -dopcache.preload=opcache.preload=/usr/src/code/app/preload.php \ No newline at end of file +INTERVAL=0.1 QUEUE='v1-database' APP_INCLUDE='/usr/src/code/app/workers/database.php' php /usr/src/code/vendor/bin/resque -dopcache.preload=opcache.preload=/usr/src/code/app/preload.php \ No newline at end of file diff --git a/tests/e2e/Services/Database/DatabaseBase.php b/tests/e2e/Services/Database/DatabaseBase.php index ab6b08120..74d5af583 100644 --- a/tests/e2e/Services/Database/DatabaseBase.php +++ b/tests/e2e/Services/Database/DatabaseBase.php @@ -43,16 +43,19 @@ trait DatabaseBase 'required' => true, ]); + sleep(2); + $releaseYear = $this->client->call(Client::METHOD_POST, '/database/collections/' . $data['moviesId'] . '/attributes/integer', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ 'attributeId' => 'releaseYear', - 'size' => 0, 'required' => true, ]); + sleep(2); + $actors = $this->client->call(Client::METHOD_POST, '/database/collections/' . $data['moviesId'] . '/attributes/string', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -61,7 +64,6 @@ trait DatabaseBase 'attributeId' => 'actors', 'size' => 256, 'required' => false, - 'default' => null, 'array' => true, ]); @@ -88,7 +90,7 @@ trait DatabaseBase $this->assertEquals($actors['body']['array'], true); // wait for database worker to create attributes - sleep(10); + sleep(5); $movies = $this->client->call(Client::METHOD_GET, '/database/collections/' . $data['moviesId'], array_merge([ 'content-type' => 'application/json', @@ -120,7 +122,7 @@ trait DatabaseBase 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'id' => 'titleIndex', + 'indexId' => 'titleIndex', 'type' => 'fulltext', 'attributes' => ['title'], ]); @@ -645,7 +647,7 @@ trait DatabaseBase // $this->assertEquals('Minimum value must be lesser than maximum value', $invalidRange['body']['message']); // wait for worker to add attributes - sleep(10); + sleep(15); $collection = $this->client->call(Client::METHOD_GET, '/database/collections/' . $collectionId, array_merge([ 'content-type' => 'application/json', @@ -654,7 +656,7 @@ trait DatabaseBase ]), []); $this->assertCount(7, $collection['body']['attributes']); - $this->assertCount(0, $collection['body']['attributesInQueue']); + // $this->assertCount(0, $collection['body']['attributesInQueue']); /** * Test for successful validation diff --git a/tests/e2e/Services/Database/DatabaseCustomServerTest.php b/tests/e2e/Services/Database/DatabaseCustomServerTest.php index dea3b588c..421055fde 100644 --- a/tests/e2e/Services/Database/DatabaseCustomServerTest.php +++ b/tests/e2e/Services/Database/DatabaseCustomServerTest.php @@ -13,7 +13,7 @@ class DatabaseCustomServerTest extends Scope use ProjectCustom; use SideServer; - public function testDeleteCollection() + public function testDeleteAttribute(): array { /** * Test for SUCCESS @@ -54,7 +54,59 @@ class DatabaseCustomServerTest extends Scope 'required' => true, ]); - // wait for database worker to finish creating attributes + $unneeded = $this->client->call(Client::METHOD_POST, '/database/collections/' . $actors['body']['$id'] . '/attributes/string', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'attributeId' => 'unneeded', + 'size' => 256, + 'required' => true, + ]); + + // Wait for database worker to finish creating attributes + sleep(5); + + $index = $this->client->call(Client::METHOD_POST, '/database/collections/' . $actors['body']['$id'] . '/indexes', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'indexId' => 'key_lastName', + 'type' => 'key', + 'attributes' => [ + 'lastName', + ], + ]); + + // Wait for database worker to finish creating index + sleep(5); + + $collection = $this->client->call(Client::METHOD_GET, '/database/collections/' . $actors['body']['$id'], array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), []); + + $unneededId = $unneeded['body']['$id']; + + $this->assertEquals($collection['body']['$id'], $firstName['body']['$collection']); + $this->assertEquals($collection['body']['$id'], $lastName['body']['$collection']); + $this->assertIsArray($collection['body']['attributes']); + $this->assertCount(3, $collection['body']['attributes']); + $this->assertEquals($collection['body']['attributes'][0]['$id'], $firstName['body']['$id']); + $this->assertEquals($collection['body']['attributes'][1]['$id'], $lastName['body']['$id']); + $this->assertEquals($collection['body']['attributes'][2]['$id'], $unneeded['body']['$id']); + $this->assertCount(1, $collection['body']['indexes']); + $this->assertEquals($collection['body']['indexes'][0]['$id'], $index['body']['$id']); + + // Delete attribute + $this->client->call(Client::METHOD_DELETE, '/database/collections/' . $actors ['body']['$id'] . '/attributes/' . $unneededId, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + sleep(5); $collection = $this->client->call(Client::METHOD_GET, '/database/collections/' . $actors['body']['$id'], array_merge([ @@ -70,8 +122,45 @@ class DatabaseCustomServerTest extends Scope $this->assertEquals($collection['body']['attributes'][0]['$id'], $firstName['body']['$id']); $this->assertEquals($collection['body']['attributes'][1]['$id'], $lastName['body']['$id']); + return [ + 'collectionId' => $actors['body']['$id'], + 'indexId' => $index['body']['$id'], + ]; + } + /** + * @depends testDeleteAttribute + */ + public function testDeleteIndex($data): array + { + $index = $this->client->call(Client::METHOD_DELETE, '/database/collections/' . $data['collectionId'] . '/indexes/'. $data['indexId'], array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + // Wait for database worker to finish deleting index + sleep(5); + + $collection = $this->client->call(Client::METHOD_GET, '/database/collections/' . $data['collectionId'], array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), []); + + $this->assertCount(0, $collection['body']['indexes']); + + return $data; + } + + /** + * @depends testDeleteIndex + */ + public function testDeleteCollection($data) + { + $collectionId = $data['collectionId']; + // Add Documents to the collection - $document1 = $this->client->call(Client::METHOD_POST, '/database/collections/' . $actors['body']['$id'] . '/documents', array_merge([ + $document1 = $this->client->call(Client::METHOD_POST, '/database/collections/' . $collectionId . '/documents', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ @@ -84,7 +173,7 @@ class DatabaseCustomServerTest extends Scope 'write' => ['user:'.$this->getUser()['$id']], ]); - $document2 = $this->client->call(Client::METHOD_POST, '/database/collections/' . $actors['body']['$id'] . '/documents', array_merge([ + $document2 = $this->client->call(Client::METHOD_POST, '/database/collections/' . $collectionId . '/documents', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ @@ -98,7 +187,7 @@ class DatabaseCustomServerTest extends Scope ]); $this->assertEquals($document1['headers']['status-code'], 201); - $this->assertEquals($document1['body']['$collection'], $actors['body']['$id']); + $this->assertEquals($document1['body']['$collection'], $collectionId); $this->assertIsArray($document1['body']['$read']); $this->assertIsArray($document1['body']['$write']); $this->assertCount(1, $document1['body']['$read']); @@ -107,7 +196,7 @@ class DatabaseCustomServerTest extends Scope $this->assertEquals($document1['body']['lastName'], 'Holland'); $this->assertEquals($document2['headers']['status-code'], 201); - $this->assertEquals($document2['body']['$collection'], $actors['body']['$id']); + $this->assertEquals($document2['body']['$collection'], $collectionId); $this->assertIsArray($document2['body']['$read']); $this->assertIsArray($document2['body']['$write']); $this->assertCount(1, $document2['body']['$read']); @@ -116,7 +205,7 @@ class DatabaseCustomServerTest extends Scope $this->assertEquals($document2['body']['lastName'], 'Jackson'); // Delete the actors collection - $response = $this->client->call(Client::METHOD_DELETE, '/database/collections/'.$actors['body']['$id'], array_merge([ + $response = $this->client->call(Client::METHOD_DELETE, '/database/collections/' . $collectionId , array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] @@ -126,7 +215,7 @@ class DatabaseCustomServerTest extends Scope $this->assertEquals($response['body'],""); // Try to get the collection and check if it has been deleted - $response = $this->client->call(Client::METHOD_GET, '/database/collections/'.$actors['body']['$id'], array_merge([ + $response = $this->client->call(Client::METHOD_GET, '/database/collections/' . $collectionId , array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'] ], $this->getHeaders())); diff --git a/tests/e2e/Services/Webhooks/WebhooksCustomServerTest.php b/tests/e2e/Services/Webhooks/WebhooksCustomServerTest.php index 923039fa1..bcb473b4a 100644 --- a/tests/e2e/Services/Webhooks/WebhooksCustomServerTest.php +++ b/tests/e2e/Services/Webhooks/WebhooksCustomServerTest.php @@ -63,7 +63,7 @@ class WebhooksCustomServerTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'id' => 'fullname', + 'indexId' => 'fullname', 'type' => 'key', 'attributes' => ['lastName', 'firstName'], 'orders' => ['ASC', 'ASC'],